详细解读指针(一)——访问数据的地址
文章目录
编址指针变量解引用操作符*指针类型
指针的运算指针 +(-)整数指针-指针指针比大小
const修饰指针野指针野指针的可能成因规避野指针
assert断言传值调用和传址调用传值调用传址调用
数组和指针数组名的理解使用指针访问数组
在计算机中,每一个写入的数据都要在内存中存储,拥有一小块空间。每块空间都有一个地址,这个地址一个数字来表示。根据地址我们可以对其进行访问,同时地址也叫做指针。
为了方便数据存储,内存被划分成一个个小的内存单元,每个内存单元是一个字节(8个比特位)
1字节(byte)=8比特位(bit)1kb=1024byte1mb=1024kb1gb=1024mb1tb=1024gb
其中一个比特位可以存放一个二进制的1或者0
编址
在32为(x86)环境下,地址总线有32根(x64环境中64根),每根线都可以由0或1表示,所以地址就可以有2^32种表示方式。 也就是说,x86环境下,地址是由一个32位的二进制序列表示的,所以无论是什么类型数据的地址,它都占32个比特位的空间,也就是4个字节(x64环境中则是64个比特位,8个字节)。
通过地址总线,内存中的数据就可以被传递到CPU中的寄存器里 数据总线可以传输数据 控制总线控制数据是写入还是读取
指针变量
#include
int main()
{
int i = 10;
int* p = &i;
printf("%p\n", p);
return 0;
}
结果打印了一个一个8位16进制数字,也就是i的地址
int* 是变量p的类型,其中int表示其指向内容的类型是int,*表示它是指针变量p中存储了i的地址,即p指向i打印地址时占位符要用%p
解引用操作符*
#include
int main()
{
int i = 10;
int* p = &i;
*p = 20;
printf("%d\n", i);
return 0;
}
打印出来的结果是20
这说明p解引用后的*p可以用来访问该地址指向的内容
(注意:p是地址,*p是地址指向的内容)
指针类型
指针类型也分为char*,int*等等,虽然它们的长度都是4个字节,但是各自也有不同意义
#include
int main()
{
int i = 0x12345678;
int* p = &i;
char* pc = &i;
*pc = 0;
return 0;
}
当代码运行完第一句时,以及把0x12345678的值放到了i里面
但是当用char*类型的指针去访问i时,只能访问到地址最低的两位
也就是说,指针变量的类型决定了指针的“步长”,即指针能够访问到空间的大小
图中char类型的指针只能访问到一个字节的空间,而int类型可以访问4个字节
指针的运算
指针 +(-)整数
#include
int main()
{
int arr[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
int* p = arr;//数组名是首个元素的地址
printf("%p\n", p);
p++;
printf("%p\n", p);
printf("%d\n", *p);
return 0;
}
可以看到,地址+1就相当于地址+“步长”,+n就是加上n倍的“步长”
这也就刚好保证了p++能访问到内存中下一个元素
(注意以上结论实现的前提是数组的元素随着下标是从低到高存储的)
指针-指针
#include
int main()
{
int arr[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
int* p = arr;
printf("%p\n", p);
int*s = &arr[9];
printf("%p\n", s);
printf("%d\n", s - p);
return 0;
}
指针-指针的绝对值就是中间元素的个数 + 1(如果低地址-高地址就是负数)
注意前提是两个指针必须指向同一块区域
指针比大小
#include
int main()
{
int arr[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
int* p = arr;
while(p <= &arr[9])
{
printf("%d ", *p);
p++;
}
return 0;
}
指针之间可以比较大小,用的时候要注意p++和p <= arr[9]是因为数组元素地址随着下标是递增的
const修饰指针
const修饰变量可以使该变量无法被修改,但是利用指针访问依然可以间接修改。
但是如果用const修饰指针,那么就有下面几种情况:const放在*前面或者后面,或者两边都放
const放在*前(与放在int的前后无关),那么该指针指向的内容(*p)不能改变,但是指针的指向(p)可以改变
#include
int main()
{
int i = 10;
int j = 20;
const int* p = &i;//const也可以放在int后面
*p = 0;//这条代码会报错
return 0;
}
const放在*后,那么该指针的指向(p)不能改变,但是可以通过该指针改变它指向的内容(*p)
#include
int main()
{
int i = 10;
int j = 20;
int* const p = &i;
p = &j;//这条代码会报错
return 0;
}
如果两边都放就都不能改变
野指针
野指针的可能成因
未初始化指针
int* p;
指针指向的内容销毁(空间释放)
int* test()
{
int n = 0;
return &n;
}//出函数时&n被保存在寄存器中传出来,但是n已经被销毁了!!!
int main()
{
int* p = test();
return 0;
}
指针访问越界
int arr[10] = 0;
int* p = arr[10];
规避野指针
指针初始化(如果提前不知道指针需要指向哪里,可以先置空int* p = NULL)及时置空(NULL)指针使用前检查有效性
assert断言
在debug版本中,判断括号内的内容,如果为真则不产生任何效果;如果为假程序报错 在release版本中,assert宏被优化掉了,无论是否为真都不产生任何效果
assert断言需要包含头文件
#include
//......
int* p = NULL;
assert(p != NULL)
此时程序会报错 如果需要禁用assert宏,需要在assert头文件前面加上#define NDEBUG这样assert语句就会失效
传值调用和传址调用
传值调用
传值调用是将实际参数的值传给形式参数,二者拥有独立空间,对形参的操作无法影响实参
#include
void Change(int a, int b)
{
int change = b;
b = a;
a = change;
}
int main()
{
int x = 3;
int y = 5;
Change(x, y);
printf("%d", x);
printf("%d", y);
return 0;
}
代码运行后,发现x和y实际上并没有交换,这就是因为改变形参不会改变实参
传址调用
#include
void Change(int* a, int* b)
{
int change = 0;
change = *b;
*b = *a;
*a = *change;
}
int main()
{
int x = 3;
int y = 5;
Change(&x, &y);
printf("%d", x);
printf("%d", y);
return 0;
}
把传入的变量变成地址后,形参和实参共用一个地址,所以是完全一样的,改变形参就是在改变实参
数组和指针
数组名的理解
在一般情况下,数组名就是首元素的地址,但有两种情况不同:
和数组在同一个函数内的sizeof(数组名),但是如果是数组传参后再用sizeof计算大小,此时的数组名就又算作首元素地址了,因为数组传参的本质是传首元素的地址,所以我们可以通过函数来改变数组
#include
void test(int* ptr)//这里用数组(int ptr[])或者指针接收都可以
{
printf("%d\n", sizeof(arr));
}
int main()
{
int arr[10] = {0};
test(arr);
return 0;
}
此时打印的结果就是指针长度4而不是数组长度40了
&数组名表示整个数组的地址,此时数组名就是整个数组,在数值上等于首元素地址,但是 &数组名+1跨越的长度是整个数组
使用指针访问数组
#include
int main()
{
int arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int sz = sizeof(arr) / sizeof(arr[0]);
int* p = arr;
int i = 0;
for(i = 0; i < sz; i++)
{
printf("%d ", *(p + i));
}
return 0;
}
这个代码可以完成对该数组的打印
此处的p换成arr,变成*(arr + i)也是可以的
那么就有*(arr + i)=*(i + arr)=arr[i]=i[arr]
最新发布
-
蠓的防治 中国卫生有害生物防制协会
2025-05-04 00:16:09 -
宝马改装排气管
2025-05-06 13:04:13 -
10款专业级logo设计软件,行业精选!
2025-05-07 02:07:52 -
王者荣耀: 为什么法师最通用的铭文是88法穿呢, 怜悯真的不好吗
2025-05-03 22:49:20 -
巷的组词:探索“巷”字的丰富用法
2025-05-07 21:48:04 -
原神刷怪时间 原神怪物和物品刷新时间间隔
2025-05-08 04:10:59 -
iPhone SE 4真要来了!库克官宣:苹果2月19日发布新品
2025-05-03 04:26:32 -
手黏糊糊是怎么回事
2025-05-05 23:18:02 -
冰封的解释
2025-05-05 15:46:21 -
婚礼邀请语简短
2025-05-06 21:16:47