指针的定义
内存区域中的每字节都对应一个编号,这个编号就是“地址”。如果在程序中定义了一个变量,那么在对程序进行编译时,系统就会给这个变量分配内存单元。按变量地址存取变量值的方式称为“直接访问”,如printf("%d"i);scanf("%d",&i);等;另一种存取变量值的方式称为“间接访问”,即将变量 的地址存放到另一个变量中。在 C 语言中,指针变量是一种特殊的变量,它用来存放变量地址。
指针变量存放的地址中存放的数据是什么类型,基类型就必须也是相同的类型。
指针与指针变量是两个概念,一个变量的地址称为该变量的“指针”。例如,地址 2000 是变量的指针。如果有一个变量专门用来存放另一变量的地址(即指针),那么称它为“指针变量”。例如,左图中的 i_pointer 就是一个指针变量。
那么 i_pointer 本身占多大的内存空间呢? 本章中编写的程序都是 64 位应用程序,寻址范围为 64 位即 8 字节,所以对于本章来说 sizeof(i_pointer)=8。如果编写的程序是 32 位,那么寻址范围就是 4 字节 (考研中往往会强调程序是 32 位的程序)。
&:取地址运算符,叫做引用,通过该操作符我们可以获取一个变量的地址值。
- :取值操作符,叫做解引用,通过该操作符我们可以得到一个地址对应的数据。
指针的传递使用场景
指针的使用场景就俩:传递和偏移
传递
C语言的函数调用是值传递,所以下面这样change函数是改变不了 i 的值
所以就需要用指针把变量的地址传过去
偏移
前面介绍了指针的传递。指针即地址,就像我们找到了一栋楼,这栋楼的楼号是 B,那么往前就是 A,往后就是 C,所以应用指针的另一个场景就是对其进行加减,但对指针进行乘除是没有意义的,就像家庭地址乘以 5 没有意义那样。在工作中,我们把对指针的加减称为指针的偏移,加就是向后偏移,减就是向前偏移。
数组变量中存放的都是数组中第一个元素的存储的地址。所以数组的传递会弱化为指针。
注意指针偏移中+1-1都是逻辑上的“1”,在内存中加减的单位是基类型的字节长度。下图中就可以看到p+1和p差了4个字节,也就是int的大小。
指针与动态内存申请
因为malloc返回的指针是无类型的,所以需要强制转换类型
如果要对指针进行偏移之后再free,那么提前准备一个变量来存放初始值
堆和栈的差异
如果想让子函数里面创造的数据返回给主函数,凭直觉可能这么写
打印出来的结果如下,第二次打印会出问题。
为什么第二次打印会有异常?原因是 print_stack()函数中的字符串存放在栈空间中,函数执行结束后,栈空间会被释放,字符数组 c 的原有空间已被分配给其他函数使用,因此在调用 print_stack()函数后,printf("p=%sn",p);中的p不能获取栈空间的数据。
如果要让子函数的数据返回主函数,那么就要用malloc
这样打印出来就是正常的。
之所以现在打印是正常的,是因为print_malloc()函数中的字符串存放在堆空间中,堆空间只有在执行free 操作后才会释放,否则在进程执行过程中会一直有效。