网站开发如何跟客户沟通需求,青岛市住房和城乡建设局,.net网站设计,百度智能小程序是什么文章目录 内存和地址内存编址 指针变量和地址取地址操作符指针变量和解引用操作符指针变量指针变量类型解引用操作符指针变量的大小 指针变量类型的意义指针的解引用指针-整数void*指针 const修饰指针指针运算指针-整数指针-指针指针的关系运算 野指针野指针成因如何规避野指针… 文章目录 内存和地址内存编址 指针变量和地址取地址操作符指针变量和解引用操作符指针变量指针变量类型解引用操作符指针变量的大小 指针变量类型的意义指针的解引用指针-整数void*指针 const修饰指针指针运算指针-整数指针-指针指针的关系运算 野指针野指针成因如何规避野指针 assert断言传值调用和传址调用写在最后 内存和地址
内存
在讲内存和地址之前先看一个⽣活中的案例
假设有⼀栋宿舍楼把你放在楼⾥楼上有100个房间但是房间没有编号你的⼀个朋友来找你玩 如果想找到你就得挨个房⼦去找这样效率很低但是我们如果根据楼层和楼层的房间的情况给每个房间编上号如
//⼀楼101102103...
//⼆楼201202203...
//...有了房间号如果你的朋友得到房间号就可以快速的找房间找到你。
⽣活中每个房间有了房间号就能提⾼效率能快速的找到房间。
如果把上⾯的例⼦对照到计算机中⼜是怎么样呢
我们知道计算机上 CPU中央处理器在处理数据的时候需要的数据是在内存中读取的处理后的数据也会放回内存中那我们买电脑的时候电脑上内存是8GB/16GB/32GB等那这些内存空间如何⾼效的管理呢
其实也是把内存划分为⼀个个的内存单元每个内存单元的⼤⼩取1个字节。
计算机中常⻅的单位
bit - ⽐特位
Byte - 字节
KB
MB
GB
TB
PB1Byte 8bit
1KB 1024Byte
1MB 1024KB
1GB 1024MB
1TB 1024GB
1PB 1024TB其中每个内存单元相当于⼀个学⽣宿舍⼀ 个字节空间⾥⾯能放8个⽐特位就好⽐同学们住的⼋⼈间每个⼈是⼀个⽐特位。
每个内存单元也都有⼀个编号这个编号就相当于宿舍房间的⻔牌号有了这个内存单元的编号CPU就可以快速找到⼀个内存空间。
⽣活中我们把⻔牌号也叫地址在计算机中我们 把内存单元的编号也称为地址。C语⾔中给地址起了新的名字叫指针。
内存单元的编号 地址 指针 编址
CPU访问内存中的某个字节空间必须知道这个 字节空间在内存的什么位置⽽因为内存中字节 很多所以需要给内存进⾏编址(就如同宿舍很 多需要给宿舍编号⼀样)。
计算机中的编址并不是把每个字节的地址记录下来⽽是通过硬件设计完成的
CPU和内存之间有⼀组线叫做地址总线。
我们可以简单理解32位机器有32根地址总线 每根线只有两态表⽰0,1【电脉冲有⽆】那么 ⼀根线就能表⽰2种含义2根线就能表⽰4种含义依次类推。32根地址线就能表⽰232种含 义每⼀种含义都代表⼀个地址。 地址信息被下达给内存在内存上就可以找到该地址对应的数据将数据再通过数据总线传⼊ CPU内寄存器。 指针变量和地址
取地址操作符
理解了内存和地址的关系我们再回到C语⾔在C语⾔中创建变量其实就是向内存申请空间⽐如 ⽐如上述的代码就是创建了整型变量a内存中申请4个字节⽤于存放整数10其中每个字节都有地址上图中4个字节的地址分别是
0x006FFD70
0x006FFD71
0x006FFD72
0x006FFD73使用操作符得到a的地址
#include stdio.h
int main()
{int a 10;a;//取出a的地址 printf(%p\n, a);return 0;
}按照画图的例⼦会打印处理006FFD70
a取出的是a所占4个字节中地址较⼩的字节的地址 虽然整型变量占⽤4个字节我们只要知道了第⼀个字节地址顺藤摸⽠访问到4个字节的数据就是可行的了。 指针变量和解引用操作符
指针变量
那我们通过取地址操作符()拿到的地址是⼀个数值⽐如0x006FFD70这个数值有时候也是需要存储起来⽅便后期再使⽤的那我们把这样的地址值存放在哪⾥呢
答案是指针变量中。
#include stdio.h
int main()
{int a 10;int * pa a;//取出a的地址并存储到指针变量pa中 return 0;
}指针变量也是⼀种变量这种变量就是⽤来存放地址的存放在指针变量中的值都会理解为地址。
指针变量类型
int a 10;
int * pa a;这⾥pa左边写的是 int* * 是在说明pa是指针变量⽽前⾯的 int 是在说明pa指向的是整型(int) 类型的对象。 要存储什么类型的对象的地址指针变量类型就是对象类型*
解引用操作符
我们只要拿到了地址指针就可以通过地址指针找到地址指针 指向的对象这⾥使用的⼀个操作符叫解引⽤操作符(*)。
#include stdio.h
int main()
{int a 100;int* pa a;*pa 0;return 0;
}上⾯代码中第6⾏就使⽤了解引⽤操作符 *pa 的意思就是通过pa中存放的地址找到指向的空间 *pa其实就是a变量了
所以*pa0这个操作符是把a改成了0。 指针变量的大小
前⾯的内容我们了解到32位机器假设有32根地址总线每根地址线出来的电信号转换成数字信号后 是1或者0那我们把32根地址线产⽣的2进制序列当做⼀个地址那么⼀个地址就是32个bit位需要4个字节才能存储。
指针变量是⽤来存放地址的那么指针变量的⼤⼩就得是4个字节的空间才可以。
同理64位机器假设有64根地址线⼀个地址就是64个⼆进制位组成的⼆进制序列存储起来就需要 8个字节的空间指针变量的⼤⼩就是8个字节。
#include stdio.h
//指针变量的⼤⼩取决于地址的⼤⼩
//32位平台下地址是32个bit位即4个字节
//64位平台下地址是64个bit位即8个字节
int main()
{printf(%zd\n, sizeof(char *));printf(%zd\n, sizeof(short *));printf(%zd\n, sizeof(int *));printf(%zd\n, sizeof(double *));return 0;
}结论
32位平台下地址是32个bit位指针变量⼤⼩是4个字节64位平台下地址是64个bit位指针变量⼤⼩是8个字节注意指针变量的⼤⼩和类型是⽆关的只要指针类型的变量在相同的平台下⼤⼩都是相同的 指针变量类型的意义
指针变量的⼤⼩和类型⽆关只要是指针变量在同⼀个平台下⼤⼩都是⼀样的为什么还要有各种各样的指针类型呢
其实指针类型是有特殊意义的接下来继续介绍。 指针的解引用
//代码1
#include stdio.h
int main()
{int n 0x11223344;int *pi n; *pi 0; return 0;
}//代码2
#include stdio.h
int main()
{int n 0x11223344;char *pc (char *)n;*pc 0;return 0;
}调试我们可以看到代码1会将n的4个字节全部改为0但是代码2只是将n的第⼀个字节改为0。
结论
**指针的类型决定了对指针解引⽤的时候有多⼤的权限⼀次能操作⼏个字节。 **
⽐如 char* 的指针解引⽤就只能访问⼀个字节⽽ int* 的指针的解引⽤就能访问四个字节。
指针±整数
#include stdio.h
int main()
{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);return 0;
}我们可以看出 char* 类型的指针变量1跳过1个字节 int* 类型的指针变量1跳过了4个字节。
这就是指针变量的类型差异带来的变化。指针1其实跳过1个指针指向的元素。指针可以1那也可以-1。
结论
指针的类型决定了指针向前或者向后⾛⼀步有多⼤距离
void*指针
在指针类型中有⼀种特殊的类型是 void * 类型的可以理解为⽆具体类型的指针或者叫泛型指针这种类型的指针可以⽤来接受任意类型地址。
但是也有局限性 void* 类型的指针不能直接进⾏指针的±整数和解引⽤的运算。
#include stdio.h
int main()
{int a 10;void* pa a;*pa 10;return 0;
}VS编译代码的结果 这⾥我们可以看到 void* 类型的指针可以接收不同类型的地址但是⽆法直接进⾏指针运算。
那么 void* 类型的指针到底有什么⽤呢
⼀般 void* 类型的指针是使⽤在函数参数的部分⽤来接收不同类型数据的地址这样的设计可以 实现泛型编程的效果。使得⼀个函数来处理多种类型的数据在之后博客中会讲解。 const修饰指针
const修饰指针变量可以放在*的左边也可以放在*的右边意义是不⼀样的。
int * p;//没有const修饰
int const * p;//const 放在*的左边做修饰
int * const p;//const 放在*的右边做修饰 我们看下⾯代码来分析具体分析⼀下
#include stdio.h
//代码1 - 测试⽆const修饰的情况
void test1()
{int n 10;int m 20;int *p n;*p 20;//ok?p m; //ok?
}
//代码2 - 测试const放在*的左边情况
void test2()
{int n 10;int m 20;const int* p n;*p 20;//ok?p m; //ok?
}
//代码3 - 测试const放在*的右边情况
void test3()
{int n 10;int m 20;int * const p n;*p 20; //okp m; //ok
}
//代码4 - 测试*的左右两边都有const
void test4()
{int n 10;int m 20;int const * const p n;*p 20; //ok?p m; //ok?
}
int main()
{//测试⽆const修饰的情况 test1();//测试const放在*的左边情况 test2();//测试const放在*的右边情况 test3();//测试*的左右两边都有const test4();return 0;
}结论const修饰指针变量的时候 const如果放在 * 的左边修饰的是指针指向的内容保证指针指向的内容不能通过指针来改变。 但是指针变量本⾝的内容可变。 const如果放在*的右边修饰的是指针变量本⾝保证了指针变量的内容不能修改但是指针指向的内容可以通过指针改变。 指针运算
指针的基本运算有三种分别是
指针±整数指针-指针指针的关系运算
指针±整数
因为数组在内存中是连续存放的只要知道第⼀个元素的地址顺藤摸⽠就能找到后⾯的所有元素。 #include stdio.h
//指针- 整数
int main()
{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 这⾥就是指针整数 }return 0;
}指针-指针
//指针-指针
#include stdio.h
int my_strlen(char *s)
{char *p s;while(*p ! \0 )p;return p-s;
}
int main()
{printf(%d\n, my_strlen(abc));return 0;
}必须是同一块内存空间
指针的关系运算
//指针的关系运算
#include stdio.h
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]);while(parrsz) //指针的⼤⼩⽐较 {printf(%d , *p);p;}return 0;
}指针是地址说白了就是一组二进制数为了方便显示的用的是十六进制那是数就会有大小即低地址高地址 野指针
概念野指针就是指针指向的位置是不可知的随机的、不正确的、没有明确限制的
野指针成因
指针未初始化
#include stdio.h
int main()
{ int *p;//局部变量指针未初始化默认为随机值 *p 20;return 0;
}指针越界访问
#include stdio.h
int main()
{int arr[10] {0};int *p arr[0];int i 0;for(i0; i11; i){//当指针指向的范围超出数组arr的范围时p就是野指针 *(p) i;}return 0;
}指针指向的空间释放
#include stdio.h
int* test()
{int n 100;return n;//函数栈帧使用完销毁
}
int main()
{int*p test();//但p还能找到这块空间printf(%d\n, *p);return 0;
}如何规避野指针
指针初始化
如果明确知道指针指向哪⾥就直接赋值地址如果不知道指针应该指向哪⾥可以给指针赋值NULL.
NULL 是C语⾔中定义的⼀个标识符常量值是00也是地址这个地址是⽆法使⽤的读写该地址会报错
#include stdio.h
int main()
{int num 10;int*p1 num;int*p2 NULL;return 0;
}注意指针越界
⼀个程序向内存申请了哪些空间通过指针也就只能访问哪些空间不能超出范围访问超出了就是越界访问。
指针变量不再使⽤时及时置NULL指针使⽤之前检查有效性
当指针变量指向⼀块区域的时候我们可以通过指针访问该区域后期不再使⽤这个指针访问空间的时候我们可以把该指针置为NULL。因为约定俗成的⼀个规则就是只要是NULL指针就不去访问 同时使⽤指针之前可以判断指针是否为NULL。
int main()
{int arr[10] {1,2,3,4,5,6,7,8,9,10};int *p arr[0];int i 0;for(i0; i10; i){*(p) i;}//此时p已经越界了可以把p置为NULL p NULL;//下次使⽤的时候判断p不为NULL的时候再使⽤ //...p arr[0];//重新让p获得地址 if(p ! NULL) //判断 {//...}return 0;
}避免返回局部变量的地址
如造成野指针的第3个例⼦不要返回局部变量的地址。 assert断言
assert.h 头⽂件定义了宏 assert() ⽤于在运⾏时确保程序符合指定条件如果不符合就报错终⽌运⾏。这个宏常常被称为“断⾔”。
assert(p ! NULL);上⾯代码在程序运⾏到这⼀⾏语句时验证变量 p 是否等于 NULL 。如果确实不等于 NULL 程序 继续运⾏否则就会终⽌运⾏并且给出报错信息提⽰。
assert() 宏接受⼀个表达式作为参数。如果该表达式为真返回值⾮零 assert() 不会产⽣任何作⽤程序继续运⾏。如果该表达式为假返回值为零 assert() 就会报错在标准错误 流 stderr 中写⼊⼀条错误信息显⽰没有通过的表达式以及包含这个表达式的⽂件名和⾏号。 assert() 的使⽤对程序员是⾮常友好的
使⽤ assert() 有⼏个好处
它不仅能⾃动标识⽂件和出问题的⾏号
还有⼀种⽆需更改代码就能开启或关闭 assert() 的机制。如果已经确认程序没有问题不需要再做断⾔就在 #include 语句的前⾯定义⼀个宏 NDEBUG 。
#define NDEBUG//关闭assert宏
#include assert.h然后重新编译程序编译器就会禁⽤⽂件中所有的 assert() 语句。如果程序⼜出现问题可以移除这条 #define NDEBUG 指令或者把它注释掉再次编译这样就重新启⽤了 assert() 语 句。
assert() 的缺点是因为引⼊了额外的检查增加了程序的运⾏时间。 ⼀般我们可以在 Debug 中使⽤在 Release 版本中选择禁⽤ assert 就⾏在 VS 这样的集成开发环境中在 Release 版本中直接就是优化掉了。这样在debug版本写有利于程序员排查问题 在 Release 版本不影响⽤⼾使⽤时程序的效率。 传值调用和传址调用
学习指针的⽬的是使⽤指针解决问题那什么问题⾮指针不可呢
例如写⼀个函数交换两个整型变量的值
⼀番思考后我们可能写出这样的代码
#include stdio.h
void Swap1(int x, int y)
{int tmp x;x y;y tmp;
}
int main()
{int a 0;int b 0;scanf(%d %d, a, b);printf(交换前a%d b%d\n, a, b);Swap1(a, b);printf(交换后a%d b%d\n, a, b);return 0;
}结果如下 我们发现其实没产⽣交换的效果这是为什么呢
在中我们就介绍到形参是实参的一份临时拷贝改变形参不影响实参这种调用方式叫做传值调用是不能实现我们上述要求的。
所以我们需要使用指针进行传址调用
#include stdio.h
void Swap2(int*px, int*py)
{int tmp 0;tmp *px;*px *py;*py tmp;
}
int main()
{int a 0;int b 0;scanf(%d %d, a, b);printf(交换前a%d b%d\n, a, b);Swap2(a, b);printf(交换后a%d b%d\n, a, b);return 0;
}结论
传址调⽤可以让被调函数和主调函数之间建⽴真正的联系在被调函数内部可以修改主调函数中的变量
所以当被调函数中只是需要主调函数中的变量值来实现计算就可以采⽤传值调⽤。
如果函数内部要修改主调函数中的变量的值就需要传址调⽤。 写在最后
C语言指针是一个重头戏关于指针的内容会有4-5篇博客敬请期待喔
以上就是关于深入理解指针1的内容啦各位大佬有什么问题欢迎在评论区指正您的支持是我创作的最大动力❤️
文章转载自: http://www.morning.qichetc.com.gov.cn.qichetc.com http://www.morning.rgnq.cn.gov.cn.rgnq.cn http://www.morning.qieistand.com.gov.cn.qieistand.com http://www.morning.hydkd.cn.gov.cn.hydkd.cn http://www.morning.smhtg.cn.gov.cn.smhtg.cn http://www.morning.fbmzm.cn.gov.cn.fbmzm.cn http://www.morning.xxgfl.cn.gov.cn.xxgfl.cn http://www.morning.yubkwd.cn.gov.cn.yubkwd.cn http://www.morning.rdlfk.cn.gov.cn.rdlfk.cn http://www.morning.tsnq.cn.gov.cn.tsnq.cn http://www.morning.jcbjy.cn.gov.cn.jcbjy.cn http://www.morning.snbrs.cn.gov.cn.snbrs.cn http://www.morning.gltmz.cn.gov.cn.gltmz.cn http://www.morning.ycpnm.cn.gov.cn.ycpnm.cn http://www.morning.fhrt.cn.gov.cn.fhrt.cn http://www.morning.kcrw.cn.gov.cn.kcrw.cn http://www.morning.pbtdr.cn.gov.cn.pbtdr.cn http://www.morning.jpmcb.cn.gov.cn.jpmcb.cn http://www.morning.kyjpg.cn.gov.cn.kyjpg.cn http://www.morning.sqyjh.cn.gov.cn.sqyjh.cn http://www.morning.qnbgk.cn.gov.cn.qnbgk.cn http://www.morning.qqhmg.cn.gov.cn.qqhmg.cn http://www.morning.wqbrg.cn.gov.cn.wqbrg.cn http://www.morning.fjntg.cn.gov.cn.fjntg.cn http://www.morning.hxlpm.cn.gov.cn.hxlpm.cn http://www.morning.ngpdk.cn.gov.cn.ngpdk.cn http://www.morning.qbgdy.cn.gov.cn.qbgdy.cn http://www.morning.ryztl.cn.gov.cn.ryztl.cn http://www.morning.rnfn.cn.gov.cn.rnfn.cn http://www.morning.rqsr.cn.gov.cn.rqsr.cn http://www.morning.jbysr.cn.gov.cn.jbysr.cn http://www.morning.qnpyz.cn.gov.cn.qnpyz.cn http://www.morning.dnls.cn.gov.cn.dnls.cn http://www.morning.nbnpb.cn.gov.cn.nbnpb.cn http://www.morning.kbqws.cn.gov.cn.kbqws.cn http://www.morning.bojkosvit.com.gov.cn.bojkosvit.com http://www.morning.rfpb.cn.gov.cn.rfpb.cn http://www.morning.blznh.cn.gov.cn.blznh.cn http://www.morning.syznh.cn.gov.cn.syznh.cn http://www.morning.mbqyl.cn.gov.cn.mbqyl.cn http://www.morning.bydpr.cn.gov.cn.bydpr.cn http://www.morning.nqwz.cn.gov.cn.nqwz.cn http://www.morning.dgng.cn.gov.cn.dgng.cn http://www.morning.bqts.cn.gov.cn.bqts.cn http://www.morning.ckwrn.cn.gov.cn.ckwrn.cn http://www.morning.gblrn.cn.gov.cn.gblrn.cn http://www.morning.wqfrd.cn.gov.cn.wqfrd.cn http://www.morning.bkqdg.cn.gov.cn.bkqdg.cn http://www.morning.drtgt.cn.gov.cn.drtgt.cn http://www.morning.qyhcg.cn.gov.cn.qyhcg.cn http://www.morning.nfsrs.cn.gov.cn.nfsrs.cn http://www.morning.rxhsm.cn.gov.cn.rxhsm.cn http://www.morning.zwfgh.cn.gov.cn.zwfgh.cn http://www.morning.mwpcp.cn.gov.cn.mwpcp.cn http://www.morning.ywtbk.cn.gov.cn.ywtbk.cn http://www.morning.rgxll.cn.gov.cn.rgxll.cn http://www.morning.zrpbf.cn.gov.cn.zrpbf.cn http://www.morning.mxcgf.cn.gov.cn.mxcgf.cn http://www.morning.llcsd.cn.gov.cn.llcsd.cn http://www.morning.lbxcc.cn.gov.cn.lbxcc.cn http://www.morning.xwbld.cn.gov.cn.xwbld.cn http://www.morning.bpmdh.cn.gov.cn.bpmdh.cn http://www.morning.wtnwf.cn.gov.cn.wtnwf.cn http://www.morning.rbjth.cn.gov.cn.rbjth.cn http://www.morning.jydky.cn.gov.cn.jydky.cn http://www.morning.bojkosvit.com.gov.cn.bojkosvit.com http://www.morning.bdwqy.cn.gov.cn.bdwqy.cn http://www.morning.qypjk.cn.gov.cn.qypjk.cn http://www.morning.xesrd.com.gov.cn.xesrd.com http://www.morning.wffxr.cn.gov.cn.wffxr.cn http://www.morning.phlrp.cn.gov.cn.phlrp.cn http://www.morning.qtfss.cn.gov.cn.qtfss.cn http://www.morning.zcncb.cn.gov.cn.zcncb.cn http://www.morning.rlns.cn.gov.cn.rlns.cn http://www.morning.ngqdp.cn.gov.cn.ngqdp.cn http://www.morning.ybnps.cn.gov.cn.ybnps.cn http://www.morning.yqsr.cn.gov.cn.yqsr.cn http://www.morning.sxcwc.cn.gov.cn.sxcwc.cn http://www.morning.lpnpn.cn.gov.cn.lpnpn.cn http://www.morning.wnjsp.cn.gov.cn.wnjsp.cn