【c语言】深入理解指针(1)
前言
本文主要介绍指针的内存和地址,指针变量和地址,指针变量类型的意义以及指针的运算,想要深入学习指针的读者,跟着小编一起深入文章学习吧
文章目录
一、内存和地址
1.1如何理解内存
在引进内存的时候我们先来看两个案例
- 在我们的刚踏入大学校园的时候,学校早就已经给每个学生分配好了学号,如果导员要进行找到学生,或者课上点名,可以通过学号来找到学生,方便了对学生的管理
- 教室也有自己独特的教室的门牌号,学校有很多很多的教室,根据教室的门牌号,我们可以迅速的定位教室方位,然后进入教室学习上课,提高了效率,试想一下如果没有教室门牌号,杂乱无章一间一间教室去找,那么必然会浪费很多的时间吧,这样的教室编号的形式,极大的便于找到对应的教室,便于对教室空间进行管理
同样的对如果将这些换位到计算机中的名词内存呢?
当计算机处理数据的时候,所需要的数据是要到内存中进行读取的,为了便于读取,计算机将内存划分为一个个的内存单元,其中每一个内存单元是一个字节
那么计算机为了找到这些内存单元,类似于上面的学生号以及教室号的形式,要给每一个内存单元进行编号,计算机根据这些编号就可以在内存中进行读取内存单元的数据,同时也可以根据编号找到这个内存单元对其中的数据进行修改,但是由于编号只是用语,计算机为了区别编号,给这个编号起名地址,而c语言更是为了突出ins高级感给这个地址起名指针
相同的概念 | ||
---|---|---|
编号 | 地址 | 指针 |
1.2如何理解编址
- 编址是将内存单元分配唯一一个地址,以便为了对内存单元进行访问和修改数据等操作
那么计算机是如何对内存单元分配地址的呢,这就不得不引出计算机cpu和内存是如何进行联系的了,两者的数据传输是通过线来进行连接的,这里我们讲述地址总线,在32位计算机下有32根线,线可以通电和不通电两种情况,那么就是有2^32种数据,通过这些数据将内存中的内存单元进行编号,通过这些数据就可以对内存单元进行访问于修改了
二、指针变量和地址
2.1取地址操作符(&)
- 当你创建一个变量的时候,这时侯就会向内存中申请了空间,而申请空间就是申请内存,内存又是由一个个内存单元构成的,那么就是说,在内存中会有内存单元来进行存放这个变量
,同样的,为了便于访问与修改计算机给这些内存进行了编号也就是给分配了地址,如何找到这个地址呢,&应运而生,这时取地址操作符通过这个操作符我们可以将变量的地址取出来
#include <stdio.h>
int main()
{
int a = 4;
printf("%p", &a);//将变量a的地址取出来并且进行打印
return 0;
}
- 这里的运行结果是在32位环境下运行的,以16进制的数字进行表示,一个16进制数就是4个二进制位,那么图中打印的地址有8位,4乘以8等于32,正好顺应了我们讲的32根线,一个表示两个信号0和1,当然这里仅仅是使用了16进制的表示形式罢了
- 变量a是int类型的这里取出的地址是a存在4个内存单元中的首个内存单元
这里可以提前剧透一下,虽然取出的是首个较低的内存单元的地址,但是将a的地址取出来存放到指针中去,指针是int类型,指针类型的不同影响的是指针一次性在内存中读取多少个字节,比如int类型就是一次性读取4个字节,char*类型就是一次性读取1个字节这样子
2.2指针变量
- 通过上文中的&取地址操作符可以取出地址,那么这个地址我们以后可能会用到,同时c语言将地址定义为指针,那么会不会是用指针变量进行接收呢?是的
指针变量就是用来接收地址的,并存放起来,话不多说我们先用起来
int main()
{
int a = 4;
int* pa = &a;
printf("%p\n", &a);
printf("%p\n", pa);
return 0;
}
- 下图可以很明显的看出,pa和&a的值完全相同,验证了地址等同指针
2.3细化理解指针
- 通过一个简单的int加上*我们就可以创建一个指针变量,那么int是指什么呢,*又是指什么呢?
2.4如何拿到指针指向的数据
- 我们将地址用指针变量存放起来就是为了有朝一日可以用上,那么如何进行使用呢,这里就要介绍*了,是解引用操作符,在变量的前面加上就可以对这个指针指向的地址中的内容数据进行访问,同时也可以对这个数据进行修改
#include <stdio.h>
int main()
{
int a = 0;
int* pa = &a;
*pa = 11;
printf("%d\n", a);
return 0;
}
- 根据代码可以很明显的看出来a中的值由0被修改成了11
2.5指针变量的大小
- 常见类型指针变量我们可以使用sizeof操作符来进行求指针的大小进行验证
int main()
{
int a = 0;
int* pa = &a;
printf("%zd\n", sizeof(pa));
printf("%zd\n", sizeof(int*));
printf("%zd\n", sizeof(short*));
printf("%zd\n", sizeof(long*));
printf("%zd\n", sizeof(long long*));
printf("%zd\n", sizeof(char*));
printf("%zd\n", sizeof(float*));
printf("%zd\n", sizeof(double*));
printf("%zd\n", sizeof(long double*));
//64位环境下是指针变量的大小是8字节数
//32位环境下是指针变量的大小是4字节数
return 0;
}
- x86环境即32位环境下,即一位可以表示0和1两个信号,32位则可以表示32个0和1的信号,而0和1就是二级制的表示形式,一个二进制数就是一个比特位bit,即有32个比特位bit
然而8个比特位就是一个字节,32位对应四个字节
那么在32位环境下,指针的大小就是4个字节
- x64环境即64位环境下,即一位可以表示0和1两个信号,64位则可以表示64个0和1的信号,而0和1就是二级制的表示形式,一个二进制数就是一个比特位bit,即有64个比特位bit,然而8个比特位就是一个字节,64位对应八个字节
那么在64位环境下,指针的大小就是8个字节
32位环境 | 64位环境 |
---|---|
32个比特位bit | 64个比特位bit |
4个字节 | 8个字节 |
三、指针变量类型的意义
3.1指针访问的字节数
- 既然指针仅仅是一个地址,所有的指针都有其唯一对应的地址,其实前文已经对读者朋友们进行“剧透”了,指针的类型影响是指针一次性可以访问几个字节,int类型的指针变量一次性访问4个字节,char类型的指着一次性访问1个字节,指针访问的字节数与它所指向地址的内容的类型有关,int整形是4个字节,那么指针指向它的时候,对指针进行解引用一次性访问4个字节,char同理访问1个字节
3.2指针±整数地址的变化
- 既然指针的访问根据指向内容的类型不同,一次性所访问的字节数也是不同的,那么我们如果对其尝试±呢,是不是对应跳过的字节数也是不同的呢?
int main()
{
int a = 1;
char ch = 'd';
int* pa = &a;
char* pc = &ch;
printf("&pa = %p\n", pa);
printf("&pc = %p\n", pc);
printf("加后\n");
printf("&pa = %p\n", pa+1);
printf("&pc = %p\n", pc+1);
return 0;
}
- 下图我们可以很清晰的看出,对int类型的指针变量+1则是一次性跳过了4个字节,char类型的指针变量+1则是一次性跳过了1个字节,所以不同的指针类型影响的是指针访问的字节数,即它的权限
3.3void*指针
3.3.1void*指针的作用
- 指针有不同的类型,那么不同类型的指针相互之间可以赋值吗,下面我们来进行探讨一下
int main()
{
int a = 0;
char* pa = &a;
return 0;
}
- 编译器提示警告,这样是不兼容的即,语法上不允许这样进行赋值,既然语法上不允许我们这样赋值,那有没有一种指针可以接收任意类型的指针呢,有的—void*无具体类型指针(又称作泛型指针)
- 当我们使用void*类型的指针变量来进行接收的时候就可以
int main()
{
int a = 0;
void* pc = &a;
return 0;
}
- 这种情况编译器没有警告,语法允许,void*类型的指针可以用来接收任意类型的指针
3.3.2void*指针的局限性
- void指针类型仅仅是用于存放指针的,不能进行指针的 - +操作
int main()
{
int a = 0;
void* p = &a;
p++;
p--;
*p;
return 0;
}
- 如图可验证,void类型的指针真的仅仅用来存放地址的,不能对地址进行一系列的操作, - +等
- void*是可以放到函数参数中去接受任意类型的指针的
- 关于void*的具体应用,后期小编会实际进行应用展示给大家,请期待以后小编的更新
四、指针运算
4.1指针的+ -
4.1.1指针的+
- 下图是使用指针进行遍历数组,取出数组的首元素的地址,使用指针+i对首元素的地址进行+操作,这样可以找到数组的每一个元素的地址,并且*解引用进行访问数组中存储的值
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int* p = &arr[0];
int sz = sizeof(arr) / sizeof(arr[0]);
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ", *(p + i));
}
printf("\n");
return 0;
}
4.1.2指针变量的减法
- 指针变量的减法是,同一块内存空间中,指针间指针所指向的类型的个数
- 在同一块内存空间中,地址是连续存放的,在数组中,随着数组下标的增加,数组由低地址向高地址进行储存,高地址减去低地址是正数,相反,低地址减去高地址是负数,在同一块空间上,两个相减的指针是其分别对应指向的元素地址(以具体类型的字节数为单位进行跳跃访问,比如两个指针指向的地址是储存的元素的类型是int类型,int类型是4个字节,指针的相差数的是以4个字节为基础单位进行计算的)之间的相差数
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int* p = &arr[0];
int* p1 = &arr[9];
int minus = p1 - p;
printf("%d\n", minus);
4.2指针的比较
- 指针之间是可以进行相互比较的,关于指针的比较,小编只探讨在同一块连续空间上的比较,比如:数组,求出数组的大小,让指针指向首元素的地址,同时arr也是首元素的地址,首元素的地址加上大小则指向数组最后一个元素的后面的第一个位置,使用指针在同一块的比较关系进行遍历数组操作
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int* p = arr;
int sz = sizeof(arr) / sizeof(arr[0]);
while (p < arr+sz)
{
printf("%d ", *p);
p++;
}
return 0;
}
总结
以上就是今天的博客内容啦,水滴石穿,坚持就是胜利,读者朋友们可以点个关注
点赞收藏加关注,关注小编的后续更新!