1. 内存与指针
内存是给程序运行而开辟的空间。如果不考虑动态内存,就有创建变量和数组两种方式。
内存可以视分解为大量内存单元,一个内存单元大小为 8 个比特位,每个内存单元都有一个编号,这个编号就是指针。(int *pa=&a;
a是房间,&a是房间号,*pa 是记录房间号的纸条 )
只有嵌入式的地址是实际物理空间,其余是假想的。
2. 取地址操作符
& 用于取出变量的地址。
#include <stdio.h>
int main()
{
int a = 10;
&a; // 这里就是直接取出地址,是指针
printf(“%p\n”, &a);
return 0;
}
# # 3. 指针变量
用于存放地址的一种变量。
例:`int *pa = &a;` 就是把 a 的地址存放到 pa 中,这也是用 `&a` 给 pa 初始化(`&a` 是读出的地址)。
pa就是指针变量
全局变量(`int *pa`):
```c
int main()
{
int a = 0;
int *pa = NULL;
return 0;
}
此时相当于 pa 没有存放地址,可以之后再存。
但是,如果是局部变量,&a 在一个小函数内,此时 int *pa 是一个未知数,也叫做野指针,不可以强行使用。
4. 理解指针类型
int a = 10;
int *pa = &a;
这里 * 说明 pa 对应指针变量,但 int 对应的是 pa 的类型,总说是一个整型的指针。
5. 解引用操作符 *(既有标记作用,也有访问作用)
#include <stdio.h>
int main()
{
int a = 10;
int *pa = &a;
*pa = 0;
return 0;
}
*即提示pa是指针变量,又有去访问的意思
这里 *pa 是通过 pa 中存放的地址,找到 a,将 a 赋值为 0(找到门牌号,然后找客人)。
6. 指针的大小
例:printf("%zd\n", sizeof(int *))
1 字节 = 8 比特。
- 32 位平台下地址是 32 比特,4 字节
- 64 位平台下地址是 64 比特,8 字节
指针变量的大小只与平台有关。
*char
*int
*short 它们的指针变量大小都是一样的,差别只是±1的时候它会跨越本元素内部的多少个byte,从而到达下一个元素,最终达到遍历打印的目的。
指针解引用(找到 pi 里存的地址,找到对应的内存空间,修改里面的数值):
``公式:指针+n=地址+n*
int *pi = &a;
*pi = 0;
char *pi = (char *)&a;
*pi = 0;
pi = 001EFA94
pi+1 = 001EFA98
pc = 001EFA94
pc+1 = 001EFA95
通俗理解:
int* pi+1 :跨过当前int内部剩下3个字节,直接落到下一个完整int元素的起始地址。
char* pc+1 :只前进1个字节,到相邻的字节。
这就是为数组遍历设计:
数组是一片连续同类型元素。用对应类型指针, p+1 自动跳到下一个数组元素,不用手动算字节。
- 想遍历int数组 → 用 int*
- 想遍历char数组(字符串)→ 用 char*
- 想遍历short数组 → 用 short*
前者可以把 4 个字节改为 0,但后者只可以改一个,这是不同类型对解引用操作符的作用权限。
总结一下:
- 指针变量的大小只与平台有关
- 指针对解引用操作符的权限与指针类型有关
## 7. 指针加减整数
指针类型决定了指针向前或向后的距离,但指针必须跨越与自己同类型的距离。
- `char * + 1` 向前走 1 个byte
- `int * + 1` 向前走 4 个byte
## 8. void* 指针
专门用于接收不同类型的地址。
```c
void *pa = 10;
void *pc = 10;
pc 和 pa 可以接收不同类型的地址。
9. 指针加减指针
- 指针加减指针 = 整数
- 指针加减整数 = 指针
F2897 - 2 = F2895
指针加减指针的结果绝对值是指针间元素的个数(必须在同一个内存中)。
#include <stdio.h>
int main()
{
int arr[5] = {1, 2, 3, 4, 5};
printf("%d\n", &arr[4] - &arr[2]);
return 0;
}
这里 &arr[4] - &arr[2] 便是指针加减指针。
10. 指针的比较
本质:一个数值。用数组举例。
先明确一个点:p 这个存的是地址,*p 这个既是指针符号,也是解引用,取数组里的数。
#include <stdio.h>
int main()
{
int arr[5] = {1, 2, 3, 4, 5};
int *p = &arr;
int sz = sizeof(arr) / sizeof(arr[0]);
while (p <= &arr[sz]) // 这个地方就是用到了指针的比较
{
printf("%d\n", *p);
p++;
}
return 0;
}
11. 传值调用和传址调用
前者相当于给复印件,不给原稿;但后者相当于直接给原稿。前者实参不改变形参,改不了原值;但后者可以改实参。
传值调用:(适用于之读取数据而不修改原值,如果要接收那个原值的话,就要弄一个新的变量去接收)
void change(int a)
{
a=100;
}
int main()
{ int num=10;
change(num);
printf("%d",num);
return 0;
}```
这种情况无返回值。
结果还是10
传址调用:(适用于需要修改外部变量)
```c
#include <stdio.h>
void change(int a)
{
*a=100;
}
int main()
{
int num=10;
change(&num);
printf("%d",num);
}
这样才打开房门,把数值传过去。
一个是把门牌改变,里面数值不变;一个是打开房门,改数值。后者才可以建立函数与主函数的关系。
12. 二级指针
用来存放一级指针的指针变量。**ppa 用来存放 pa 的指针变量,访问 pa。
13.整形提升
整型提升(课程顺带内容)
CPU运算单元ALU,默认以int长度来做计算。
只要表达式里面, char / short 这类小于int长度的整型,运算前自动转成int,这个转换叫整型提升。
规则
- 有符号char/short:按照符号位补齐高位
- 无符号char/short:高位补0
示例代码
c
char a = 10;
char b = 120;
char c = a + b;
printf(“%d\n”,c); // 输出 -126
分析:
- char是1字节,范围 signed char:-128 ~ 127
- a+b=130,超出char上限127,发生溢出
- 130 二进制: 10000010 ,作为signed char,最高位是符号位 → 代表-126
一句话总结:char、short做运算,会先提升成int算;算完再截断放回char/short变量。
其实就是把a和b的补码相加,得到c的补码,最高位不动,0000010,之后取反,1111101,之后+1,1111110这里有7为数是参与计算的,最高位只用于符号判断,和计算无关。
14六、常见易错点汇总
- ❌ 误区:指针变量大小由指向类型决定
✅ 纠正:指针大小只看32/64位系统,全部指针一样大。指针类型只控制解引用访问字节数、指针+1的偏移步长。 - ❌ 误区: int* p , *p 是指针变量名字
✅ 纠正:变量名是p; int * 是类型, * 只是标记这是指针。 - ❌ 误区: pi+1 是地址+1字节
✅ 纠正: pi+1 = 地址 + 1 * sizeof(int) ,一次跳过一整个int。 - ❌ 随便把int地址直接赋值给char*不会出事
✅ 纠正:语法允许强制转换,但是解引用的时候只会读写1字节,容易出现数据异常。 - 只有数组内的两个指针相减才有意义,不同变量的指针相减无意义。
15 const 修饰指针(有三种写法)
1.const intp 这里是指p不可以修改,但是可以换p。(相当于可以换房间,但是不可以专门去换房间里的床)
- int *p const 这里不可以换p,但是可以换p
(相当于可以换房间里的被子,但是不可以换房间)
3.const int p const 这里既不可以换p,也不可以换p
例子:const int*p=&a; 这里已经固定好了
如果 *p=100; 这里就会报错
但是 p=&c;这里没问题(换了一个地址)
const离谁近,谁就不可以改
16 野指针
指针指向的内存是不处于当前可用程序,是无法访问的
造成这个的三个成因
1.没有给指针初始化,使指针存随机的垃圾地址,
自然找不到对应的内存了
2.指针越界:超出数组/变量的内存范围
3.返回局部变量的地址:函数结束,局部变量栈镇会自动销毁。除非加上一个static,这样就可以把它给保留了
17 assert断言
头文件:#include<assert.h>
作用:括号内表达式为假时,程序直接跳出一个界面报错(这个就是用来找错误的)
#include<assert.h>
int main()
{
int a=10;
assert(a==15); //在这一步就直接报错了
return 0;
}
352



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



