1.取地址运算:&运算符取得变量的地址
1.1.变量的地址
- 计算机的内存是以字节为单位的连续存储空间,每个字节都有一个编号,可以根据一个内存单元的编号准确的找到这个内存单元,这些编号称为地址,内存中地址的编号都是连续的。
1.2.指针
- 通常把变量的地址称为该变量的指针,即指针是一个地址,是一个常量。
1.3.取地址符
- 因为指针变量p中储存的是地址,因此如果p指向变量a,需要在a前面加一个取地址符&(获得变量的地址,它的操作数必须是变量),即p=&a;,含义是指针p指向a的存储单元/地址,不能将一个整型变量赋值给一个指针变量,指针变量指向的是地址。
- 获得变量的地址,它的操作数必须是变量。
- int i; printf(“%x”,&i);
- 地址的大小是否与int相同取决于编译器
- int i; printf(“%p”,&i);
- 取地址用%p。
2.指针:指针变量就是记录地址的变量
2.1.指针变量
- 概念:C语言中允许用变量存放地址。如果一个变量专门用来存储其他变量的地址,那么这个变量就称为指针变量。严格的说,一个指针是一个地址,为一个常量,而一个指针变量的内容可以根据需要存放某个变量的地址,是可以变化的,是一个变量。
- 普通变量的值是实际的值
- 指针变量的值是具有实际值的变量的地址
- 定义:类型说明符 *指针变量名;
- 初始化:int a, *p=&a; 或 int a; int *p; p=&a; 指针p指向a的存储单元/地址。
- *在不同位置有不同含义:可作为算术运算符乘号、定义指针,如果出现在表达式中表示指针变量所指向的变量,即取内容运算符,取的是指针变量所指存储单元的值。
3.指针的使用
3.1.指针的使用
- 返回多个结果

- 函数返回运算的状态,结果通过指针返回

4.指针与数组
4.1.指针和一维数组
- C语言规定,数组名代表数组的起始地址,注意是地址!!!,等价于&a[0],a+i代表数组元素a[i]的地址,等价于&a[i]。例:
- int a[5], *p=a; 注意:a前面没有&符号,因为数组名a代表数组的起始地址。
- int a[5], *p=&a[0]; 注意:a[0]前有&符号,因为a[0]代表元素不代表地址。
4.2.通过指针引用数组元素
- 数组a中任一元素a[i]的地址可以用一下几种方式表示:&a[i] a+i p+i
- 数组a中任一元素a[i]可以用一下几种方式表示: a[i] *(a+i) *(p+i) p[i]
- 引用步骤:
定义指针和数组,例:int *p, a[10];
指针指向数组,例:p=a;或p=&a[0];,即指向数组首地址
通过指针引用数组元素
- 注意:数组名是一个常量,不允许自增自减,但数组名可以加上或减去一个整数值,并把结果赋给指针变量。
4.3.指针和二维数组
- 对于二维数组,a[i]代表第i行的首地址,则数组元素a[i][j]的地址可以由a[i]+j得到。
- 对二维数组int a[i][j]:
- a表示二维数组的首地址,即第0行的首地址。
- a+i表示第i行的首地址。
- *(a+i) a[i] &a[i][0],表示第i行第0列元素的地址。
- 下标表示:a[i]+j 指针表示:*(a+i)+j,表示第i行第j列元素的地址。
- 下标表示:*(a[i]+j) 指针表示: *(*(a+i)+j) a[i][j],表示第i行第j列的元素。
4.4.字符指针与字符数组
- 字符串实际上就是每个元素都是字符的一维数组,最后一个元素用'\0'作为结束标志,通常用*ps是否等于'\0'判断字符串的结束位置。
- 字符指针:char *ps ="We are students" 用一个字符指针指向一个字符串的首地址,如果要改变指针ps所代表的字符串,通常直接改变指针的值,让其指向新的字符串。
- 字符数组:char str[]="We are students" 用一个字符数组存放一个字符串,如果要改变数组str所代表的字符串只能改变数组元素的内容。
|
区别 |
字符指针 |
字符数组 |
|
存储内容 |
字符串的首地址 |
字符串本身(数组的每个元素存放一个字符) |
|
赋值方式 |
可直接赋值,例: char *ps; ps="China"; |
可在定义时初始化,但不能用赋值语句整体 赋值,要逐字循环赋值 |
|
类型 |
变量,可改变指向 |
字符数组名是常量,始终代表字符数组首地址 |
- 对于字符数组的引用可以逐个字符引用也可以整体引用,输出时%c用*ps单个字符输出,%s用ps一次性输出全部元素,其他数组是不能一次性全部输出的。

5.指针与const
5.1.指针是const
表示一旦得到了某个变量的地址,不能再指向其他变量
int*const q=&i;//q指针是const,q的值即i的地址不能被改变,q指向i的地址的事实不能被改变。
*q= 26; // OK q所指的存储空间的值是可以改变的
q++ // ERROR q的地址是不能被改变的
判断哪个被const了的标志是const在*的前面还是后面,const在*前面,*p被const,*p所指的存储空间的值不能被修改,const在*后面,p被const,p所指存储空间地址不能被修改。
- 指针本身是常量(const影响p)
int *const p = &a; // 指针本身是常量
- p 不能指向别的地址,但是可以修改所指向的值:
*p = 20; // ✅ 可以修改*p的值
p = &b; // ❌ 错误,不能修改p的指向
5.2.所指是const
- 表示不能通过这个指针去修改那个变量(并不能使得那个变量成为const)
- const int *p = &i;
- *p=26; // ERROR! (*p)是const, p所指存储空间的值不能变
- i=26; //OK
- p= &j; //OK p所指存储空间地址能改变
- 指向常量的指针(const影响int)
const int *p = &a; // 指向常量的指针
- p 可以指向不同的地址,但是不能修改所指向的值:
*p = 20; // ❌ 错误,不能修改*p的值
p = &b; // ✅ 可以改变指针的指向
5.3.const数组
- const int a[]={,2,3,4,5,6,};
- 数组变量已经是const的指针了,这里的const表明数组的每个单元都是constint所以必须通过初始化进行赋值
5.4.保护数组值
- 因为把数组传入函数时传递的是地址,所以那个函数内部可以修改数组的值
- 为了保护数组不被函数破坏,可以设置参数为const
- int sum(const int a[], int length);
5.5.const int *const p
- 指向常量的常量指针
const int *const p = &a;
- 既不能修改 p 的指向,也不能修改 *p 的值。
6.指针与函数:指针和函数关系的三个方面
6.1.指针作为函数参数
- 指针可以是普通变量的地址、数组名、字符串首地址等。
- 指针作为函数参数是将一个变量的地址传送到另一个函数中。
- 指针作为函数参数,将实参传给形参,是地址传递而不再是值传递,实参和形参共享同一段内存,形参的改变会影响实参。
- 例如:int f(int *p); 在被调用的时候得到某个变量的地址
- int i=0;f(&i); 在函数里面可以通过这个指针访问外面的这个i
- 实参用数组名或者指针时程序执行效率差不多,但形参用指针(把数组名改成指针形式就行)有时候可以提高程序执行效率。
6.1.1数组变量
- 无论传递数组首地址的函数参数是数组名还是指针,其实质都是指针,在函数被调用时,该指针通过参数传递指向数组。
- 注意:函数参数表中的数组,实际上就是指针。对其做sizeof(a)==sizeof(int *a),但是可以用数组的运算符[]进行运算。可以注意到函数调用时,数组名作为实参,形参的数组[]内的数字实际上无效,因为此时的数组实际上就是指针
- 数组变量是特殊的指针:数组变量本身表达地址,即用数组名,所以:int a[10];int*p=a; 中无需用&取地址但是数组的单元表达的是变量,需要用&取地址·a == &a[0],数组名a实际上是取的数组起始地址&a[0]
- []运算符可以对数组做,也可以对指针做:p[0]<==> a[0]
- *运算符可以对指针做,也可以对数组做:*a=25;
- 数组变量是const的指针,所以不能被赋值:int a[] <==> int * const a=....,是常量指针
6.2.指针型函数
- 一个函数的返回值可以是各种类型,如int、float、double、char型数据类型,也可以返回一个指针类型数据。
- 格式:类型说明符 *函数名(形参表)
- 说明:函数名前加一个*表示这是一个指针型函数(函数值是指针),调用它后能得到一个与所指类型说明符相对应的指针(地址)。函数名两侧运算符分别是*与(),因为()比*优先级更高,先与函数名结合说明”函数名()“是一个函数,然后再与*结合,说明此函数的值为指针。
- 调用形式:指针变量=函数名(实参);
- 示例:
int* getMax(int a, int b) {
if (a > b) {
return &a; // 返回 a 的地址
} else {
return &b; // 返回 b 的地址
}
}
int x = 5, y = 10;
int *maxPtr = getMax(x, y);
printf("最大值是: %d\n", *maxPtr); // 解引用指针来获取值
这里,getMax 返回一个指向较大值的指针,maxPtr 保存返回的指针,最后通过 *maxPtr 获取值。
6.3.指向函数的指针(函数指针)
- C语言中,函数是不能作为参数在函数间进行传递的,但实际应用中可能需要把一个函数作为参数传递给另一个函数,解决的方式是使用指向函数的指针作为参数。这种传递不是传递普通变量的地址,而是传递函数的入口地址。
- 格式:类型说明符 (*指针变量名)();
- 说明:类型说明符表示函数返回值的类型,最后的()表示指针变量所指的是一个函数。
- 示例1:
double (*fp)(); 说明fp是指向函数的指针 fp=fun; 将函数fun在内存中的起始地址赋值给fp,从而使fp指向fun(函数的标识符本身就是地址,可以直接赋值给函数指针) y=(*fp)(3.5); 调用函数的形式为:(*指针变量名)(实参表)
示例2:
int add(int a, int b) {
return a + b;
}
int subtract(int a, int b) {
return a - b;
}
int (*operation)(int, int); // 声明一个函数指针,指向一个返回 int,接受两个 int 参数的函数
operation = add; // 将 add 函数的地址赋给函数指针
int result = operation(5, 3); // 通过函数指针调用 add 函数,结果为 8
printf("结果: %d\n", result);
operation = subtract; // 将函数指针指向 subtract 函数
result = operation(5, 3); // 通过函数指针调用 subtract 函数,结果为 2
printf("结果: %d\n", result);
函数指针使得你可以通过动态指定函数来实现多态性或回调机制,比如在事件驱动编程中经常用到。

7.动态内存分配
7.1.malloc
- #include <stdlib.h>
- void* malloc(size t size);向malloc申请的空间的大小是以字节为单位的返回的结果是void*,需要类型转换为自己需要的类型
- (int*)malloc(n*sizeof(int))
7.2.free()
- 把申请得来的空间还给“系统"
- 申请过的空间,最终都应该要还
- 只能还申请来的空间的首地址

7.3.常见问题
- 申请了没free->长时间运行内存逐渐下降
- 新手:忘了
- 老手:找不到合适的free的时机
- free过了再free
- 地址变过了,直接去free
- 记住:写malloc就要写对应的free
8.指向指针的指针和指针数组
8.1.指向指针的指针
- 指针是一种变量,也有地址。可以用另一个指针变量去存储它的地址,该指针就称为指向指针的指针,也叫多级指针。
- 二级指针定义式: 类型名 **指针变量名
8.2.指针数组
- 指针数组是一个数组,数组中的每一个元素都是指针变量。
- 定义式:类型说明符 *数组名[正整型常量表达式1]…[正整型常量表达式n];
- 例如:int *p[3], *q[3][4]; p、q都是指针数组,数组中的每个元素都是指向整型的指针变量。
- int *p[3], q[3][4];
- p[0]=q[0];p[1]=q[1];p[2]=q[2];,则p[0]、p[1]、p[2]分别指向q数组每行的首地址。
- 指针数组比使用字符数组处理字符串更方便灵活,且节省存储空间。
- 例如:char *ps[3]={"we","are","students"}; 指针数组ps的每个元素中存放一个字符串的首地址。
8.3.行指针
- 定义式:类型说明符 (*指针变量名)[长度];
- 注意:括号不能少,否则就是指针数组了。
- 例如:int (*p)[4], q[3][4]; p=q; 行指针长度要与二维数组的列数相对应
- 行指针(*p)[4]:
-
(*p)[0]
(*p)[1]
(*p)[2]
(*p)[3]
- 由于p是指向一维数组的指针变量,因此p+i指向下一个一维数组,等价于a+i,即a[i],是地址。*(p+i)+j是a[i][j]的地址,*(*(p+i)+j)是a[i][j]的值。
9.void* 通用指针类型
9.1.概念
- 在 C 语言 和 C++ 语言 中,void * 代表通用指针类型(void 指针),它可以指向任何数据类型的地址。
- 由于 void * 没有特定的类型,所以它无法直接解引用(访问数据),但可以转换成其他具体类型的指针再使用。
9.2.特点
- void * 可以指向任何类型的数据,不受数据类型限制。
- 不能直接解引用:由于 void * 没有具体的类型,不能直接使用 *ptr 访问数据,必须先转换成具体类型的指针。
- 常用于动态内存分配:malloc() 和 calloc() 返回 void *,需要强制转换成具体类型。
9.3.示例
动态分配内存
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
#include <stdio.h> #include <stdlib.h> int main() { void *ptr = malloc(sizeof(int)); // 分配 4 字节内存 if (ptr == NULL) { printf("内存分配失败\n"); return -1; } *(int *)ptr = 10; // 先转换成 int* 再解引用 printf("值: %d\n", *(int *)ptr); free(ptr); // 释放内存 return 0; } |
注意:malloc() 返回 void *,所以 ptr 需要转换成 int * 才能使用 *ptr 访问数据。
作为通用参数传递
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
#include <stdio.h> void printValue(void *ptr, char type) { if (type == 'i') { printf("整数: %d\n", *(int *)ptr); } else if (type == 'f') { printf("浮点数: %.2f\n", *(float *)ptr); } } int main() { int a = 10; float b = 3.14; printValue(&a, 'i'); // 传递 int 指针 printValue(&b, 'f'); // 传递 float 指针 return 0; } |
void * 在 FreeRTOS 任务参数
|
1 2 3 4 5 6 7 |
void vTaskFunction(void *pvParameters) { int param = *(int *)pvParameters; // 转换成 int* for (;;) { printf("任务参数: %d\n", param); vTaskDelay(pdMS_TO_TICKS(1000)); } } |
注意:pvParameters 允许传递任意类型的数据,但必须在函数内转换成正确的类型才能使用。
9.4.限制
不能直接解引用
|
1 2 |
void *ptr; *ptr = 10; // ❌ 错误,void * 不能直接解引用 |
需要先转换成具体类型:
|
1 |
*(int *)ptr = 10; // ✅ 先转换成 int* |
不能进行指针运算
|
1 2 |
void *ptr; ptr++; // ❌ 错误,void * 不知道步长 |
需要转换成具体类型:
|
1 2 |
int *intPtr = (int *)ptr; intPtr++; // ✅ 现在可以进行指针运算 |

1626

被折叠的 条评论
为什么被折叠?



