今天会像是笔记一样QQ,但是就是有关函式的知识!
!!!IMPORTANT!!!!
Variable lifetime (变数的出生到死亡的时间)
Local
在 block 里面出生(宣告),在结束的时候死掉
lifetime : (declaration → end of block)
int main()
{
int i = 50; // it will be hidden
for (int i = 0; i < 20; i++) //这个 i 也是 local variable
{ //里面的 i 会比外面的大(外面的 i 先藏起来)
cout << i << " "; // print 0 1 2 ... 19
}
cout << i << "\n"; //50
return 0;
}
Global
意旨不在 block 里面宣告的:
#include<iostream>
using namespace std;
int i = 5; //他就是一个 global variable
int main()
{
for (; i < 20 ; i++)
cout << i << " ";//?
cout << "\n";
int i = 2;
cout << i << "\n"; //?
cout << ::i << "\n"; //?
return 0;
}
因为它比 local variable还早宣告,所以会被他们藏起来
像是 下面宣告了 int i = 2;
就会先 cout 2
但是如果在那个变数前面加::
就会找到 global variable出来。
External
在大型的系统里面,会有很多 program 在同时进行。
如果有一个 program 想要读取其他 program 里面的变数,就要用这个东西:extern
。
extern int a
这个 a 需要在别的 program 里面宣告过(可能是 global 或是 local)
且这两个程序要在一起跑的状态下extern
才能作用
但是现在已经不太喜欢extern
,因为这两个 program会使用同一个记忆体空间,会让两个程序无法被分割(coupling),所以整个程序会被搞得非常的复杂。
所以 global variable 也尽量不要使用,因为他也会导致一个程序里面每一个 function 或是 block 都 couple 在一起
static
当 local variable 死掉的时候(block 结束的时候),记忆体会把它清掉 (recycled)
global 会到整个program结束後才会被清掉
static variable 就像是介於两者之间,在 block 里面被宣告,可是会到 program 结束之後才会被清掉
static 的规则是,如果他被宣告後,那个宣告的 statement 就不会再执行了
int test();
int main(){
for (int a = 0; a < 10; a++)
cout << test() << " ";
return 0;//1, 1, ..., 1
}
int test()
{
int a = 0;
a++;
return a;
}
上面这个结果会跑出 1 1 1 1 1 1 1 ...1
如果改写成这样:
int test();
int main(){
for (int a = 0; a < 10; a++)
cout << test() << " ";
return 0;//??
}
int test()
{
static int a = 0;
a++;
return a;
}
却会跑出 1 2 3 4 5 6 7 8 9 10
回到刚刚的 static 的规则,如果他被宣告後,那个宣告的 statement 就不会再执行了。
所以在下面的程序,执行过一次 static int a = 0;
後,就不会再执行了,所以a 就会一直累加上去了。
那它可以拿来干嘛?
他可以拿来数一个 function 被呼叫了几次。像是上面那个方式。
为什麽不用 global?
因为 global 有破坏模组化的缺点。
why should we initialize local variable yet not global and static variables
In fact, the system initializes global and static variables to 0.
Because there are too many local variables and few global and static variables.
Though troublesome for programmers to initialize, this act actually improve the efficiency of C++ compared to other language like Python.
Call-by-value mechanism
void swap(int x , int y);
int main()
{
int a = 10, b = 20;
cout << a << " " << b << endl;
swap(a, b);
cout << a << " " << b << endl;
}
void swap(int x, int y)
{
int temp = x;
x = y;
y = temp;
}
如果看上面的 code,理论上 a 与 b 应该是会交换才对,但是实际上跑出来却并没有交换,这是为什麽?
在C++里面,呼叫函式的机制,是所谓的 "Call-by-value"。
void swap(int x, int y)
{
int temp = x;
x = y;
y = temp;
}
int main()
{
int a = 10, b = 20;
swap(a, b)
}
如果我们把上面的程序以记忆体来看
【Memory】
Address | Identifier | Value |
---|---|---|
- | x | - |
- | y | - |
... | - | - |
- | a | 10 |
- | b | 20 |
Why using Call-by-value mechanism but not call-by-variable?
In cases, we do need a callee(be called) to modify the values of some variables defined in the caller(call the callee).
Passing an array as an argument
要怎麽在 function 中回传一个 array?
example_1
void printArray(int[], int);
int main()
{
int num[5] = {1, 2, 3, 4, 5};
printArray(num, 5);
return 0;
}
void printArray(int a[], int length)
{
for (int i = 0; i < length; i++)
cout << a[i] << " ";
cout << "\n";
}
为什麽不直接在 declare function 时直接宣告 array 的长度?
example_2
void shiftArray(int[], int); //可把 int[]想像成一个array variable
int main()
{
int num[5] = {1, 2, 3, 4, 5};
shiftArray(num, 5);
for (int i = 0; i < 5; i++)
cout << num[i] << " ";
return 0;
}
void shiftArray(int a[], int length)
{
int temp = a[0];
for (int i = 0; i < length; i++)
a[i] = a[i + 1];
a[length - 1] = temp;
}
简单说上面的程序,就是让 array 每一项往後移,再把第一项放到最後一项。 实际上跑出来就会是 2 3 4 5 1
。
实际上在记忆体里面,这个 function的作用,就是把指定地址上面的东西去做改变,所以最後 num array中的东西(变数)就会被改变。
也可以传多维阵列:
example_3
void printArray(int[][2], int);
int main()
{
int num[5][2] = {1, 2, 3, 4, 5}; // five 0s (default)
printArray(num, 5);
return 0;
}
void printArray(int a[][2], int length)
{
for (int i = 0; i < length; i++)
{
for (int j = 0; j < 2; j++)
cout << a[i][j] << " ";
cout << "\n";
}
}
就会跑出:
1 2
3 4
5 0
0 0
0 0
要注意,在传多维 array 时,要把第二维度以上的数量传进去,像是第一行写的 int[ ][2] 这样。
可以把它想像成:
a[0]
[] []
a[1]
[] []
a[2]
[] []
a[3]
[] []
a[4]
[] []
是一个一维阵列,总共有 5 个 elements,每一个 element 又是一个有两个 element 的一维阵列。
Constant parameters
example_1
int factorial (int n)
{
int ans = 1;
for (int a = 1; a <= n; a++)
ans *= a;
return ans;
} // n 阶层
在这个例子里面,理论上 n 是不应该被 modified,那要怎麽阻止 programmer 自己去将 n 被 modified?
答案就是:
int fatorial (const int n)
{
int ans = 1;
for (int a = 1; a <= n; a++)
ans *= a;
return ans;
}
int main()
{
int n = 0;
cin >> n;
cout << factorial(n); // as usual
return 0;
}
因为这样某一天,你和你一起做一个 project 的夥伴,才不会随便乱改这个理论上不能改的 integer。
而且就算是 constant 也没差,因为 argument 回传的是一个值,而不是变数本身(call-by-value)
sometimes an argument's value in a caller may be modified in a callee: e.g., arrays.
但有时候我们却需要(甚至是必要)改动这个 argument
例如 argument 是 array 的时候。
example_2
void printArray (const int [5], int);
int main()
{
int num[5] = {1, 2, 3, 4, 5};
printArray(num, 5);
return 0;
}
void printArray(const int a[5], int len)
{
for(int i = 0; i < len; i++)
cout << a[i] << " ";
cout << "\n";
}
Function overloading 函数多载
例如今天有一个 function 会计算 $x^y$
或是
但如果今天我们要两个都是分数或是 base 是分数呢?
如果我们在每次改动的时候都要换一个新的 function
这样会有一大堆 function 出生吧,所以在C++中提供了一个功能: function loading。(也就是 虽然有不同的 parameters,但还是可以使用同样的名字)
所以说,不同的 function 需要有不同的 function signature
Function name
Function parameters(numbers of parameters and their types)
DO NOT INCLUDE return type! WHY?
因为在呼叫一个函数的时候,回传值不重要(因为还不会处理)
example_1
void print(char c, int num)
{
for (int i = 0; i < num; i++)
cout << c;
}
void print(char c)
{
cout << c;
}
在这个例子中,如果没有传入 num 就会跑下面那个 function
或是 可以把 num 预设为 1:
Default arguments
example_1
double circleArea(double, double = 3.14);
//
double circleArea(double radius, double pi)
{
return radius * radius * pi;
}
Inline function
(没甚麽人在用)
因为宣告函式会让系统颇为负担沉重,但是又不能不写 function,所以C++中会 define inline function。
#include<iostream>
using namespace std;
int gcd(int a, int b);
int min(int a, int b);
int main()
{
int a = 0, b = 0;
cin >> a >> b;
cout << gcd(a, b);
}
int min(int a, int b)
{
int temp = 0;
if (a > b)
return b;
else
return a;
}
int gcd(int a, int b)
{
}
函式要写好真的...好难 ?
本系列是为了转生,为了点技能而解任务的攻略提示,皆无营利、亦非营利取向。 Javascript:属於...
这样的食材,才299吃到饱,别挑剔了啦~ 这家好好吃肉,就位在前几天分享过的「咕咕家」正对面。 好好...
今天,我们要来完成整个 I2C 的最後一个部份了! 先来看看这个 I2C Master write ...
注:发文日和截图的日期不一定是同一天,所以价格计算上和当日不同,是很正常的。 声明:这一系列文章并无...
一般订购的程序都是由下订单开始, 接着取单号为依据来分批或批次采购相关物资, 因此订单编号有举足轻重...