前言:
全文以虚拟内存开讲,关于这个无意间刷到一个up讲的非常简单:虚拟内存
本文章只谈C指针,也会涉及到指针用法,像“数组为什么与指针有关”但对其不会详谈数组、函数、兼容、隐式转换等额外内容只会给其的简单解释,如果在本文章内给详细解释只会加大各位学习难度,而且也为我自己没事找麻烦,如果本文章“流量”可观的话,我会同样根据cppreference网站与C标准内容(含C98到C20)为参考在以个人的话整理后出相应的文章,也会有小部分C23草案内容,C23与新标准如有使用,本人会在文章内标出来。
本文章对于新人来说:“帮助你们理解改变之前的错误认知…”,对于入门后的也可以看下加深印象,对于大佬与从业人员希望指出文章错误,本人(CSDN:星空长夜i)第一次出教程文章,原来都是在群给解答。
未经允许,禁止转载。
目录:完成后放置。
第一章 指针入门了解(必看)
1.0理论(都适合):
想必大家学C的时候被指针、数组折腾过吧,尤其是有些“书/视频,甚至觉得你自己”把指针等价于数组,这在C中是不正确的,根据标准解释只有在特殊几种情况下才会经历到指向其首元素非左值(常量)指针的隐式转换。
注补充知识点:
①只有在特殊几种情况下才会经历到指向其首元素非左值(常量)指针的隐式转换。
②指针是一种对象类型,它引用函数或另一种类型的对象,可以添加限定符。指针亦可以不引用任何内容,这通过一个特定的空指针值指示。
③空指针≠野指针,请不要对这二种指针使用间接运算符-解引用。
注,本文章约定: T为假想类,读者不用细想关于该类,全当他会各种处理,在真正使用时读者请自己用C类型代替。
1.1内存与对象(入门,入门后的也建议看下!)
基础知识指针语法 : 类型 * 指针名;
先看定义声明:
T num=10; //T类型变量
/*T类型指针 初始化为空指针*/
T* needle=NULL;
以上语句声明定义了以下“内存模型”(实际复杂,…号代表省略,其实也有可能是两个对象互相靠近,也有可能差个n个内存):
可以根这图发现变量名有匹配的地址,由高级语言提供的特性之一,细节则由编译器与系统进行处理…,因为这样的话可以省时省力,要是记地址的话非常麻烦如:
如果设10个8位“内存地址”,如一个8位:(010101010),你可能记得住,但是100个呢,你可能讲记本子上,更多的话用二转十进制或十六:0xF7d,0xF6d……
但是如果我们暂时不思考如何组成“16位内存地址”(提示:不绝对int类大小)⁰,例如一个“十六位内存地址”(01100010 00011101)
请问这“16位内存地址”你怎么记?
别说十个光一个就很麻烦记,而且现实你的程序难道只用单位数的int类对象吗,可能太小了,还有为了解决整数溢出(int 存不下的情况下)你会选择用long,那么long类型可能是“32位内存地址”,如果再超你用可能是“64位内存地址”long long……。
所以说一个人是记不住的,所以出现了用英文字母组成的名字来代替“内存地址”,而且不是更方便吗,当然对我们来说中文更好,但是C语句由英语字母组成的,当然也不是不可用中文,但非常不推荐而且讲起来也跑题了。(注(简讲):这些细节就是OS、编译器给予一定抽像,C语言又给我们的抽像为他的语法)。
而且还有几个好处好:“内存地位”会有以系统、平台…发生改变,甚至说就算一模一样的“内存”你程序搬过去,你可以保证该内存没有其他的程序/硬件占用吗,而且每次开关机会“重新成内存地址表……”,而且还有很原因不细讲了,已经快超纲。(这也是OS给的抽像,方便理解、方便使用,相比于直接操作)
总结:为了方便人们学习所以天才们发明了这个方法,使用英文字母代替操作地址,字母代替内存的地址(机器:二进制地址,人看:对应十六进制)让编译器处理,内存管理与操作等让系统处理。¹
注释:⁰不绝对int类大小:
也是因为标准没有绝对定义该类型多大,标准只是确保:
……sizeof(short) <= sizeof(int) <= sizeof(long) <= sizeof(long long)
可以明白吗int可能比short大也可能相等,在不用位4,16,32,64平台都会不同,甚至在相同位平台也会因为系统、编译器等也会导致不同,这个不细节讲(又快超纲了,需要的话会再出一期,专门讲这)。
¹:对1.0节图片下所有内容,补充知识:这些方法,不影响硬件,硬件还是用地址来访问内存位置。
额外的话:抽像这个概念很重要,可以说比知道低层更加实用。(我打算出对抽像简解释文章,不过最近没时间)
1.1指针基础①
接上面二个语句,接下来在来一段:
needle=& num; //赋值,让指向T类型num
这句中&为间接运算符,意为取地址、取出对象的地址。
内存模型:
可以看到:neede内部→指向num外部,这叫指针指向num,或者称neede指针内部存储了num的地址。
总结:
T num=10; //T类型变量
/*T类型指针 初始化为空指针*/
T* needle=NULL;
needle=& num;
以上所有语句为:
①创建T类型needle与num对象,而*决定对象为指针,T类型决定对象可以存储的T类地址值。
②对其needl指针赋值NULL让其为空指针,(无指向对这种指针解引用会未定义行为),这个行为在声明定义时赋值称为“赋初值”。
③接下来把T变量num的地址值使用间接运算符&,取地址赋值给needle,使needle指向num。
1.2指针基础②
上一节1.1介绍了“一重”指针,如何指向(存储),名为num的地址,和NULL,本节用以上内容扩展加上如何使用来讲指针。
1.2.0解引用指针:
上一节介意了&间接运算符-作用:取地址,这节介绍他的亲戚:
*needle;
这句中*为间接运算符,意为解引用、解引用指向对象的地址,该地址上的值。
看程序段:
T num=10; //T类型变量
/*T类型指针 初始化为空指针*/
T* needle=NULL;
needle=& num;
printf("num地址:%p丨num存储的值:%d\n",&num,num);
printf("needle指针的地址:%p\n",&needle);
prinrf ("needle存储的值:%p\n",needle);
printf ("needle存储的值解引用:%d",*needle);
执行可能的结果:
num地址:0xa59ebba4丨num存储的值:10
needle指针的地址:0xa59ebba0
needle存储的值:0xa59ebba4
needle存储的值解引用:10
根据结果,可以发现num地址和needle存储的地址是同一个(第1.1和1.0有讲),needle指针刚开始是空指针(NULL)后被赋num对象地址,称needle指向num对象的地址(0xa59ebba4),而指针本身的也有自己本身的地址(0xa59ebba0)。
给needle解引用会出来num的值,请结合1.0-1.1思考,为什么、不会的请结合模型图,自己试画。
尽量自己画,一定有自己的理解,别人的理解永远是别人的你看了别人理解,只会简单的使用,却不了解他本身。
注意: 可以观察到0xa59ebba0与0xa59ebba4差4但这地址会不一样,像1.0所讲有可能连续也可能差n个,而且在不同平台……也会不同,但num地址和needle存储的值必为同一个,因为needle指针存储的就是num地址详见1.0。
*该代码有一处隐式转换,涉及到(void *)这个后讲。
1.2.1“深”入了解-题目:
有以上知识接下来直接做题较好,请思考、如果理解不了可画图,或敲代码试一下。(本题尾有答案)
①请思考如果我们改变了num值,请问指向num的指针needle会改变吗?
例如num=1;
②请思考如果我们改变了needle值,请问num会改变吗,请问这needle指向谁、对needle解引用值为?
例如
T i=0;
needle= & i;
③请思考以下运行后,i值、num值、解引用needle的值、needle指向。
needle= & num;
*needle =10;
needle=&i;
i=5;
④请思考以下有几处错误,并指出改正(注nu为“局部”变量)。
T nu;
num=1,i=9;
needle=NULL;
needle=i;
printf ("变量i地址%d\n",&needle);
num=nu+(*needle);
printf ("%d+%d=%d",*i,nu,num);
结果:
变量i地址0x……
9+5=14
答案:
点我或可:https://blog.csdn.net/asd2387/article/details/129107668 请莫怪我设粉丝可见。
以下请勿看,暂时为大纲
1.3 const限定符
const是C语句的一个关键词,属于限定符一种,以后本文章简称cv限定符,因还有一个限定符为volatile(一个限制编译器优化的,一般用不到不讨论)。
1.3.0 const基础-对象
对于const语义有基础的请跳过本小节(1.3.0)只讲简单的用法。
由const限定的变量只可读请看程序:
const int a=0;
int const b=1;
这两个变量在整个程序运行时,只可以读取不可更改值,如果进行修改编译器会报错,如(错误的):
a=5;
i=a;
a=i;
以上三句都不可以,因为const限定了变量a,b为“常量”,只可以读取或者对其取地址(可选),注意是双引号的“常量”原因见1.3.2进阶。
1.3.1const基础-指针
对于const与指针了解的请跳过本小节(1.3.1)
int i=1;
const int* q=&i;
int const* w=&i;
以上这俩语句,表达了一样的语义,伪代码解释:
只读 整数类* 指针q 赋i地址;
整数类 只读* 指针q 赋i地址;
这样可以理解了吗,是不是眼前一亮?,没有的话不用担心,接着拆开+辅助词解释:
只读的*指针q
对,就是把“*指针”这句定义为只读了!也就是说把指针解引用操作,定义为只可以读!
也就是说以下使用为错:
*q=2;
*w=i;
*q=*w;
#大部分人跟我一样可能看过其他的文章,上面那二句指针定义讲的大概是,把i设置为只读,这个我不认同的,因为只讲C的语法,我认为const限定符只是对指针 限定的,而不是跳过指针把他指向的对象设置为const,因为明显可以直接修改i。
int* const r=NULL;
整数类* 指针q只读 赋NULL;
int* const i=NULL;
1.3.2const进阶(建议看)
更完1.3.1更↓
const语义仅应用到左值表达式,告诉编译器该对象放在只读内存中。
详解释:
只要在不要求左值的语境中使用 const 左值表达式,就会丢失const 限定符!
const int* const CannontMaodify = NULL;
该CannontMaodify不可以进行任何直接修改,除非使用其他的无c限定符指针对象对CannontMaodify修改,间接修改借一个指针修改。
证明程序链接:个人文章
1.4函数参数与值传递
接下来讲函数的参数与指针关系,我这一章以实用和简单为主所以先讲函数参数与指针,大部分书上应为数组与指针开始这节。
没有学的函数内容的,放心函数不细讲,细讲其实函数内容也很多,只教你指针用于简单的函数参数方法。