?hello,愿意点进来的小伙伴们,你们好呐!? 🐻🐻系列专栏:【C语言基础】 🐲🐲本篇内容:浅学指针基础 🐯🐯作者简介:一名大一即将升大二的三非编程小白
前言
指针是什么? 内存空间怎么管理呢? 切割成内存单元 – 1 byte(字节) – (最小单元) 每一个内存单元都有z自己独有的编号,这个编号就叫做地址(地址也称为指针) 指针其实就是地址,地址就是指针 指针就是内存单元的编号。
指针所占的内存大小
我们一般口中说的指针其实就是指针变量,指针变量本质上就是存放地址的变量,因此指针本质上也是一个地址,如 &a 是取出第一个字节的地址,该地址其实是一个数值。我们可以通过这个地址来找到一个内存单元。
int main() {
int a = 10;
int* pa = &a;
return 0;
}
总结: 1.指针变量存放地址的,地址是唯一标示一块地址空间 2.指针的大小在 32 位平台是 4 个字节,在 64 位平台是 8 个字节 (32 位 为32条地址线)。例如: 该代码运行环境位 X86 (32位),所以该环境下的指针内存大小都为 4 个字节
指针和指针类型
指针也是有类型的,指针类型有什么意义呢?
int main() {
int a = 0x11223344;
char* pc = (char*)&a;
*pc = 0;
return 0;
}
> 结论: 1.指针类型决定了指针在被解引用的时候访问了几个字节 2.如果是 int 的指针,解引用的时候访问了 4 个字节 3.如果是 char 的指针,解引用的时候访问了 1 个字节 推广到所有类型**
例1:
pa 的指针类型为 int* – 该类型能访问 4 个字节的内存空间 pc 的指针类型为 char* – 该类型可以访问 1 个字节的内存空间 pa + 1 指在 pa 后面的另一个元素的地址。 pc + 1 指在 pc 后面的另一个元素的地址。 由上面的例子看出,pa + 1 跳过了 4 个字节, pc + 1跳过了 1 个字节。 可以得出: 由此可见,指针类型决定了指针 ± 1操作的时候跳过多少个字节 决定了指针的步长 因为指针类型 决定了解引用的时候在内存中访问了多少个字节。(访问权限) 然后一定要注意: 指针变量是 4 个字节 和指针变量的在内存中访问的内存空间不同(解引用) 不要将指针变量内存和指针变量的在内存中访问的内存空间搞混。 🐞🐞指针变量的编号是 4 个字节,但是他在内存中可以访问多少个字节的内存空间,要看指针类型🐞🐞
例2:
int main() {
int a = 0;
int* pi = &a;
float* pf = &a;
return 0;
}
虽然说,两个指针类型的访问内存空间大小相同,但是不可以将两个该指针类型的指针变量通用,因为它们在地址中存放的值是不同类型的。
野指针
🐞🐞野指针指的是一个指针指向的位置是未知的,不确定的。
例1:没有初始化
int main() {
int* p;
*p = 10;
return 0;
}
因为指针变量 p 没有初始化值,所以在内存中该指针指向一个随机值,这种情况下 *p 就是非法访问内存。 🐝🐝p 就称为野指针。
例2:数组下标越界
int main() {
int arr[10] = { 0 };
int* p = arr;
int i = 0;
for (i = 0; i <= 10; i++){
*p = i;
p++;
}
return 0;
}
arr是首元素地址,p 相当于 arr[0] , 在for循环中,指针 p 已经指向arr数组外的一个地址,指向访问超过了数组的访问,这时候 p 就是野指针
例3:函数栈帧的销毁
函数栈帧的销毁的销毁属于比较难理解的一个原因,在这里我举个简单的例子:
int* test() {
int a = 10;
return &a;
}
int main() {
int* p = test();
return 0;
}
🐛🐛test 将 a 的地址返回过来给 p , 因为 a 是局部变量,当 p 接收到 a 的地址后, a 这个局部变量就会销毁 , 所以 p 访问不到 a 的地址, 🐛🐛所以 p 就是野指针.
所以在知道了野指针的形成条件后,我们以后在写代码的过程中要注意一些事项,养成好习惯 1.
🐠🐠 我们要明确的初始化变量, 🐠🐠就算不知道初始化多少也要赋值为 NULL
int a = 10;
int* p = &a;
int* p2 = NULL;
*p2 = 100;
if (p2 != NULL) {
printf("%d\n", *p2);
}
野指针的防范
如何避免野指针 1.指针初始化 2.小心指针越界 3.指针指向空间释放即设置 NULL 4.避免返回局部变量 5.指针使用前检查有效性
指针运算
减法
指针可以进行相减运算(指针 - 指针)
int main() {
int arr[10] = { 0 };
printf("%d\n", arr[9] - arr[0]);
printf("%d\n", arr[0] - arr[9]);
int arr2[5] = 0;
char ch[2] = 0;
printf("%d\n", arr2[4] - ch[1]);
return 0;
}
指针减指针得到的绝对值是指针和指针之间元素的个数 不是所有指针都可以相减 要指向同一个空间的指针相减才有意义,如:printf("%d\n", arr2[4] - ch[1]);//无意义,会报错 指针相加也无意义
知道了指针可以相减后,我们可以用一种新的方法求字符串长度
int my_strlen(char* ch) {
char* start = ch;
while (*ch != '\0') {
ch++;
}
return ch - start;
}
int main() {
int len = my_strlen("abcdef");
printf("%d\n", len);
return 0;
}
加法
指针的加法只能自增,如:
int main() {
int arr[5];
int* vp;
for (vp = &arr[0]; vp < &arr[5];) {
*vp++ = 0;
}
return 0;
}
指针的自增在上文已经提过
指针关系运算
int main() {
int arr[5];
int* vp;
for (vp = &arr[5]; vp > &arr[0];) {
*--vp = 0;
}
for (vp = &arr[5 - 1]; vp >= &arr[0]; vp--) {
*vp = 0;
}
return 0;
}
指针的大小比较 vp >= &arr[0] ,比较的是指针的大小(地址在内存中的先后顺序), 标准规定: 允许数组元素与指向数组最后一个元素的后面的指针相比,但是不允许与指向数组第一个元素前面的指针相比
指针和数组
数组是一组相同类型元素的集合 指针变量是一个变量,存放的是地址
int main() {
int arr[10] = { 0 };
int* p = arr;
for (int i = 0; i < 10; i++) {
printf("%d\n" , *(p + i));
}
for (int i = 0; i < 10; i++) {
printf("%p ----- %p\n", &arr[i] ,(p + i));
}
return 0;
}
p 指向 arr 的首元素,p + i 就可以访问arr数组里面的元素
|