1,*和&的使用
&:取操作符,取出操作对象在内存单元中的地址;*:访问操作符,访问操作对象所指向的变量
注意点:&的操作对象必须是变量或数组元素,不能是常量或表达式,因为常量和表达式在内存中没有占用固定的单元;&也不能作用于寄存器变量
*的操作对象必须是指针变量或指针表达式
2,指针变量和指针所指变量
指针变量:用于存储某个内存地址的变量
指针所指变量:指针变量中保存的内存地址所对应的变量
注意:指针变量在使用进行间接访问前必须初始化,同时一个变量只能指向同一类型的变量
3,指针数组和数组指针
int *p1[10];
int (*p2)[10];
根据运算符的优先级进行判断:
(1)'[]的优先级' >'*的优先级',∴ p1和[]先结合是数组;int *p1[10]; 是一个指针数组,该数组里面有5个元素,每个元素类型是(int *)
(2)'()的优先级' >'[]的优先级',∴p1和*先结合是指针;int (*p2)[10];是一个数组指针,该指针指向一个匿名数组,数组里有10个int型元素
补充:其实,指针变量的类型就是把相应数据的说明中的标示符去除后剩下的部分,如int (*p2)[10]; 去除p2后int (*)[10],int (*p2)[10]可以写成int (*)[10]p2,前面是指针类型,后面是指针变量,但是看着别扭,所以把p2提到前面,效果是一样的
4,数组的首地址和数组首元素的首地址(a 和 &a的区别)
int a[5] = {1,2,3,4,5};
int (*p1)[5] = &a;
int (*p2)[5] = a; //会警告类型不一致!但是当变量作为右值时,编译器只计算变量的值,此时和&a变量的值相同,所以不提示错误,运行结果会发现结果一样!
使用printf("%d%d",&a+1,a+1);运行结果不同如下:
#include <stdio.h> #include <stdlib.h> #include <string.h> int main(){ int a[5]={1,2,3,4,5}; int (*p1)[5]; int (*p2)[5]; p1 = &a; p2 = a; printf("p1 = %d\n", p1); printf("p2 = %d\n", p2); printf("&a = %d\n", &a); printf("a = %d\n", a); printf("&a+1 = %d\n", &a+1); printf("a+1 = %d\n", a+1); printf("p1+1 = %d\n", p1+1); printf("p2+1 = %d\n", p2+1); return 0; }
运行结果:
p1 = 2359028
p2 = 2359028
&a = 2359028
a = 2359028
&a+1 = 2359048
a+1 = 2359032
p1+1 = 2359048
p2+1 = 2359048
另一个例子:
int main() { int a[4]={1,2,3,5}; int *ptr1=(int *)(&a+1); int *ptr2=(int *)((int)a+1); int *ptr3=(int *)(&a); printf("%d\n",a); printf("%d\n",a+1); printf("%d\n",(int)a+1); printf("%d\n",&a+1); printf("%d,%d\n",*(ptr1-1),*ptr2); printf("%d,%d\n",*(ptr1),*(ptr3+1)); printf("%d,%d\n",(int *)(&a),ptr3);
printf("%d",ptr1[-1]); return 0; }
解析:
int *ptr1=(int *)(&a+1):将&a+1强制转化为(Int*)类型,然后赋值给ptr1,此时ptr1指向数组a的下一个整形数据;
ptr1[-1]:相当于*(ptr1 -1),即ptr1向后退四个字节地址处所指的数组元素的值,此题中为5;
int *ptr2=(int *)((int)a+1):就是将地址a转化为整型值,然后整数之间的加减运算+1,就是a[0]第二个字节的地址,然后把这个地址强制转换为(int *)后赋值给ptr2,
所以,*ptr2的值为a[0]第二个字节后的4个字节的内容,如下图所示:
5,空指针、野指针和通用指针(void指针/泛指针)
空指针:(NULL)不指向任何对象,和其他有效地址的值均不同,是<stddef.h>里面定义的宏,可理解为一个纯粹的0;
使用其可以防止指向任何未知的内存区域;
主要用法:(1)用空指针终止对递归数据结构的间接引用。如:链表、哈希表等递归数据结构,while(p!=NUll)
(2)用空指针作函数调用失败时的返回值。许多C库函数的返回值是一个指针,在函数调用成功时,函数返回一个指向某一对象的指针;反之,则返回一个空指针 (3)用空指针作警戒值 :警戒值是标志事物结尾的一个特定值。例如,main()函数的预定义参数argv是一个指针数组,它的最后一个元素(argv[argc])永远是一个空指针
注意:指针的值不能是整型值,但空指针是个例外,即空指针的值可以是一个纯粹的零(空指针的值并不必须是一个纯粹的零,但这个值是唯一有用的值。在编译时产生的任意一个表达式,只要它是零,就可以作为空指针的值。在程序运行时,最好不要出现一个为零的整型变量);
绝对不能间接引用一个空指针,否则,你的程序可能会得到毫无意义的结果,或者得到一个全部是零的值,或者会突然停止运行。
野指针:不是NULL指针,是指向被释放的或者访问受限的垃圾内存的指针
避免方法
int main(){ int *p =(int *) malloc(sizeof(int )*6); free(p); p = NULL; if(p!=NULL){ *p = 7; printf("%d",*p); } }
char *month_name(int n){ char *name[]={"illegal","January","February","March"}; if(n<1) return name[0]; else return name[n]; } int main(){ int n; char *p; scanf("%d",&n); p=month_name(n); printf("month = %s",p); return 0; }
实例代码2:
double square(double x){ return x*x; } int main(){ double (*p)(double x); p = square; printf("%f,%f,%f",square(1.6),p(1.6),(*p)(1.6)); return 0; }