一、什么是指针

C语言里,变量存放在内存中,而内存其实就是一组有序字节组成的数组,每个字节有唯一的内存地址。CPU 通过内存寻址对存储在内存中的某个指定数据对象的地址进行定位。这里,数据对象是指存储在内存中的一个指定数据类型的数值或字符串,它们都有一个自己的地址,而指针便是保存这个地址的变量。也就是说:指针是一种保存变量地址的变量
前面已经提到内存其实就是一组有序字节组成的数组,数组中,每个字节大大小固定,都是 8bit。对这些连续的字节从 0 开始进行编号,每个字节都有唯一的一个编号,这个编号就是内存地址。示意如下图:
image.png
这是一个 4GB 的内存,可以存放 2^32 个字节的数据。左侧的连续的十六进制编号就是内存地址,每个内存地址对应一个字节的内存空间。而指针变量保存的就是这个编号,也即内存地址。

二、为什么要使用指针

在C语言中,指针的使用非常广泛,因为使用指针往往可以生成更高效、更紧凑的代码。总的来说,使用指针有如下好处:
1)指针的使用使得不同区域的代码可以轻易的共享内存数据,这样可以使程序更为快速高效;
2)C语言中一些复杂的数据结构往往需要使用指针来构建,如链表、二叉树等;
3)C语言是传值调用,而有些操作传值调用是无法完成的,如通过被调函数修改调用函数的对象,但是这种操作可以由指针来完成,而且并不违背传值调用。

三、如何声明一个指针

3.1 声明并初始化一个指针

指针其实就是一个变量,指针的声明方式与一般的变量声明方式没太大区别:
image.png
image.png
image.png

3.2 未初始化和非法的指针

image.png
image.png
image.png

3.3 NULL指针

image.png四、指针的运算

image.png
image.png

五、指针与数组

5.1 指针与数组的关系

image.png
image.png
image.png
image.png

5.2 指针数组

image.png

5.3 数组指针

image.png

  1. #include "stdio.h"
  2. int main(){
  3. int arr[2][3] = {1,2,3,4,5,6}; // 定义一个二维数组并初始化
  4. int (*p)[3]; // 定义一个数组指针,指针指向一个含有3个元素的一维数组
  5. p = arr; // 将二维数组的首地址赋给 p,此时 p 指向 arr[0] 或 &arr[0][0]
  6. printf("%d\n",(*p)[0]); // 输出结果为 1
  7. p++;   // 对 p 进行算术运算,此时 p 将指向二维数组的下一行的首地址,即 &arr[1][0]
  8. printf("%d\n",(*p)[1]); // 输出结果为5
  9. return 0;
  10. }

六、指针与结构

6.1 简单介绍一下结构

image.png

  1. struct message{       // 声明一个结构 message
  2. char name[10]; // 成员
  3. int age;
  4. int score;
  5. };
  6. typedef struct message s_message; // 类型定义符 typedef
  7. s_message mess = {"tongye",23,83};// 声明一个 struct message 类型的变量 mess,并对其进行初始化 
  8. --------------------------------------------------------------------------------------------------------------
  9. /* 另一种更简便的声明方法 */
  10. typedef struct{
  11.   char name[10];
  12.   int age;
  13.   int score;
  14. }message;

image.png

6.2 结构指针

image.png

七、指针与函数

C语言的所有参数均是以“传值调用”的方式进行传递的,这意味着函数将获得参数值的一份拷贝。这样,函数可以放心修改这个拷贝值,而不必担心会修改调用程序实际传递给它的参数。

7.1 指针作为函数的参数

image.png

  1. #include "stdio.h"
  2. void swap1(int a,int b)// 参数为普通的 int 变量
  3. {
  4.   int temp;
  5.   temp = a;
  6.   a = b;
  7.   b = temp;
  8. }
  9. void swap2(int *a,int *b)// 参数为指针,接受调用函数传递过来的变量地址作为参数,对所指地址处的内容进行操作
  10. {
  11.   int temp; // 最终结果是,地址本身并没有改变,但是这一地址所对应的内存段中的内容发生了变化,即x,y的值发生了变化
  12.   temp = *a;
  13.   *a = *b;
  14.   *b = temp;
  15. }
  16. int main()
  17. {
  18.   int x = 1,y = 2;
  19.   swap1(x,y); // 将 x,y 的值本身作为参数传递给了被调函数
  20.   printf("%d %5d\n",x,y); // 输出结果为:1 2
  21.   swap(&x,&y); // 将 x,y 的地址作为参数传递给了被调函数,传递过去的也是一个值,与传值调用不冲突
  22.   printf("%d %5d\n",x,y); // 输出结果为:2 1
  23.   return 0;
  24. }

7.2 指向函数的指针

image.png

  1. #include "stdio.h"
  2. #include "string.h"
  3. int str_comp(const char *m,const char *n); // 声明一个函数 str_comp,该函数有两个 const char 类型的指针,函数的返回值为 int 类型
  4. void comp(char *a,char *b,int (*prr)(const char *,const char*)); // 声明一个函数 comp ,注意该函数的第三个参数,是一个函数指针
  5. int main()
  6. {
  7. char str1[20]; // 声明一个字符数组
  8. char str2[20];
  9. int (*p)(const char *,const char *) = str_comp;   // 声明并初始化一个函数指针,该指针所指向的函数有两个 const char 类型的指针,且返回值为 int 类型
  10. gets(str1); // 使用 gets() 函数从 I/O 读取一行字符串
  11. gets(str2);
  12. comp(str1,str2,p); // 函数指针 p 作为参数传给 comp 函数
  13. return 0;
  14. }
  15. int str_comp(const char *m,const char *n)
  16. {
  17.    // 库函数 strcmp 用于比较两个字符串,其原型是: int strcmp(const char *s1,const char *s2);
  18. if(strcmp(m,n) == 0)
  19. return 0;
  20. else
  21. return 1;
  22. }
  23. /* 函数 comp 接受一个函数指针作为它的第三个参数 */
  24. void comp(char *a,char *b,int (*prr)(const char *,const char*))
  25. {
  26. if((*prr)(a,b) == 0)
  27. printf("str1 = str2\n");
  28. else
  29. printf("str1 != str2\n");
  30. }

image.png