指针是C语言的精华所在,可以说,学习C语言的指针就是学习C语言管理内存的方式。
什么是指针
通俗地讲,指针就是一个储存内存地址的整形变量
指针的定义
定义指针跟定义普通变量差不多:
int *p; //声明一个指向int型数据的指针
格式为 类型* 指针名;
在声明时用int型变量为它赋值:
int i = 0;
int *p = &i;
需要注意的是,用于给指针赋值的只能是一个确定的内存地址:
int *p = 5; //错误,只能用一个确定的地址给指针赋值,5并不占有一块内存地址
如果在声明时不知道指针应该指向哪里,应该用NULL为指针赋值,防止出现一些奇怪的错误 :
int *p = NULL;
取地址符"&"
C语言提供了取地址符“&”来获取某程序实体(变量,指针,结构体,甚至是函数等)在内存中的地址,既然是在内存中的地址,那么同样对于没有确定地址的数字常量取地址是错误的。但是对于字符串,函数(在C语言中函数也被解析为是指针),取地址是正确的:
int i = &10;//错误
int j = &"abcdef";//合法语句
为什么 &"abcdf" 会是合法的语句呢?因为在C语言中,字符串被认为是字符串在内存中的首地址的指针,对指针取地址当然是合法的
类似下面这样的语句也是合法的:
char c[] = "abcd";
printf("%p\n",&c); //用16进制输出
使用“*”访问指针指向的内容
C语言提供了取值符号“*”来访问指针指向的内容,例如:
int i = 5;
int* p = &i;
printf("%d",*p); //等价于 printf("%d",i);
上面这个代码片段运行后会输出数字“5”
同样地,也可以用指针操作目标变量:
int i = 5;
printf("%d\n",i); //输出i
int* p = &i;
(*p)++;
printf("%d\n",i); //再次输出i
运行上面的代码,会输出数字“5”然后输出数字"6"
要注意的是,当运算符“*”与++, --运算符同时使用时要特别注意优先级问题,养成多用括号的习惯能避免产生很多奇怪的bug。
那么指针声明语句里的"*"又是代表什么意思呢?实际上在声明语句里,"*"只作为一个标志符,用来说明某个变量是指针变量。
指针变量指向的类型
指针的本质是一个int整形变量,但是指针指向的对象有类型之分,因为不同的类型占用不同的内存空间,类型不匹配就会出错,如上文所说的,用一个int型变量给一个int指针赋值:
int i = 0;
int *p = &i;
如果用其他类型的变量给int型指针赋值就是错误的:
long l = 123456789;
int *p = &l; //错误
为什么说这样是错误的呢?
在32位的机器上,int类型占4个字节,而long类型占8个字节。声明一个int型指针,指针指向4个字节的数据,将long赋值给int型的指针后,指针指向的内存变成了8个字节,但是指针能读取的大小只有4字节,还有4个字节没办法读取,于是就会造成程序读取到一些很奇怪的数据。如果用低精度的变量给指向高精度(占字节多的)数据类型的指针赋值,就会导致指针额外地改写内存,如果被改写的内存很重要,那么程序就会出错。
void类型指针
空类型指针又成为泛指针或者通用指针,空类型指针是C语言关于纯粹地址的一种约定:
int i = 5;
int* p = &i;
void* v = p;
对于指针p,它指向的是int型数据,而void指针可以指向任意类型,并且任意类型的指针赋值给它都不需要类型转换,反之亦然(实际上有些编译器会提示你要强制类型转换)
sizeof操作符
sizeof是C/C++中的一个操作符,简单地说,它的作用就是返回一个对象或者类型所占的内存字节数,例如我们可以用它获得我们机器上int类型所占的大小:
printf("%d",sizeof(int));
在博主的机器上上面这行代码输出数字“4”,即int占4个字节。还可以看看指针类型占的字节数:
printf("%d\n",sizeof(int*));
printf("%d\n",sizeof(char*));
printf("%d\n",sizeof(void*));
运行上面的语句,会输出三个同样的数字,例如在博主的机器上输出了三个数字“8”,我的是64位机器,32位机器会输出三个数字“4”,由此也说明不管何种类型的指针,其大小都是一样的,不同的是其指向空间的大小。
此外要注意的是,sizeof()语句会在编译期间被替换成一个常数,所以不能用if去判断类型所占的大小然后编写相应处理代码。
本节内容就到这里。