C语言之指针

C语言之指针

1.指针

内容RAM支持随机寻址,对内存空间的访问通过地址进行

  • 变量名的本质就是地址的别名,编译器根据符号表进行绑定解析。
  • 指针的初衷用途就是通过间接访问的方式支持内存空间的匿名访问

指针的类型与所指向的数据类型相关联,但本质是储存地址,其大小与系统有关,与类型无关

  • 指针类型可以支持编译器类型检查
  • 指针运算与类型相关联

声明

  • 指针的类型

    • 函数指针: void (*f)(int, int);
    • 对象指针: char*, int*
    • 通用指针: void*, 一般作为函数的参数使用,转为其他类型需要强制类型转换,不不会丢失数据
  • 指针相关的运算符,优先级依次递增

    • 类型声明:int *
    • 取址:&
    • 间接访问:*
    • 自增自减:--, ++
    • 成员选择:->
    • 其他:(),[]
  • 易混淆的指针表达式,基于运算符的优先级与结合性

    *p++; // 间接访问(*p),然后自增p++
    &p++; // 指针的地址自增(&p)++
    &a.name; // &(a.name)
    int *a[]; // 指针数组,元素类型 int*
    int (*a)[]; // 数组指针,指向数组 int []
    int *f(); // 指针函数,返回值 int*
    int (*f)(); // 函数指针,指向函数 int f();
    int *(*f)[];// 数组指针,指向 int * a[], 元素类型 int*

运算

所有指针运算都会自动考虑所指向对象的长度

  • 有效的初始化:NULL或者表示地址的表达式
  • 有效的运算
    • 相同类型指针之间的赋值
    • 指针与整数之间的加减法
    • 数组或链表中相同类型的两个指针之间的减法和比较
      • 允许赋值但不可访问最后一个元素的下一个元素的位置
      • 减法的单位是数据类型的长度而不是字节
    • 将指针赋值为0或者与0进行比较

2.指针与函数

指针作为函数参数

  • C语言基于传值方式将参数传递给函数,将指针作为函数参数可以实现在函数内修改实参

指针函数

  • 函数的返回值类型是指针

函数指针

  • 指向函数的指针

    int func(void); // 声明函数
    int (*fp)(void); // 声明函数指针
    fp = func; // 等价于 f = &func;
    fp(); // 调用函数,等价于 (*f)();
  • 类似于数组名,函数名就是指向函数的指针常量,即函数的入口地址。

  • 主要用途

    • 回调函数
    • 转换表

3.指针与数组

  • 数组下标操作 E1[E2] 等价于指针操作 *(E1+E2)

    • 一般后者效率稍好,但前者更具可读性
    • 具体可参考 《C和指针》8.1.3和8.1.4中关于二者的效率对比
  • 数组名与数组指针:

    • 数组名表示数组首个元素的地址

      int a[5]; // 声明数组,分配5个整型空间
      int *p; // 声明指针变量,并未初始化
      // 情景1 赋值给字符指针
      p = a; // 等价于 p = &a[0];
      // 情景2 将数组名作为参数,实际传递的是指针
      int f(int *a); // 等价于 int f(int a[]); 但更推荐指针类型的写法
    • 数组名表示整个数组

      // 情景1 声明一个数组
      int a[3] = {1, 2, 3};
      // 情景2 使用sizeof计算整个数组的大小
      #define NKEYS (sizeof(a) / sizeof(a[0]))
      // 情景2 取址运算
      int (*pa)[3];
      pa = &a; // 将数组的地址赋值给数组指针
  • 注意指针的合法运算

指针与字符串

字符指针

字符串本质是一个字符数组常量,一般通过字符指针进行访问。

// 只是字符指针的赋值,不涉及字符串的复制
char p1 = "hello";
char p2 = p1;

4.指针与结构体

  • 将指向结构体的指针传递给函数优化效率,使用->间接访问成员

  • 利用指针实现引用声明

    // 自引用结构
    struct tnode {
    struct *tnode left;
    struct *tnode right;
    }
    // 所有指针大小一致,编译期间即可确定长度
    // 互引用结构
    struct B; // imcomplete declaration
    struct A {
    struct B *ref;
    }
    struct B {
    struct A *ref;
    }
  • 结构体地址与其第一个成员的地址相同,但是类型不同

    类似于数组与其首元素

    typedef struct A {
    int a;
    } T_A, *PT_A;
    T_A ta = {100};
    PT_A pa = &ta; // pa是指向结构体的指针,*pa表示结构体ta
    int *p = &pa->a; // p是指向整型的指针,*p表示ta.a
    // pa和p的值相等,因为ta和ta.a的地址相同,但是二者的类型并不相同
  • 经典应用:链表

5.二级指针

  • 修改指针变量的值

  • 指针数组传参

    // 指针数组和二维指针作为函数参数是等价的
    int main(int argc, char *argv[]);
    int main(int argc, char **argv);
  • 操作二维数组

    void array_print(int a[][5]);
    void array_print(int (*a)[5]);

6.辨析

指针与常量

int const *p = &a; // 指向常量的指针,const修饰(*p), 改向不改值
int * const p = &a; // 指针类型的常量,const修饰p, 改值不改向

字符指针与字符数组

char a[] = "hello"; // 字符数组,字符元素可以修改,数组位置不可变
// 等价于 char a[] = {'h', 'e', 'l', 'l', 'o', '\0'};
char *p = "world"; // 字符指针,指向字符串常量,字符不可修改,指向的位置可以改变

数组名

int a[] = "hello";
printf("%p\n", a+1); // 首元素的下一个元素的地址
printf("%p\n", &a+1); // 下一个数组的地址 // TODO 等于a[5]的地址吗?

指针数组与数组指针

// 指针数组用于传递命令行参数,数组保存不等长字符串
// 数组指针用于参数传递二维数组,更方面体现其行数组等长的结构
char a[2][3] = {"abc", "def"};
char (*p)[3] = a;
char **pp = &a[0][0];
int f1(char **p1); // pointer to pointer to char
int f2(char *p2[]); // array of pointer to char
int f3(char (*p3)[3]); // pointer to array[3] of char
int f4(char p4[][3]); // array of array[3] of char

指针数组与二维数组

int a[5][10]; // 二维数组,分配50个int类型长度空间
int *p[10]; // 指针数组,仅分配了10个指针,且没有初始化,但是指针所指向的长度无需相同
// 后续都可以通过 a[i][j]的形式访问,本质上还是指针运算
int (*p1)[10] = a; // 数组指针,指向int array[10]的数组 a[0]
// 数组指针作为参数传递二维数组需要显式说明长度 int f(int (*p)[10]);

7.参考

  • 《C程序设计语言》-2th
    • 5.指针和数组
    • 6.4 指向结构的指针
      • 6.2 结构与函数
      • 6.3 结构数组
      • 6.5 自引用结构
  • 《C和指针》
    • 6.指针
    • 8.数组
    • 9.字符串、字符和字节
    • 10.结构体和联合
    • 13.高级指针话题
  • 《嵌入式C语言的自我修养》
    • 7.8从变量到指针
    • 7.9指针与数组的暧昧关系
    • 7.10指针与结构体
    • 7.11二级指针
    • 7.12函数指针
posted @   libq8  阅读(38)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 【.NET】调用本地 Deepseek 模型
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
点击右上角即可分享
微信分享提示