建设网站需要的软硬件,域名信息,安康做网站,wap网站 视频教程关注小庄 顿顿解馋 (≧∇≦) 引言#xff1a; 小伙伴们在学习c语言过程中是否因为指针而困扰#xff0c;指针简直就像是小说女主#xff0c;它逃咱追#xff0c;我们插翅难飞…本篇文章让博主为你打理打理指针这个傲娇鬼吧~ 本节我们将认识到指针本质#xff0c;何为指针和… 关注小庄 顿顿解馋 (≧∇≦) 引言 小伙伴们在学习c语言过程中是否因为指针而困扰指针简直就像是小说女主它逃咱追我们插翅难飞…本篇文章让博主为你打理打理指针这个傲娇鬼吧~ 本节我们将认识到指针本质何为指针和她的性质用法请放心食用 指针本质及其性质用法 一.指针的本质1. 内存与地址2. 指针变量与地址2.1 取址符2.2指针变量2.3解引用操作符*2.4指针变量的大小 二.指针的性质1.指针类型的意义1.1 不同指针类型的解引用1.2指针-整数 2. const修饰指针2.1 const修饰变量2.2 const修饰指针变量 3. 指针运算3.1 指针-整数3.2 指针-指针3.3指针的关系运算 三.指针的使用1. 野指针3.assert断言4.传值调用和传址调用在这里插入图片描述 一.指针的本质
1. 内存与地址 内存单元我们在学校寄宿时是几个人为一个集体为宿舍的为了方便入住以及内务检查学校给每个宿舍设置了门牌号方便辨认。类似地存储数据的内存划分一个一个内存单元同时给它们编上“号”方便取出数据。 内存单元内存最小单元单位是字节也就是8个bit。
地址正如上言所说我们给内存单元的编号就是地址在c语言中我们叫它指针
总结 指针 地址 内存单元编号 他是我们用来取数据的东西
2. 指针变量与地址
数据在内存中的地址就是指针类比之前我们学习过的数据类型整形变量是存储整形的变量那么指针变量就是存指针的变量
我们知道指针变量了先不着急我们知道指针变量存的是地址赋值两边要相同类型你知道怎么获得地址吗
2.1 取址符
取址符顾名思义就是取出地址打印地址时我们用的是%p,在我们编译器如何查看地址呢接下来上图 在vs上进入调试模式-调试-窗口-内存/监视窗口都可查看内存别忘记加上我们的取址符哦 2.2指针变量
指针变量也是⼀种变量这种变量就是⽤来存放地址的存放在指针变量中的值都会理解为地址他的定义如下
Datatype * name;
int a 10;
int * pf a;
char c c;
char * pc c;理解这里的*用来表示这是一个指针变量前面的Datatype表示的是指针指向的数据类型而去掉变量名就是这个指针变量的类型比如int* pf表示pf是个指针变量它指向的数据类型是int存储的是int类型数据的地址指针类型是int *
类比地聪明的你应该也会类比其他数据类型了吧~
void*类型 在指针类型中有这么一个存在它可以可以理解为⽆具体类型的指针或者叫泛型指针这种类型的指针可以⽤来接受任意类型地址。就像一个公共场所。
2.3解引用操作符*
有的小伙伴到这里可能要问了既然知道什么是指针变量那他怎么用啊快端上来罢这里我们就要了解我们的解引用操作符了
客人房间变量房间编号地址变量a0x11223344变量b0x12332166变量c0x12ffaa11
如上表所示每个变量房间都对应一个编号地址而要进入房间使用它就得用解引用操作符
int a 1;
printf(%d ,a);
int *p a;
*p 10;
printf(%d,a);输出结果是1 10 总结 解引用操作符*作用是通过地址以地址为媒介找到地址指向的空间对该空间进行操作。
2.4指针变量的大小
我们知道不同的数据类型所占字节大小不同那不同的指针类型是不是也是大小不同呢接下来我们先普及一下编址的相关知识 CPU与内存之间是有大量数据交互的。大致过程如下CPU先通过控制总线下命令-地址总线收到命令找对应地址-通过数据总线传输数据。在这个过程中地址总线不只图中一条哦作者只是大概画出来。不同机器地址总线总数不同比如32位机器有32条64位有64条每根线只有两态表⽰0,1【电脉冲有⽆】那么⼀根线就能表⽰2种含义2根线就能表⽰4种含义依次类推。32根地址线就能表⽰2^32种含义每⼀种含义都代表⼀个地址 通过上面的知识普及我们了解到地址总线发出的电信号转换成二进制序列就是我们的地址所以不同的机器就会产生不同位的二进制序列当做地址32位地址-32个bit-4个字节64位地址-64个bit-8个字节。
printf(%zd\n, sizeof(char *));
printf(%zd\n, sizeof(short *));
printf(%zd\n, sizeof(int *));
printf(%zd\n, sizeof(double *));x86(32位环境) 输出结果为4 4 4 4 x64(64位环境) 输出结果为8 8 8 8 总结 指针变量大小与它指向的数据类型无关而与使用的机器有关
二.指针的性质
1.指针类型的意义
既然指针变量大小与指针类型无关那为什么还要设置这么多类型的指针变量有句话叫做存在即合理设置了它肯定是有它的意义的。
1.1 不同指针类型的解引用
//代码1
int n 0x11223344;
int *pi n;
*pi 0;
//代码2
int n 0x11223344;
char* pc n;
*pc 0;两段代码都是存了n的地址只不过是不同指针类型那是否值都变为0了呢我们用之前的查看内存知识观察下。 通过上面观察我们发现不同指针类型得出的结果也不同由此我们可以得出结论不同指针类型解引用权限不同也就是能操作字节个数不同正如上面的例子int解引用能将4个字节都置为0而char解引用只能将1个字节置为0.
1.2指针±整数
int n 10;
char *pc (char*)n;
int *pi n;
printf(%p\n, n);
printf(%p\n, pc);
printf(%p\n, pc1);
printf(%p\n, pi);
printf(%p\n, pi1)输出结果 004FF700 004FF700 004FF701 004FF700 004FF704 我们可以看出 char* 类型的指针变量1跳过1个字节 int* 类型的指针变量1跳过了4个字节。 这就是指针变量的类型差异带来的变化。
结论指针类型还决定了指针向前或向后走一步有多大。 注之前提到的void*虽然能接收各种类型指针但却不能使用指针加减也不能解引用可谓是上天为你关了一扇门也为你打开一扇窗啊。
2. const修饰指针
2.1 const修饰变量
变量是可以修改的如果把变量的地址交给⼀个指针变量通过指针变量的也可以修改这个变量。 但是如果我们希望⼀个变量加上⼀些限制不能被修改怎么做呢这就是const的作⽤。
int m 0;
m 20;//m是可以修改的
const int n 0;
n 20;//n是不能被修改的加上const后n不能动了那我们运用之前的解引用呢 const int n 0;
printf(n %d\n, n);
int*p n;
*p 20;
printf(n %d\n, n);我们可以看到这⾥⼀个确实修改了但是我们还是要思考⼀下为什么n要被const修饰呢就是为了 不能被修改如果p拿到n的地址就能修改n这样就打破了const的限制这是不合理的所以应该让 p拿到n的地址也不能修改n那接下来怎么做呢 2.2 const修饰指针变量
我们给上几段代码来测试下
//代码1 无const
int n 10;
int m 20;
int *p n;
*p 20;//能否赋值
p m; //能否赋值
//代码2 const在*左边
int n 10;
int m 20;
const int *p n;
*p 20;//能否赋值
p m; //能否赋值
//代码3 const在*右边
int n 10;
int m 20;
int *const p n;
*p 20;//能否赋值
p m; //能否赋值
//代码4 const在*左边和右边
int n 10;
int m 20;
int const *const p n;
*p 20;//能否赋值
p m; //能否赋值代码1 都能赋值 代码2 : 语句1不能赋值 代码3 语句2不能赋值 代码4 两语句都不能赋值 总结1.const如果放在*的左边修饰的是指针指向的内容保证指针指向的内容不能通过指针来改变但指针变量本身的内容可变。2.const如果放在*的右边修饰的是指针变量本身的内容保证存储的内容不能修改但指针指向内容可通过指针修改3.const在*的左边和右边则指针变量本身内容和指针指向内容都不能被修改。
总的来说const修饰变量具有常量属性但解引用相当于绕道找后门进入这个变量修改变量的值。此时用const修饰指针相当于把这个后门也关了
3. 指针运算
指针±整数指针-指针指针关系运算
3.1 指针±整数 根据我们前面分析指针±整数表示不同指针向前一步或向后一步的大小。这在数组可以很好运用我们知道数组在内存中是连续存放的这样知道其中一个元素地址再运用指针±整数就能得到其他元素地址我们可以用它来打印数组 int arr[10] {1,2,3,4,5,6,7,8,9,10};
int *p arr[0];
int i 0;
int sz sizeof(arr)/sizeof(arr[0]);
for(i0; isz; i)
{
printf(%d , *(pi));//pi 这⾥就是指针整数
}3.2 指针-指针
int arr[10] {1,2,3,4,5,6,7,8,9,10};
printf(%d,arr[2]-arr[0]);
printf(%d,arr[0]-arr[2]);输出结果为2 -2 我们可以得出指针-指针的绝对值得到的是指针与指针之间的元素个数可以类比日期-日期是天数而指针-指针、就是元素个数。前面的指针±也可以这样类比日期加天数还是日期 注意:该运算的前提条件是相同类型的指针变量
3.3指针的关系运算
指针也是可以比较大小的比较的是在内存位置的高低。
三.指针的使用 接下来我们来了解一些关于指针的使用和注意事项吧1. 野指针
野指针就是指针指向的位置是不可知的随机的、不正确的、没有明确限制的通常以下三种情况会造成野指针。
指针未初始化指针越界访问指针指向空间释放一般指针调用函数后内存空间被回收
//原因3
int* test()
{
int n 100;
return n;
}
int main()
{
int*p test();
printf(%d\n, *p);
return 0;
}这个地方*p接收了返回的n地址但是调用函数后n就被释放了也就是我们常说的空有其表 规避方法
指针初始化为NULL小心越界访问不要返回局部变量地址指针不用了及时置为NULL
3.assert断言
assert.h 头⽂件定义了宏 assert() 用于在运⾏时确保程序符合指定条件如果不符合就报错终⽌运行这个宏常常被称为“断⾔”。assert() 宏接受⼀个表达式作为参数。如果该表达式为真返回值⾮零 assert() 不会产⽣任何作⽤程序继续运⾏。如果该表达式为假返回值为零 assert() 就会报错在标准错误stderr 中写⼊⼀条错误信息显⽰没有通过的表达式以及包含这个表达式的⽂件名和⾏号。
好处 1.能⾃动标识⽂件和出问题的⾏号 2.有⽆需更改代码就能开启或关闭 assert() 的机制。(如果已经确认程序没有问题不需要再做断⾔就在 #include assert.h 语句的前⾯定义⼀个宏NDEBUG)
4.传值调用和传址调用
我们知道函数传值调用时形参是实参的一份临时拷贝改变形参不影响实参那有什么方法能改变我们的实参呢答案是我们的传址调用。
oid Swap2(int*px, int*py)
{
int tmp 0;
tmp *px;
*px *py;
*py tmp;
}
int main()
{
int a 10;
int b 20;
printf(交换前a%d b%d\n, a, b);
Swap1(a, b);
printf(交换后a%d b%d\n, a, b);
return 0;
}输出结果 交换前 a10 b20 交换后 a20 b10 由此可以得出传址调用本质上也是传值也是拷贝地址的值传到函数内部只不过地址指向的空间是唯一的我们便能通过地址来改变它的空间。值得注意的是在函数内部创建的px,py跟a,b不是同个变量只不过他们存储的值地址一样存储的值能干的事一样 本次分享到这里就结束了希望小伙伴能够学到知识喜欢的话给小庄来个三连吧