【新手向】一些C/C++ 与指针有关的知识简单的衍生

本文面向编程初学者,详细解释了C/C++中的指针概念及其作用,通过内存管理的角度阐述了指针的重要性。文章讨论了指针如何存储变量地址、数组和函数指针的使用、以及指针在函数参数传递中的特殊性。此外,还探讨了指针在堆内存管理、数据结构实现等方面的应用,旨在帮助初学者理解指针的本质和用途。
摘要由CSDN通过智能技术生成

(本人能力有限,纯新手向主要是面向那些刚学编程没多久对编程感兴趣的萌新,大佬就没必要花时间看了。当然也欢迎各路大佬来指正小破文不足的地方)
也算是接触了编程有一段时间了,记录一下心得。
当年读C++ primer最头疼的就是无疑就是指针这一章,感觉指针这个东西没啥用处而且十分玄学(毕竟当时对内存的认识只是知道计算机里有个内存条而已)。后来随着指针在书中后面的章节出现频率越来越高我对指针的恐惧也日益加深。可以说,指针是我当年学编程最大的障碍。
直到现在我看到的很多编程初学者也很难理解指针变量的储存形式和作用,于是就想写这篇文章说说现在的我对指针的理解,也算是给当年的自己一个答案吧。。。


指针是什么

这个问题书上说的很清楚,指针是储存变量的地址的变量。那么变量地址是什么呢?这个问题很少有编程书解释的很清楚。变量地址就是变量在计算机里存储的位置,大多数初学者只能得到这个笼统的回答。于是,我们记住了指针是变量在计算机里存储的位置。然而当我们看到:

int num[3] = {1,2,3};
int * p = num;//指向数组的指针
int fun(){
     return 0;
}
int main(){
     int (*  p)() = fun;//指向函数的指针
     printf("%d",p());
     return 0;
}
void fun(int *p){
        *p = 2;
}
int main(){
     int *p ,num = 1;
     p = #
     fun(p);//通过函数改变参数的值
     return 0;
}

当我们看到这些代码的时候,我们又开始怀疑人生。指针不是变量地址吗,凭什么数组也是变量就不需要取地址,函数难道也是变量吗,为什么将指针作为形参传递到函数里面就能改变实参的值非指针就不能。而一般的编程初级教程上很难找到这些问题的答案。接着我们就开始怀疑变量地址到底是什么,真的就只是变量储存的位置这么简单吗。这个时候估计很多非计算机专业的同学已经准备开始弃坑了。确实,网上很多“大学学了冷门专业,毕业报班4个月编程学XX语言出来月入10万”广告中这个XX语言很少有是C或者C++的。当然这个问题原因肯定不是因为指针太难学(当然java的确没有指针这个说法),但是我觉得这这两个现象的出现有一个共同的原因:
C与C++语法与计算机程序的运行原理有紧密的联系
因此,对一些完全不了解计算机运行机理的初学者(只是凭多年玩游戏看配置配电脑的那点知识是肯定不够的)很难理解一些概念。这个特点也导致了C/C++算是主流高级编程语言中比较硬核的,不同于java,python之类可以随便学一下就马上可以上岗写一些不太复杂的代码。
有点扯远了,让我们切回正题。指针之所以难以理解正是因为它与内存管理紧密联系。我个人认为指针之所以在C与语言中很重要是因为它极大拓宽了程序管理内存的能力。想要彻底理解指针的本质必须从程序是怎么使用内存的说起:

程序是怎么使用内存的

“程序将变量产生的临时数据放到内存中,需要长期保存的数据放到磁盘中 ”我当年就是这么天真的认为的。事实上实际情况比这稍微复杂一点。。。
(在这里我们不讨论内存分页机制,操作系统多程序同时运行,多线程这些复杂情况,只讨论单线程,一个程序,一块内存)首先我们要知道的是我们运行一个程序的所有资源都是CPU从内存上取得的。这里的资源包括指令也就是我们写的代码(当然是经过编译器翻译成机器码的)。这和我们想象中内存只是数据的中转站有点不太一样。事实上我们的CPU会一边从内存中把指令取出来一边根据指令将计算出来的数据写到内存里面去。这样就诞生了一个问题:如果CPU不小心取到了别的数据而不是指令怎么办呢?解决办法很简单:指令放在一块区域,数据放在另一块区域两块区域隔离开来,CPU要取指令只去专门放代码的区域,CPU改数据就去专门的数据区域去改。这样就有了我们的内存分段

内存分段机制

顾名思义将内存分成各种不同功能的区段,每个区段放对应功能的数据。总体来说内存被分为了4个区段:数据(Data)段,代码(Code)段,堆栈(Stack-Heap)段和附加(Extra)段
其中代码段最好理解,就是我们前文所提的指令数据所放置的区段。数据段稍微有点复杂,可能我们直观感受是数据段是我们代码中所有变量数据所放的位置,而实际上我们变量数据是堆栈段和数据段共同存储的。这个我们稍后会讨论。附加段我不是很了解,为了避免误人子弟我就不过多叙述了。。。(太菜了)。
分好段把数据放到内存里,接下来我们要做的就是要访问内存了。然而内存里全都是0和1的数据,没有什么标记能辨别出我们想要的数据。比如下面这段内存
00001010 11101001 00001111
10010011 11001010 00101001
我们想要访问一个叫做num的unsigned int(32位)变量数据我们该如何知道这48位的数据中哪32个我们想要的呢。
将每8个数据编成一组从0开始给它们一个组编一个号。要想找数据的时候就去找相应的号码,这个号码便是我们的地址。比如:
0:00001010
1:11101001
2:00001111
3:10010011
4:11001010
5:00101001
如果我们告诉cpu我们num变量的地址是1,cpu很容易就能找到11101001 00001111
10010011 11001010。这就是我们要找的num的值接下来就是把它翻译成十进制就能得到我们看到的数值了
事实上这就是我们编译器在编译程序的时候做的事情。我们用高级语言书写代码,声明变量的时候只知道变量的名字而不知道变量的地址值,而cpu在执行程序的时候只知道变量的地址并不知道变量的名字,给变量名字分配地址的过程是由编译器完成的。基本上,我们可以认为我们所声明的每一个变量,编译器都在为我们找了一块大小与变量类型相当大小的内存。(实际上编译器会把可预见值的变量忽略)我们修改变量就是在修改内存!这块内存的编号便是我们变量的地址。
看到这里我们应该很清楚指针变量到底是做什么的了
比如还是这块内存
0:00001010
1:11101001
2:00001111
3:10010011
4:11001010
5:00101001
我们在别的地方声明了一个很特别的变量p它存储的数据是num的地址也就是
(假设p存储在6,实际上指针都是32位的数据这里为了方便只写8位)
6:00000001
这个时候我们就称 p是指向num的指针
在赋值时我们的 p = &num实际上就是&num运算计算出num的地址是00001,接下来将1放到p所对应的内存地址6上,当我们(*p) = 2的时候,(*p)运算符实际上就是将p所对应地址6的数据取出(00000001)再根据数据00000001找到地址1处的num将值2赋给num。
(PS:顺便一提 *符号在 不同场合下有完全不同的意思:

 1. int *p;//*是声明指针变量的标志表示我们声明了一个int类型的指针 
 2.*p = 10;//这里*是解引用运算符意思是找到指针p保存的地址对应的数据并将它修改成10
 3.m = 10 * 10;//这里*只是普通乘法符号

这三种情况下*符号意思完全不同,可以把它们理解为三个不同符号)
因此指针能间接修改变量的值
那么为什么能将数组名字赋给指针呢
我们再看一下第一个例子

int num[3] = {1,2,3};
int * p = num;

我们已经知道变量的名字p一般在编译的时候会被翻

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值