C语言05指针基础
1 局部变量和全局变量
1.1 问题
分别定义一个局部变量和全局变量,测试生命期限和作用域。
1.2 步骤
实现此案例需要按照如下步骤进行。
步骤一:局部变量和全局变量代码如下所示:
#include /*auto*/ int x = 1000; void f1(int a) { a = 10; auto int x = 20; static int y = 30; y++; x++; printf("in f1(),a=%d,x=%d,y=%d\n",a,x,y); } void f2() { printf("in f2(),x=%d\n",x); x = 2000; } void f3() { printf("in f3(),x=%d\n",x); } int main() { int x = 30,b=100; f1(b); f1(b); printf("in main(),b=%d,x=%d\n",b,x); f2(); f3(); return 0; }
上述代码中,以下代码:
- /*auto*/ int x = 1000;
定义了一个全局变量x,该变量在所有函数*享。
上述代码中,以下代码:
- void f1(int a)
- {
- a = 10;
- auto int x = 20;
- static int y = 30;
- y++;
- x++;
- printf("in f1(),a=%d,x=%d,y=%d\n",a,x,y);
- }
定义了一个函数f1,该函数没有返回值,有一个参数是一个整型变量a。该函数中以下语句:
- a = 10;
将参数a赋值为10,参数a是局部变量,只能用在f1函数中。该函数中以下语句:
- auto int x = 20;
定义了另一个局部变量x,也只能用在f1函数中。前面的auto关键字可有可无。该函数中以下语句:
- static int y = 30;
定义了一个static局部变量y,也只能用在f1函数中,但函数返回后该变量不会被释放,直到主函数退出时,它才会被释放。该函数中以下语句:
- y++;
- x++;
对局部变量x和y进行自增操作。
注意:在函数f1中,定义了局部变量x,而在该函数的上面还定义全局变量x,这样在函数f1中,有两个x,但C语言规定,在这种情况下,局部变量x将隐藏全局变量x,即全局变量x此时不起作用。
该函数中以下语句:
- printf("in f1(),a=%d,x=%d,y=%d\n",a,x,y);
使用函数printf输出局部变量a和x的值,以及静态局部变量y的值。
上述代码中,以下代码:
- void f2()
- {
- printf("in f2(),x=%d\n",x);
- x = 2000;
- }
定义了一个无参无返回值的函数f2。该函数中以下语句:
- x = 2000;
对全局变量x进行赋值。
上述代码中,以下代码:
- void f3()
- {
- printf("in f3(),x=%d\n",x);
- }
定义了一个无参无返回值的函数f2。该函数中以下语句:
- printf("in f3(),x=%d\n",x);
对全局变量x进行输出。
上述代码中,以下代码:
- int main()
- {
- int x = 30,b=100;
在主函数中,定义了两个局部变量x和b,并分别初始化为30和100。这两个局部变量只能用在main函数中。
上述代码中,以下代码:
- f1(b);
- f1(b);
两次调用函数f1,并用局部变量b作为实参。
注意:两次调用中,函数f1中的静态局部变量y输出值不一样。因为static关键字使该变量在函数f1返回后并不释放,所以第二次调用时,其值会在上一次调用返回前的基础上发生变化。
上述代码中,以下代码:
- printf("in main(),b=%d,x=%d\n",b,x);
使用函数printf输出局部变量b和局部变量x的值。
注意:在函数main中,定义了局部变量x,而在该函数的上面还定义全局变量x,这样在函数main中,有两个x,但C语言规定,在这种情况下,局部变量x将隐藏全局变量x,即全局变量x此时不起作用。
上述代码中,以下代码:
- f2();
- f3();
调用函数f2和f3,从输出的值上可以看出,全局变量可以在所有函数*享。
1.3 完整代码
本案例的完整代码如下所示:
#include /*auto*/ int x = 1000; void f1(int a) { a = 10; auto int x = 20; static int y = 30; y++; x++; printf("in f1(),a=%d,x=%d,y=%d\n",a,x,y); } void f2() { printf("in f2(),x=%d\n",x); x = 2000; } void f3() { printf("in f3(),x=%d\n",x); } int main() { int x = 30,b=100; f1(b); f1(b); printf("in main(),b=%d,x=%d\n",b,x); f2(); f3(); return 0; }
2 指针的基本用法
2.1 问题
定义一个int类型的指针,指向一个整型变量,然后分别使用&和*取得地址或者数据。
2.2 步骤
实现此案例需要按照如下步骤进行。
步骤一:指针的基本用法
代码如下所示:
- #include
- int main()
- {
- int a = 10;
- int *p;
- p = &a;
- printf("p = %p\n", p);
- printf("&a = %p\n", &a);
- printf("a = %d\n", a);
- printf("*p = %d\n", *p);
- return 0;
- }
上述代码中,以下代码:
- int a = 10;
定义了一个整型变量a,并初始化为10。
上述代码中,以下代码:
- int *p;
- p = &a;
定义了一个整型指针变量p,并使用取地址运算符&将整型变量a的地址赋值给p。
上述代码中,以下代码:
- printf("p = %p\n", p);
- printf("&a = %p\n", &a);
使用函数printf输出指针变量p的值和整型变量a的地址值。从输出结果可以看出,指针变量p中存放的就是整型变量a的地址值。
上述代码中,以下代码:
- printf("a = %d\n", a);
使用函数printf输出整型变量a的值。
上述代码中,以下代码:
- printf("*p = %d\n", *p);
使用函数printf输出指针变量p所指向的地址单元的值。由于指针变量p被赋值成了整型变量a的地址值,所以,p就指向a的地址,所以输出的就是a的值。
2.3 完整代码
本案例的完整代码如下所示:
- #include
- int main()
- {
- int a = 10;
- int *p;
- p = &a;
- printf("p = %p\n", p);
- printf("&a = %p\n", &a);
- printf("a = %d\n", a);
- printf("*p = %d\n", *p);
- return 0;
- }
3 指针的优势
3.1 问题
使用指针实现交换变量值的函数swap(),同时编写不使用指针交换失败的函数做对比。
3.2 步骤
实现此案例需要按照如下步骤进行。
步骤一:指针的优势代码如下所示:
#include void swap(int a, int b) { printf("in swap() a = %d, b = %d\n", a, b); int tmp = a; a = b; b = tmp; printf("in swap() a = %d, b = %d\n", a, b); } void swap1(int *a, int *b) { printf("in swap1() *a = %d, *b = %d\n", *a, *b); int tmp = *a; *a = *b; *b = tmp; printf("in swap1() *a = %d, *b = %d\n", *a, *b); } int main() { int x = 10, y = 20; printf("in main() x = %d, y = %d\n", x, y); swap(x, y); printf("in main() x = %d, y = %d\n", x, y); swap1(&x, &y); printf("in main() x = %d, y = %d\n", x, y); return 0; } 上述代码中,以下代码: void swap(int a, int b) { printf("in swap() a = %d, b = %d\n", a, b); int tmp = a; a = b; b = tmp; printf("in swap() a = %d, b = %d\n", a, b); }
定义了一个函数swap,该函数有两个参数,为整型变量a和b。在该函数中,以下语句:
- printf("in swap() a = %d, b = %d\n", a, b);
使用函数printf输出形参a和b从调用函数传入的值。在该函数中,以下语句:
- int tmp = a;
- a = b;
- b = tmp;
通过第三个变量tmp,将整型变量a和b的值进行交换。在该函数中,以下语句:
- printf("in swap() a = %d, b = %d\n", a, b);
使用函数printf输出形参a和b交换后的值。
上述代码中,以下代码:
- void swap1(int *a, int *b)
- {
- printf("in swap1() *a = %d, *b = %d\n", *a, *b);
- int tmp = *a;
- *a = *b;
- *b = tmp;
- printf("in swap1() *a = %d, *b = %d\n", *a, *b);
- }
定义了一个函数swap1,该函数有两个参数,为整型指针变量a和b。在该函数中,以下语句:
- printf("in swap1() *a = %d, *b = %d\n", *a, *b);
使用函数printf输出整型指针变量a和b指向的地址的值。在该函数中,以下语句:
- int tmp = *a;
- *a = *b;
- *b = tmp;
通过第三个变量tmp,将整型指针变量a和b指向的地址的值进行交换。在该函数中,以下语句:
- printf("in swap1() *a = %d, *b = %d\n", *a, *b);
使用函数printf输出整型指针变量a和b指向地址的值交换后的值。
上述代码中,以下代码:
- int main()
- {
- int x = 10, y = 20;
在主函数中定义两个整型变量x和y,并将它们初始化为10和20。
上述代码中,以下代码:
- printf("in main() x = %d, y = %d\n", x, y);
使用函数printf输出变量x和y的初始值。
上述代码中,以下代码:
- swap(x, y);
- printf("in main() x = %d, y = %d\n", x, y);
首先,调用函数swap。
然后,使用函数printf输出整型变量x和y在调用函数swap后的变化情况。从输出结果可以看出,没有发生变化。因为swap函数定义的参数是整型变量,这时,实参到形参的传递过程是值传递,即仅将实参x和y的值传递给形参a和b,而形参a和b在函数swap中发生的变化并不会传回给实参。
上述代码中,以下代码:
- swap1(&x, &y);
- printf("in main() x = %d, y = %d\n", x, y);
首先,调用函数swap1。
然后,使用函数printf输出整型变量x和y在调用函数swap1后的变化情况。从输出结果可以看出,确实发生了变化。因为swap1函数定义的参数是整型指针变量,实参将变量x和y的地址传给了形参a和b,在函数swap1中使用*a和*b操作的是指针a和b指向的地址单元的值,即变量x和y的值。
3.3 完整代码
本案例的完整代码如下所示:
#include void swap(int a, int b) { printf("in swap() a = %d, b = %d\n", a, b); int tmp = a; a = b; b = tmp; printf("in swap() a = %d, b = %d\n", a, b); } void swap1(int *a, int *b) { printf("in swap1() *a = %d, *b = %d\n", *a, *b); int tmp = *a; *a = *b; *b = tmp; printf("in swap1() *a = %d, *b = %d\n", *a, *b); } int main() { int x = 10, y = 20; printf("in main() x = %d, y = %d\n", x, y); swap(x, y); printf("in main() x = %d, y = %d\n", x, y); swap1(&x, &y); printf("in main() x = %d, y = %d\n", x, y); return 0; }
4 指针的优势(续1)
4.1 问题
使用指针做参数取函数的返回值,写一个函数,找出一个数组中的最大值和最小值。
4.2 步骤
实现此案例需要按照如下步骤进行。
步骤一:指针的优势(续1)代码如下所示:
#include #include #include void maxAndMin(int data[], int size, int *max, int *min) { *max = data[0]; *min = data[0]; for (int i = 0; i < size; i++) { if (*max < data[i]) *max = data[i]; if (*min > data[i]) *min = data[i]; } } int main(int argc, const char * argv[]) { int data[10]; srand((unsigned)time(0)); for (int i = 0; i < 10; i++) data[i] = rand() % 100; printf("数组中的数据为:"); for (int i = 0; i < 10; i++) printf("%d ", data[i]); printf("\n"); int max; int min; maxAndMin(data, 10, &max, &min); printf("最大值为:%d\n", max); printf("最小值为:%d\n", min); return 0; }
上述代码中,下面代码行:
- void maxAndMin(int data[], int size, int *max, int *min)
是自定义函数的函数头,该函数为无返回值函数,函数名为maxAndMin,函数有四个形参,第一个形参为一个数组,在此数组中找到最大值和最小值;第二个形参为数组的长度;第三个形参是一个指针变量,此参数用于将数组中的最大值带回主函数;第四个形参也是一个指针变量,此参数用于将数组中的最小值带回主函数。
上述代码中,下面代码行:
- *max = data[0];
- *min = data[0];
首先假定数组的最大值与最小值都是数组的第一个元素。
上述代码中,下面代码行:
- for (int i = 0; i < size; i++)
- {
- if (*max < data[i])
- *max = data[i];
- if (*min > data[i])
- *min = data[i];
- }
遍历数组,逐个元素与最大值和最小值对比,如果比最大值大,替换最大值;如果比最小值小,则替换最小值。
上述代码中,下面代码行:
- int main(int argc, const char * argv[])
- {
- int data[10];
在主函数中,定义数组data,有10个元素。
上述代码中,下面代码行:
- srand((unsigned)time(0));
- for (int i = 0; i < 10; i++)
- data[i] = rand() % 100;
- printf("数组中的数据为:");
- for (int i = 0; i < 10; i++)
- printf("%d ", data[i]);
- printf("\n");
首先,为每个数组元素赋值成一个0~99之间的随机数。
然后,将数组中数据显示出来。
上述代码中,下面代码行:
- int max;
- int min;
定义两个变量,分别用于存储数组中的最大值和最小值。
上述代码中,下面代码行:
- maxAndMin(data, 10, &max, &min);
调用maxAndMin函数,数组data作为第一个实参,第二个实参是数组data的长度,第三个实参是变量max的地址,这样可以将最大值带回来,第四个实参是变量min的地址,它用于带回最小值。
4.3 完整代码
本案例的完整代码如下所示:
#include #include #include void maxAndMin(int data[], int size, int *max, int *min) { *max = data[0]; *min = data[0]; for (int i = 0; i < size; i++) { if (*max < data[i]) *max = data[i]; if (*min > data[i]) *min = data[i]; } } int main(int argc, const char * argv[]) { int data[10]; srand((unsigned)time(0)); for (int i = 0; i < 10; i++) data[i] = rand() % 100; printf("数组中的数据为:"); for (int i = 0; i < 10; i++) printf("%d ", data[i]); printf("\n"); int max; int min; maxAndMin(data, 10, &max, &min); printf("最大值为:%d\n", max); printf("最小值为:%d\n", min); return 0; }
5 指针的算术运算
5.1 问题
测试不同类型的指针的算术运算。
5.2 步骤
实现此案例需要按照如下步骤进行。
步骤一:指针的算术运算代码如下所示:
#include int main() { int a; int *pa = &a; printf("pa = %p, pa + 1 = %p\n", pa, pa + 1); short s; short *ps = &s; printf("ps = %p, ps + 1 = %p\n", ps, ps + 1); long l; long *pl = &l; printf("pl = %p, pl + 1 = %p\n", pl, pl + 1); char c; char *pc = &c; printf("pc = %p, pc + 1 = %p\n", pc, pc + 1); float f; float *pf = &f; printf("pf = %p, pf + 1 = %p\n", pf, pf + 1); double d; double *pd = &d; printf("pd = %p, pd + 1 = %p\n", pd, pd + 1); return 0; }
上述代码中,以下代码:
- int a;
- int *pa = &a;
- printf("pa = %p, pa + 1 = %p\n", pa, pa + 1);
首先,定义了一个整型变量a。
然后,定义了一个整型指针变量pa,并初始化为整型变量a的地址。
最后,使用函数printf输出指针pa的值和pa+1的值。从输出结果可以看出,整型指针变量加1时,相当于加了1*sizeof(int),即4个字节。
上述代码中,以下代码:
- short s;
- short *ps = &s;
- printf("ps = %p, ps + 1 = %p\n", ps, ps + 1);
首先,定义了一个短整型变量s。
然后,定义了一个短整型指针变量ps,并初始化为短整型变量s的地址。
最后,使用函数printf输出指针ps的值和ps+1的值。从输出结果可以看出,短整型指针变量加1时,相当于加了1*sizeof(short),即2个字节。
上述代码中,以下代码:
- long l;
- long *pl = &l;
- printf("pl = %p, pl + 1 = %p\n", pl, pl + 1);
首先,定义了一个长整型变量l。
然后,定义了一个长整型指针变量pl,并初始化为长整型变量l的地址。
最后,使用函数printf输出指针pl的值和pl+1的值。从输出结果可以看出,长整型指针变量加1时,相当于加了1*sizeof(long),即8个字节。
上述代码中,以下代码:
- char c;
- char *pc = &c;
- printf("pc = %p, pc + 1 = %p\n", pc, pc + 1);
首先,定义了一个字符型变量c。
然后,定义了一个字符型指针变量pc,并初始化为字符型变量c的地址。
最后,使用函数printf输出指针pc的值和pc+1的值。从输出结果可以看出,字符型指针变量加1时,相当于加了1*sizeof(char),即1个字节。
上述代码中,以下代码:
- float f;
- float *pf = &f;
- printf("pf = %p, pf + 1 = %p\n", pf, pf + 1);
首先,定义了一个浮点型变量f。
然后,定义了一个浮点型指针变量pf,并初始化为浮点型变量f的地址。
最后,使用函数printf输出指针pf的值和pf+1的值。从输出结果可以看出,浮点型指针变量加1时,相当于加了1*sizeof(float),即4个字节。
上述代码中,以下代码:
- double d;
- double *pd = &d;
- printf("pd = %p, pd + 1 = %p\n", pd, pd + 1);
首先,定义了一个双精度浮点型变量d。
然后,定义了一个双精度浮点型指针变量pd,并初始化为双精度浮点型变量d的地址。
最后,使用函数printf输出指针pd的值和pd+1的值。从输出结果可以看出,双精度浮点型指针变量加1时,相当于加了1*sizeof(double),即8个字节。
5.3 完整代码
本案例的完整代码如下所示:
#include int main() { int a; int *pa = &a; printf("pa = %p, pa + 1 = %p\n", pa, pa + 1); short s; short *ps = &s; printf("ps = %p, ps + 1 = %p\n", ps, ps + 1); long l; long *pl = &l; printf("pl = %p, pl + 1 = %p\n", pl, pl + 1); char c; char *pc = &c; printf("pc = %p, pc + 1 = %p\n", pc, pc + 1); float f; float *pf = &f; printf("pf = %p, pf + 1 = %p\n", pf, pf + 1); double d; double *pd = &d; printf("pd = %p, pd + 1 = %p\n", pd, pd + 1); return 0; }
6 const的使用
6.1 问题
定义const的普通变量和const指针的三种方式,验证一下效果。
6.2 步骤
实现此案例需要按照如下步骤进行。
步骤一:const的使用代码如下所示:
#include int main() { int i1=5; const int i2 = 10; //i2 = 6; //i2++; i1 = i2; i1 = 100; printf("i2=%d\n",i2); const int *p1 = &i2; //*p1 = 200; p1 = &i1; int * const p2 = &i2; *p2 = 200; //p2 = &i1; printf("i2=%d\n",i2); return 0; }
上述代码中,以下代码:
- int i1=5;
定义了一个整型变量i1,并初始化为5。
上述代码中,以下代码:
- const int i2 = 10;
定义了一个const整型变量i2,并初始化为10。const是一个C语言的关键字,它限定变量i2不允许被改变,如以下代码:
- //i2 = 6;
- //i2++;
是错误的,如果不注释,将报编译错误。但以下代码:
- i1 = i2;
- i1 = 100;
是正确的,因为变量i1未被const关键字修饰。
上述代码中,以下代码:
- const int *p1 = &i2;
使用关键字const修饰了一个整型指针变量。由于关键字const在*号的左边,所以此时限定不能改变的是指针p1指向的地址的值,如以下语句:
- //*p1 = 200;
是错误的,因为*p1代表指针p1指向的地址的值。但以下代码:
- p1 = &i1;
是正确的。因为关键字const在*号的左边并未限定指针p1本身不能改变。
上述代码中,以下代码:
- int * const p2 = &i2;
同样使用关键字const修饰了一个整型指针变量。但由于关键字const在*号的右边,所以此时限定不能改变的是指针p1本身的值,如以下语句:
- *p2 = 200;
是正确的。因为关键字const在*号的右边并未限定指针p1指向的地址的值。但以下代码:
- //p2 = &i1;
是错误的,由于关键字const在*号的右边,所以此时指针p1本身的值不能改变。
6.3 完整代码
本案例的完整代码如下所示:
#include int main() { int i1=5; const int i2 = 10; //i2 = 6; //i2++; i1 = i2; i1 = 100; printf("i2=%d\n",i2); const int *p1 = &i2; //*p1 = 200; p1 = &i1; int * const p2 = &i2; *p2 = 200; //p2 = &i1; printf("i2=%d\n",i2); return 0; }
7 指针和数组
7.1 问题
定义一个数组,然后赋值给指针,然后互相用对方的方式进行操作。
7.2 步骤
实现此案例需要按照如下步骤进行。
步骤一:指针和数组代码如下所示:
#include int main() { int arr[5] = {10, 20, 30, 4, 5}; int *po = arr; for(int i = 0; i < 5; i++) { printf("arr=%d\n",*(arr+i)); printf("po=%d\n",*(po+i)); printf("arr[i]=%d\n",i[arr]);//*(arr+i) } printf("*po=%d\n",*po++); printf("*po=%d\n",(*po)++); printf("%d\n",*po); po++; printf("*po=%d\n",*po); //arr = arr+1;//arr++; //printf("*arr=%d\n",*arr); return 0; }
上述代码中,以下代码:
- int arr[5] = {10, 20, 30, 4, 5};
定义了一个整型数组arr,数组中有5个元素,并对数组进行了初始化。
上述代码中,以下代码:
- int *po = arr;
定义了一个整型指针变量po,并初始化为数组名arr,这种赋值是合法的,因为数组名就是数组第一个元素arr[0]的地址。
上述代码中,以下代码:
- printf("arr=%d\n",*(arr+i));
数组名arr是数组第一个元素arr[0]的地址。当i为0时,*(arr+i)等效于*arr,即数组第一个元素arr[0]。当i为1时,arr+1是数组第二个元素arr[1]的地址,*(arr+i)即数组第二个元素arr[1]。依次类推。
上述代码中,以下代码:
- printf("po=%d\n",*(po+i));
使用函数printf输出指针po+i指向的地址的值,当i为0时,指针po+0就是数组名arr的值,而arr又是数组第一个元素arr[0]的地址,所以*(po+0)就是arr[0]。当i为1时,指针po+1就是数组名arr+1的值,而arr+1又是数组第二个元素arr[1]的地址,所以*(po+1)就是arr[1]。依次类推。
上述代码中,以下代码:
- printf("arr[i]=%d\n",i[arr]);
i[arr]是一种不推荐使用的方法。i[arr]与arr[i]是等价的,在编译时分别被编译成*(i+arr)和*(arr+i),而它们是一回事。
上述代码中,以下代码:
- printf("*po=%d\n",*po++);
由于*po++左右有两个运算符*和++,它们是同优先级的,结合性是自右向左,所以先计算++,但又由于++是后置,所以该语句等效于以下语句:
- printf("*po=%d\n",*po);
- po = po + 1;
即输出为*po=10;
上述代码中,以下代码:
- printf("*po=%d\n",(*po)++);
同样是因为++是后置的,所以该语句等效于以下语句:
- printf("*po=%d\n",*po);
- *po = *po + 1;
即输出为*po=20;不是10的原因是上次等效时,进行了po = po + 1运算。
上述代码中,以下代码:
- printf("%d\n",*po);
输出为*po=21,是因为上次等效时,进行了*po = *po + 1运算。
上述代码中,以下代码:
- po++;
- printf("*po=%d\n",*po);
输出为*po=30,因为po++再次改变了po的值。
上述代码中,以下代码:
- //arr = arr+1;//arr++;
- //printf("*arr=%d\n",*arr);
为错误的。因为数组名是一个常量,虽然它是数组第一个元素arr[0]的地址,但它不能改变。
7.3 完整代码
本案例的完整代码如下所示:
#include int main() { int arr[5] = {10, 20, 30, 4, 5}; int *po = arr; for(int i = 0; i < 5; i++) { printf("arr=%d\n",*(arr+i)); printf("po=%d\n",*(po+i)); printf("arr[i]=%d\n",i[arr]);//*(arr+i) } printf("*po=%d\n",*po++); printf("*po=%d\n",(*po)++); printf("%d\n",*po); po++; printf("*po=%d\n",*po); //arr = arr+1;//arr++; //printf("*arr=%d\n",*arr); return 0; }
上一篇: QoS学习笔记