指针
本文最后更新于136 天前,其中的信息可能已经过时,如有错误请发送邮件至 2641805259@qq.com

1. 指针的本质

指针是存储内存地址的变量

int a = 1;

float b = 0.1;

指针 p = 地址

普通变量存储数据本身;指针变量存储的是某个数据在内存中的位置。

内存中每个字节都有唯一的地址,如果内存中有个字节,其字节地址可编号为

示例:

int a = 10;
int *p = &a;  // p 中存放 a 的地址

2. 指针的声明与初始化

2.1 声明

类型 *指针名;

示例:

int *p;      // 指向 int 的指针
double *q;   // 指向 double 的指针

2.2 初始化

int a = 5;
int *p = &a;

&:表示取变量的地址

未初始化的指针属于“野指针”,危险且不可使用。


3. 指针的解引用(dereference)

解引用指通过指针访问指向的变量。

int a = 10;
int *p = &a;

*p == a

printf("%d\n", *p); // 输出 10
*p = 20;            // 修改 a 的值为 20

4. 指针与数组

4.1 数组名是不可修改的指针常量(指向的内存不变)

int arr[3] = {1,2,3};
int *p = arr;     // 等价于 &arr[0]

printf("%d\n", *(p+1));  // 输出 2   移动了 1 * sizeof(int) = 4

指针 + n 实际移动 n × sizeof(类型) 字节位置。

4.2 指针运算

++优先级大于*

* p++取当前指针 p 指向的元素的值。表达式结束后,p 自增 1,指向下一个元素。(对当前元素解引用,然后指针后移。)
*(p++)取当前指针 p 指向的元素的值。表达式结束后,p 自增 1,指向下一个元素。(对当前元素解引用,然后指针后移。)
(*p)++返回:原来的 *p, 副作用:*p = *p + 1p 不移动
++*p副作用:*p = *p + 1 ,返回:新的 *pp 不移动
++(*p)同上
*++Pp 先递增,指向下一个元素。解引用新位置的值。
*(++p)同上

5. 指针与函数

5.1 通过指针实现“传引用”

用于函数内部修改外部变量。

void set(int *x) {
    *x = 100;
}

int main() {
    int a = 5;
    set(&a);
}

5.2 指针作为数组参数

避免复制大数组。

void print(int *arr, int n) {
    for(int i = 0; i < n; i++) {
        printf("%d ", arr[i]);
    }
}

*(arr + i) == arr[i]


6. 多级指针

表示指向指针的指针。

int a = 10;
int *p = &a;
int **pp = &p;
(int *) *pp = &p;
*pp = p
**pp = *(*pp) = *p = a
printf("%d\n", **pp); // 输出 10

典型用例:

  • 管理二维动态数组
  • 修改函数内部的指针(如 malloc 封装)

7. 动态内存与指针

使用 malloc / calloc / realloc 动态申请内存后通过指针访问。

int *p = malloc(sizeof(int) * 5);
p[0] = 1;
free(p);
p = malloc(sizeof(int) * 10);

注意事项:

  1. 申请后必须检查是否为 NULL
  2. 使用完毕必须 free
  3. free 后将指针置为 NULL,避免悬挂指针

8. 指针的赋值与类型转换

不同类型指针不可直接赋值

C 语言中,指针类型必须与所指向对象的类型一致;不同类型的指针不能在未转换的情况下相互赋值。

错误示例:

int *p;
char *q;

q = p;     // 类型不兼容,编译器将给出警告或错误

原因: 不同指针类型解读内存的方式不同(访问粒度、对齐要求、步进方式都不同)。直接赋值会导致行为错误。


void* 指针

1 含义

void * 表示“指向未知类型”的指针,即通用指针类型。 特点:

  • 不知道指向的对象的类型
  • 不支持解引用 (*q 非法)
  • 不支持指针算术运算 (q+1 非法)
  • 可以接收“任意类型指针”的赋值
  • 可以赋值给“任意类型指针”,但需要显式转换

示例:

int *p = &i;
void *q = p;    // 正确,int* -> void*

指针类型转换

1 显式转换

指针可以通过显式类型转换改变“访问内存的视角”。

示例:

int  i = 0x12345678;
int *p = &i;
void *q = (void *)p;     // p 给 q,类型变为 void*

此时:

  • p 按 int 视角解读内存(通常 4 字节)
  • q 不知道类型,无法直接解读
  • 若将 q 转换为 char*,则内存变成按字节解读

示例:

char *c = (char *)q;    // 现在按 1 字节读内存

printf("%02x\n", c[0]); // i 的低字节

2 “通过 q 解读内存的视角变了”

这句的技术意义是:

指针类型决定了解释内存的单位与方式

  • int* → 每次解引用读取 sizeof(int) 字节
  • char* → 每次解引用读取 1 字节
  • void* → 无法解引用,必须先转换类型
  • double* → 每次解引用读取 sizeof(double) 字节

因此,改变指针类型即改变查看内存的方式

这是 C 语言中非常重要且需要谨慎使用的机制。


4. 示例:同一地址,不同视角

int i = 0x11223344;

int *p = &i;         // 按4字节整数读取
char *c = (char*)&i; // 按字节读取

内存(假设小端机):

地址内容
&i+044
&i+133
&i+222
&i+311

访问差异:

printf("%x\n", *p); // 11223344
printf("%x\n", c[0]); // 44
printf("%x\n", c[1]); // 33

5. 合法但危险的行为

C 的类型转换功能强大,但必须注意:

  • 若按错误类型访问内存,可能破坏对齐要求
  • 可能导致未定义行为(UB)
  • 用于解析二进制协议、内存分析、字节序转换时需要精确控制

8. 函数指针

用于指向函数入口地址。

声明方式:

返回类型 (*指针名)(参数类型列表)

示例:

int add(int a, int b) { return a+b; }
int (*fp)(int,int) = add;

int r = fp(3,4);  // 调用 add

应用场景:

  • 回调机制
  • 状态机
  • 函数表

9. 指针常见错误与风险

  1. 野指针(未初始化)
  2. 悬挂指针(free 后继续使用)
  3. 越界访问
  4. 指针类型不匹配
  5. 重复释放导致崩溃

示例(错误):

int *p;
*p = 10;   // 未初始化直接使用

10. 指针与 const

10.1 指向常量的指针

const int *p;  // 不允许 *p 被修改

10.2 指针本身为常量

int * const p; // p 不可被重新赋地址

10.3 都为常量

const int * const p;

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇