c语言指针 | 字数总计: 2.8k | 阅读时长: 11分钟 | 阅读量:
以下讲解是按照如下这个程序的执行顺序来讲解的
一,程序中的c语言指针 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 int a, b; int * p;a = 3 ; b = 4 ; 6 printf (" a的地址:%d;\r\n" , &a);printf (" b的地址:%d;\r\n" , &b);printf (" p的地址:%d;\r\n" , &p);printf (" p的值:%d,现在p的值是不确定的,目前只是为p申请了地址,还没有为它赋值;\r\n" , p);p = &a; printf (" 利用取址操作p = &a;,把a的地址赋值给p,现在p的值是%d,也就是a的地址;\r\n" , p);printf (" p的地址没有变化,p的地址仍然是%d,在这个地址上存储的是变量a的地址;\r\n" , &p);printf (" 利用*运算符得到指针p指向地址中的数值为%d,在刚才p已经指向变量a的地址了,所以指针p指向地址中的值是3,但是p的值仍然是a的地址;\r\n" , *p);b = *p; printf (" b = *p;,现在b的值就是p指向地址中的值,也就是a的值:%d;\r\n" , b);*p = 5 ; printf (" 现在利用*p为p指向地址中存储的值进行赋值:%d,这时a的值也已经改变了:%d;\r\n" , *p, a);
1 2 3 4 5 6 7 8 int a,b; int *p;a = 3 ; b = 4 ; printf (" a的地址:%d;\r\n" , &a);printf (" b的地址:%d;\r\n" , &b);printf (" p的地址:%d;\r\n" , &p);printf (" p的值:%d,现在p的值是不确定的,目前只是为p申请了地址,还没有为它赋值;\r\n" , p);
指针p定义的时候没有进行初始化,所以在这里,p的初始值是不确定的。
当然也可以在p定义的时候赋初值,这样p的初始值就是确定的了。
p = 1;
一元运算符&可用于取一个对象的地址,如下,这时p为指向a的指针。地址运算符&只能应用于内存中的对象,即变量与数组元素。它不能作用于表达式、常量或register类型的变量。
这时p的值是3930420,即变量a的地址;
利用&取出p的地址仍然为3930396,没有变;
利用间接寻址, p可以得到指针p指向地址中的值为3,即a的值。
p = &a;//取址运算/* p现在指向a */
printf(“ 利用取址操作p = &a;,把a的地址赋值给p,现在p的值是%d,也就是a的地址;\r\n”, p);
printf(“ p的地址没有变化,p的地址仍然是%d,在这个地址上存储的是变量a的地址;\r\n”, &p);
1 2 3 4 5 p = &a; printf (" 利用取址操作p = &a;,把a的地址赋值给p,现在p的值是%d,也就是a的地址;\r\n" , p);printf (" p的地址没有变化,p的地址仍然是%d,在这个地址上存储的是变量a的地址;\r\n" , &p);printf (" 利用*运算符得到指针p指向地址中的数值为%d,在刚才p已经指向变量a的地址了,所以指针p指向地址中的值是3,但是p的值仍然是a的地址;\r\n" , *p);
利用*可以得到p地址中的数值,这里b的值就是3,即a的值。
1 2 b = *p; printf (" b = *p;,现在b的值就是p指向地址中的值,也就是a的值:%d;\r\n" , b);
对*p进行赋值后,也就是对p指针指向地址中的数值进行赋值,这是a的值也就变为了5
1 2 * p = 5 ; printf (" 现在利用*p为p指向地址中存储的值进行赋值:%d,这时a的值也已经改变了:%d;\r\n" , *p, a);
二,图解C语言指针 定义两个变量
定义后,a的地址是0x2000,p的地址是0x3000; 在定义的时候a赋的初始值是3,p没有赋初始值,所以p的值是不确定的。
现在进行运算:
这时内存图就变成了这样,p的地址没有变化,但是p的值变化了,此时,*p=3;
三,指针与函数 指针作为函数的形参 在程序设计中,指针作为函数形参往往会带来意想不到的效果,下面用一个例程来讲解指针作为函数形参的特性。
例子:用一个函数交换两个变量的值:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 void Swap (int x, int y) { int temp = 0 ; temp = x; x = y; y = temp; } void Swap_pointer (int * x, int * y) { int temp = 0 ; temp = *x; *x = *y; *y = temp; } void Test2 () { int a = 1 , b = 2 ; Swap (a, b); printf ("a=%d\r\n" , a); printf ("b=%d\r\n" , b); Swap_pointer (&a, &b); printf ("\r\n" ); printf ("a=%d\r\n" , a); printf ("b=%d\r\n" , b); }
执行结果如下图,可以明显的看出指针作为函数形参的特性。
具体讲解详见《C语言程序设计》的5.2章节。
指针,结构体与函数 在C语言中,函数本身不是变量,但是也可以定义指向函数的指针。这种指针的使用方法、特性与变量指针的使用方法、特性大同小异。
下面的介绍都是围绕这段函数来讲解的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 #include "stdio.h" int max (int x, int y) { return x > y ? x : y; } int min (int x, int y) { return x < y ? x : y; } int * maxP (int x, int * y) { return x > * y ? x : *y; } void CallbackFun (int x, int (*f)(int , int b)) { int i = 0 , a = 0 ; for (i = x; i < 10 ; i++) { a = f (i, 0 ); printf ("%d, " , a); } printf ("\r\n" ); } typedef struct _dat { int (*fun)(int , int ); int * (*funP)(int , int *); void (*funFun)(int , int (*f)(int , int )); }datTypedef; datTypedef s; int main () { int maxval = 0 , minval = 0 ; int a = 10 ; int * pval; pval = &a; int (*p)(int , int ); p = &max; maxval = p (1 , 2 ); printf ("max=%d\r\n" , maxval); p = &min; minval = p (1 , 2 ); printf ("min=%d\r\n" , minval); s.fun = &max; maxval = s.fun (3 , 4 ); printf ("\r\nmax=%d\r\n" , maxval); s.funP = &maxP; pval = s.funP (9 , pval); printf ("max=%d\r\n" , pval); s.funFun = &CallbackFun; s.funFun (4 , max); system ("pause" ); }
1,指针指向函数。在某些场景下,我们需要用到指针指向函数。
2,在结构体中定义函数指针。利用这样的技巧,可以实现高级语言的特性,例如类的方法,属性等,可以使得代码更加安全规范。
3,函数指针作为函数的参数。
四,指针与数组 使用数组时,需要明白的一些事项:
1,申请的数组存储在相邻的内存区域中;
2,数组名所代表的就是该数组最开始的一个元素的地址;
3,对数组元素a[i]的引用也可以写成*(a+i)这种形式;
4,&a[i]和a+i的含义是相同的,简而言之,一个通过数组和下标实现的表达式可等价地通过指针和偏移量实现。;
5,数组名不是变量,因此,类似于a=pa和a++形式的语句是非法的;
6,当把数组名传递给一个函数时,实际上传递的是该数组第一个元索的地址;
下面通过一些例程来解释指针与数组的运用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 int StrLen (char s[]) { int n; for (n = 0 ; *s != '\0' ; s++) n++; return n; } void Array_pointer () { int arr[10 ]; int * p; int i = 0 ; for (i = 0 ; i < 10 ; i++) { arr[i] = i; printf ("数组[%2d]的地址:%d\r\n" , i, &arr[i]); } printf ("将指针p指向数组arr的第0个元素\r\n" ); p = &arr[0 ]; p = arr; for (i = 0 ; i < 10 ; i++) { printf ("p[%d]=%d\r\n" , i, *(p++)); } printf ("将指针p指向数组arr的第3个元素\r\n" ); p = &arr[3 ]; for (i = 0 ; i < 10 ; i++) printf ("p[%d]=%d\r\n" , i, *(p++)); printf ("安全的做法应该是\r\n" ); p = &arr[3 ]; for (i = 0 ; i < 10 - 3 ; i++) printf ("p[%d]=%d\r\n" , i, *(p++)); printf ("对数组也可以进行如下方式的使用\r\n" ); for (i = 0 ; i < 10 ; i++) printf ("arr[%d]=%d\r\n" , i, *(arr + i)); int n = 0 ; char * str = "hello,word" ; n = StrLen (str); printf ("n=%d\r\n" , n); n = StrLen (&str[2 ]); printf ("n=%d\r\n" , n); }
五,地址算术运算 通过以下这个例子讲解地址算术运算
这是一个不完善的存储分配程序。它由两个函数组成。第一个函数alloc(n)返回一个指向n个连续字符存储单元的指针,alloc函数的调用者可利用该指针存储字符序列。第二个函数afree(p)释放已分配的存储空间,以便以后重用。之所以说这两个函数是“不完善的”,是因为对afree函数的调用次序必须与调用alloc函数的次序相反。换句话说,alloc与afree以栈的方式(即后进先出的列表)进行存储空间的管理。标准库中提供了具有类似功能的函数malloc和free,它们没有上述限制。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 #define ALLOCSIZE 10000 static char allocbuf[ALLOCSIZE];static char * allocp = allocbuf; char * alloc (int n) { if (allocbuf + ALLOCSIZE - allocp >= n) { allocp += n; return allocp - n; } else return 0 ; } void afree (char * p) { if (p >= allocbuf && p < allocbuf + ALLOCSIZE) allocp = p; } void Pointer_cal () { char * allAddr; printf ("空闲地址:%d\r\n" , allocp); allAddr = alloc (100 ); printf ("空闲地址:%d\r\n" , allocp); afree (allAddr); printf ("空闲地址:%d\r\n" , allocp); }
未完,待续