&取地址运算符

image.png

打印一个地址

printf(“%p”,&i)
image.png image.png
32位架构 int和地址数据所占字节大小相同
image.png
64位架构 int和地址数据所占字节大小不同
image.png

相邻变量的地址

**本地变量都存放在堆栈中,它们自顶而下,地址逐渐变小

数组的地址

image.pngimage.png

指针

image.png
image.png
C语言中,没有int这种类型,与p相连,表示p是一个指针
int p,q;
int
p,q;
二者的意思相同,表示p是一个指针,q是一个int类型的变量

p指向i,p保存了i的地址
image.png

指针变量

image.png

作为参数的指针

image.png
image.png
image.png

访问地址上的变量

image.png
image.png

传入地址

指针的运算符&*

image.png

左值

image.png

指针的应用

交换两个变量的值

image.png
image.png

通过指针使得函数返回多个值

image.png
通过指针传入值,并通过指针返回值
image.png

return返回函数状态,指针返回函数结果

image.png
image.png

指针最常见错误

image.png
image.png
int p =0; p =12会让地址为0的地方写入12,会产生重大错误
正确写法为int p =&i; p =12

传入函数的数组变成了什么(数组与指针)

image.png

举例

image.png
void minmax(int a[], int len, int min, int max)
函数的a[]是数组a[13]的首位a[0]的地址(指针),只是看着像数组。
所以在函数中,用sizeof不能得到a[]正确的个数,因为**sizeof(a)相当于是求指针a所占内存的大小,结果为4.*
image.png
将void minmax(int a[], int len, int
min, int max)的第一个参数改为int a,程序正常运行。证明第一个参数a[]确实是地址*a。

数组参数

image.png

数组变量是特殊的指针

image.png

*p == p[0]

举例
int p = &min; p == p[0] ==min
p[0]相当于把*p所指的地方当作数组

int b[] —> int *const b ;(b是一个常数,不能被改变)

错误:a[] = b[];

相当于两个地址,不能做赋值,把一个的值改变

指针与const

image.png

指针是const

image.png
int * comst q = &i;
q的值(i的地址)不能被改变,相当于q不能再指向别的变量,只指向i。

所指的是const

image.png
const int *p = &i;
i的值可以改变,p也可以指向别的变量,但不能通过指针p修改i的值(*p是const)
image.png

辨析

image.png
const int p1 = &i;和*int const p1 = &i;等价,指针指向的值不能被修改
int **
const * p1 = &i;const在后面,表示指针不能被修改

转换

image.png
void f(const int *x)常在结构体中使用,通过指针传递结构体,但不改变结构体的值
类似于数组传递,只将数组的头地址(a[])传给函数,

const数组

image.png
数组可以看作一种const的指针,a[13]中a是指针是const,对应关系不变
const int a=5;a是int类型的常量,不能通过指针重新给a赋值,只能在初始化时进行赋值

保护数组值

image.png

数组变量和const指针

测试程序如下,发现数组变量a和const指针p在 初始化、sizeof()运算、&运算 上有所不同。

  1. #include <stdio.h>
  2. int main(void)
  3. {
  4. int a[10]={0,1,2,3,4,5,6,7,8,9};
  5. int* const p = a;
  6. //测试1:(由于影响编译,因此将其注释掉,与其他测试并非在同一次中进行)
  7. //能否用一个数组变量来初始化一个数组变量?
  8. //能否用一个const指针来初始化一个const指针?
  9. /*
  10. int b[10] = a; //编译无法通过 error:invalid initializer
  11. //非法的初始化,因此不可以用一个数组变量来初始化一个数组变量
  12. int* const q = p; //编译通过,这也是通常的用法
  13. printf("q=%p,p=%p\n", q, p); //运行结果 q=0022FEE0,p=0022FEE0
  14. //可见q和p是一样的,因此可以用一个const指针来初始化一个const指针
  15. */
  16. //测试2:(由于影响编译,因此将其注释掉,与其他测试并非在同一次中进行)
  17. //能否用一个数组变量来初始化一个const指针?
  18. //能否用一个const指针来初始化一个数组变量?
  19. /*
  20. int* const q = a; //编译通过,这也是通常的用法
  21. printf("q=%p,a=%p\n", q, a); //运行结果 q=0022FEE0,a=0022FEE0
  22. //可见q和a是一样的,因此可以用一个数组变量来初始化一个const指针
  23. int b[10] = p; //编译无法通过 error:invalid initializer
  24. //非法的初始化,因此不可以用一个const指针来初始化一个数组变量
  25. */
  26. //以下的测试为同一次编译运行
  27. //测试3:
  28. //能否对一个数组变量进行sizeof()运算,结果是什么?
  29. //能否对一个const指针进行sizeof()运算,结果是什么?
  30. printf("sizeof(a)=%d\n", sizeof(a)); //编译通过,运行结果 sizeof(a)=40
  31. printf("sizeof(p)=%d\n", sizeof(p)); //编译通过,运行结果 sizeof(p)=4
  32. //sizeof(a)得到的是整个数组的大小4*sizeof(int)
  33. //sizeof(p)得到的是一个指针的大小
  34. //测试4:
  35. //数组变量的值是什么?
  36. //const指针的值是什么?
  37. //能否对一个数组变量进行“&”运算,结果是什么?
  38. //能否对一个const指针进行“&”运算,结果是什么?
  39. printf("a=%p\n", a); //编译通过,运行结果 a=0022FEE8
  40. printf("p=%p\n", p); //编译通过,运行结果 p=0022FEE8
  41. printf("&a=%p\n", &a); //编译通过,运行结果 &a=0022FEE8
  42. printf("&a[0]=%p\n", &a[0]); //编译通过,运行结果 &a[0]=0022FEE8
  43. printf("&p=%p\n", &p); //编译通过,运行结果 &p=0022FEE4
  44. //对数组变量和const指针都能进行“&”运算
  45. //a,&a,&a[0]的值相同,说明a的值是一个地址,所以可以把a赋给p
  46. //a与&a相同,都是数组a的第一个元素a[0]的地址
  47. //p的值就是程序开始时初始化得到的a的值
  48. //&p取到的是p这个constant量自己在内存中的地址,和普通变量一样
  49. //a的地址竟然是它自己,这个确实很有趣,可以理解,但有点说不通
  50. //测试5:
  51. //能否对一个数组变量进行“*”运算?
  52. //能否对一个const指针进行“*”运算?
  53. printf("*a=%d\n", *a); //编译通过,运行结果 *a=0
  54. printf("*p=%d\n", *p); //编译通过,运行结果 *p=0
  55. //对数组变量和const指针都能进行“*”运算
  56. //意义也相同
  57. //测试6:
  58. //能否对一个数组变量进行“[n]”运算?
  59. //能否对一个const指针进行“[n]”运算?
  60. printf("a[6]=%d\n", a[6]); //编译通过,运行结果 a[6]=6
  61. printf("p[6]=%d\n", p[6]); //编译通过,运行结果 p[6]=6
  62. //对数组变量和const指针都能进行“[n]”运算
  63. //两者运算规律相同,
  64. //偏移量n实际偏移量的都与指向的类型(如本测试中的int)相关
  65. //测试7:
  66. //数组变量+1是什么?
  67. //const指针+1是什么?
  68. printf("a+1=%p\n", a+1); //编译通过,运行结果 a+1=0022FEEC
  69. printf("p+1=%p\n", p+1); //编译通过,运行结果 p+1=0022FEEC
  70. printf("*(a+1)=%d\n", *(a+1)); //编译通过,运行结果 *(a+1)=1
  71. printf("*(p+1)=%d\n", *(p+1)); //编译通过,运行结果 *(p+1)=1
  72. printf("a[1]=%d\n", a[1]); //编译通过,运行结果 a[1]=1
  73. printf("p[1]=%d\n", p[1]); //编译通过,运行结果 p[1]=1
  74. //第一次写前两句时,用了%d,结果编译warning,说类型是int*
  75. //所以改为%p,说明a+1和p+1仍为指针
  76. //与测试4的结果相比较,a+1和a,p+1和p之间都相差sizeof(int)
  77. //两者运算规律相同,其意义也与[]运算符相同
  78. return 0;
  79. }

感悟

指针*p用来保存变量i的地址,在函数调用时可以传入i的地址,通过p=45,对指针p指向的i进行赋值和运算,在函数内可以改变函数外变量的值。
如果没有指针,传入函数的是变量的值,在函数内对变量的值进行运算,不能改变函数外的变量。