找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 5016|回复: 42
打印 上一主题 下一主题
收起左侧

关于C语言的指针,与教材不一样的解释

  [复制链接]
跳转到指定楼层
楼主
C语言中为什么要设计指针这个东西,这个东西的设计原理是什么,本来就是个很简单的东西,小学生思维就够了。还有,这些原理之类的,对于使用C来编程的人来说,也是必须完全了解的,了解的深度与你编程时的轻松度密切相关,所以,弄清这个对你很重要。

总有人在问指针是干什么用的,那我先说说我的理解:获取某个数据所在单元的地址值,由此推算出其它数据所在单元的地址值,以用于找到这个数据所在的存储位置。
计算机编程,可以说,基本就是对存于存储器中的数据进行操作,这就牵扯到一个最重要的问题:你现在要操作的数据,存在哪个地方?
想要弄清这个问题,最根本的方法,是了解芯片的设计和工作基本原理与基本结构组成。当然,如果知道存储器的大概结构,也算是有了一个基础。这里我不讲这么多,因为学单片机的人,基本的知识应该还是有的,不是那种不与硬件打交道的程序员,所以不需我多讲。

C与汇编有两个根本的区别,造成了C中指针的产生:一个是数据所存储的地址是由C编辑器自动分配的,程序员没自主权,所以一个数据存储的地址,程序员是不知道的;二是C编程中所用到的数据的长度与存储单元容量的不一致导致一个数据可能会占据数个存储单元,由此造成数据存储地址值的不连续,这个现象造成的原因在于,不管你是8位机、16位机还是其它位机,一个存储单元的位数都是8位,这个是芯片设计就如此,原因自然主要是兼容问题而不得不做出的选择。本段所述的现象,在下面的比喻之中会进行解释。

C语言编写的程序,在程序运行中,你所要处理的数据存在哪,你是必须知道的,否则你找不到这个数据在哪,你的程序就没法编下去了;如果你把数据所在的位置给弄错了,那这个程序在运行中就得不到你想要的结果。

有关数据存在哪,及如何找到这个位置,我打如下一个比方:
我们知道,宾馆的房间都是有编号的,而且是用数字来编写的,很统一,一个房间一个编号;每个房间的大小都是一样的,也就是说其容量是一样的。宾馆来了一家一家的,住哪间?宾馆前台来安排,一家人数少,可能就是一间,人数多了,可能就得几间了,也就是说,同样是一家,可能他只占据一个房间编号,也有可能占据数个房间编号(这就造成了地址编号的不连续),这个数据,只有前台知道。如果你想去找到哪一家,你怎么找?你一个房间一个房间地去看单子也好看现场也好,都不是个办法,一般情况下,每一家都是有个名字的,如张家、李家、王家、陈家等等的,你告诉前台,前台根据这个名字查一下,就会把这家的第一个房间的编号(首地址)给了你,而你在事先知道他们家人数及宾馆每间房能住的人数的情况下,也就知道了他们家占几间房。这个比方里,与C语言中的情况极其相似,就是存储内容物的位置都是用统一数字编号来标识的,存储内容的每一个单元(房间)的容量都是固定一致的,每一个(组)数据(一个家庭)都是有一个名字的,单个数据的长度(某个家庭人数)你事先是知道的,内容物的存储位置分配是由他人分配而你事先是不知道的,其占据几个存储单元数量你是事先知道的。

同样的原理,在C语言中,要找到一个数据的首地址,我们先要把这个数据的名字给编辑器(前台),编辑器就会依据这个名字把这个数据存储单元的首地址给你,这个动作,就是在这个数据的名字前面加一个“&”,其首地址值就出来了。
然后这里就又有了一个问题,这个查出的地址值,放在什么地方?宾馆是可以将地址值写在纸上,那计算机呢?它能放这个数据的地方,还是只有存储器,当然,你也可以为存储这些个地址而单独在芯片中设计一个专门的区域,那你可以算是创新,但是,现有的芯片怎么办?没办法,还是只能存于现有的存储器之中,于是,C编辑器还得给一个房间(存储单元)专门用于存储这个地址值,然后,这个单元编号是多少?程序员又不知道了,因为又是编辑器自动分配的,所以,没法,又得给这个取个名字以便查找利用,所以,在将要存入的内容存入之前,得先对要存入的内容进行定义(取名)一个变量,这样我们以后就可以用名字直接找到这个内容了。

下面就谈谈C中有关获取地址与使用地址的一些规则,有关这个概念的名词,现有教材都称之为指针,本人对这个名词比较不感冒,这个名词显然没能与“地址”一词紧密联系,于是不易理解不易记住(现有计算机教材中,类似槽点很多):
存放入单元的内容要要先取个名字,也就是所谓的定义(前面已经解释过),这个因为指针变量的功能不同,所以定义变量名时,也要给出区别,让编辑器知道这个变量的用途,也让读程序的人知道这个变量是用于存储地址的,也就是所谓的指针变量。其进行区别的规则是变量名前面加一个*”以示区别,如果不加会怎么样?你照样可以将取得的地址值存入其中,但是,你用这个变量去取这个地址所指向数据时,程序在编译时就报错了,这个现象,只是编辑器的硬性规定,也是有道理的。其实编辑器在分配指针变量在存储器中的位置时,也没做什么特殊处理,它与其它变量都是统一分配地址的,基本也是顺序分配,谁先使用谁先排前面(不是谁先定义谁先排前面),一点也不特殊。

在使用指针来取得存储单元中的数据时,C的规定也是在指针变量名前加一个“*”以示这个是用地址值来取得数据,如a=*p,它的含意是:P所表示的存储单元中的数据,是一个地址值,a=*p就是把这个地址值所指向的单元中的数据送给a。其实吧,个人觉得这个“*”符号用得不好,改为“@”可能更让人容易理解,这个“@”有“在”的意思,然后我们就可以理解为“把在这个p值所指向的单元中的值,把它给a”。如果是这样,很多运算公式就很容易理解了,如@p++就是操作“在”p值为地址所指向的单元中的数据,这个具体操作就是将其加1,而p++就是p值加1个数据长度值以将p值变为下一个数据的地址值(不是地址值本身直接增加1,这个在后面再解释),也就是说,只要在p前面加了@,那要操作的对象就并不是p本身,而是它所指向的单元,这个一定要牢记不是操作p本身,这样你在编程时就不容易出错了。

指针变量是可以直接赋一个常量值的,但是,这个值必须是你所要用到的单元的地址值,如果不是,程序编译时不会报错,但程序运行时肯定是乱了,也就是说,前提是你知道这个数据的绝对地址值。

接下来的一个问题,就是指针变量的长度,既然是表示地址,那它就是一个没有负号的整数,其长度最长也就是存储器地址值的长度,如51机的RAM,其地址就只有八位,所以,在定义时,它的指针长度也就一个单元,多了浪费。
还有一个规则,指针变量的加减运算,是与其所指向的数据的长度绑定的,这个是在编译处理时用到的,意思是其尽管这个指针变量也是一个数据,但它在做加减运算时,例如p=p+1,并不是p值直接加1,而是加一个其所指向的数据的长度址,以直接变为下一个数据的首地址值,这个处理,并不是在程序运行中进行的,而是在编译时进行的,所以指针变量在使用之前一定要先定义,定义之后,编辑器才会知道这个是个指针变量,然后这样做处理。
还有一个教材中没说清楚的问题,这个所谓的指针,到底是只针对RAM,还是包括ROM?这个有兴趣的朋友可以研究一下,方法也不难。
以上所说,其实都可以用编辑器调试中的汇编窗口来证实。
以上纯属个人理解,如有错误与不当,万请指出。

评分

参与人数 2黑币 +95 收起 理由
admin + 80 共享资料的黑币奖励!
xiaoshan7748 + 15 说的很通透

查看全部评分

分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏19 分享淘帖 顶8 踩
回复

使用道具 举报

沙发
ID:266429 发表于 2023-4-20 20:06 | 只看该作者
C的指针问题,对于汇编来说,其实是个非常简单的问题,就是用地址值来寻址的方式。当然,在现有的汇编教材中,也没有具体的说什么用地址值来来寻址的这种说法,在汇编中,除了立即寻址方式,其它的方式,都是用到地址值的寻址方式。事实上,只要是用到RAM,就得知道其地址才能进行存取操作。
说起来,在C中,C的指针的操作,都只是取操作,前面已经说得很清楚了,其存操作,是由编辑器来进行的,程序员无权直接操作。也当然,如果程序员知道编辑器的分配规则,那么程序员想把某数在编辑器分配时分配在某单元中,也是可以做到的,这个就是对芯片和C通透了。
回复

使用道具 举报

板凳
ID:266429 发表于 2023-4-20 20:48 | 只看该作者
这个帖子内容,如果配上汇编窗口的实验,应该也是一篇不错的文档吧?
回复

使用道具 举报

地板
ID:266429 发表于 2023-4-20 21:27 | 只看该作者
知道了指针的原理,那我们完全可以在编程时很容易地确定任一数据的存储地址,以及任一数据中某一字节的地址,哪怕各数据的字节长度并不一致。
回复

使用道具 举报

5#
ID:883242 发表于 2023-4-20 22:04 | 只看该作者
C与汇编有两个根本的区别,造成了C中指针的产生:一个是数据所存储的地址是由C编辑器自动分配的,程序员没自主权,所以一个数据存储的地址,程序员是不知道的;

这话说明你的汇编没学好。以Keil C51自带的A51汇编器为例:

  1. Datum EQU 50h
复制代码

这是大部分人常用的把变量Datum定义到50h地址上,严格地说是错误的。正确的写法是这样:
  1. Datum data 50h
复制代码

但是这个写法把Datum定义到50h地址上,如果是纯汇编编程那么没有问题,如果是汇编和c语言混合编程,这种写法会和BL51.exe分配内存的方法发生冲突,不会出现编译错误,也不会出现运行错误,但是可能会浪费RAM空间,不能实现资源的高效利用。那么最合理的写法是这样的:
  1. Datum ds 1
复制代码
给Datum变量保留一个字节的空间,在编译期间,编译器也是不知道Datum地址是多少,只有最后连接完成,才能确定,可以查看连接器生成的map文件来知道Datum的地址到底是几。


评分

参与人数 1黑币 +5 收起 理由
IdeaMing + 5

查看全部评分

回复

使用道具 举报

6#
ID:401564 发表于 2023-4-20 22:10 | 只看该作者
纯技术问题不要搞这种花里胡哨的东西,对于学习意义不大
什么"老王教你用示波器","七天学会单片机"......之类的,往往是没什么用的,说它不行,它好像有点用,说它行,好像用处不大
老外的书就简单,往往枯燥,生涩难懂,但往往能真正的学到东西,很多时候能当工具书来查资料
这是对指针的描述,几行代码,加黑字,很快就能理解指针了


回复

使用道具 举报

7#
ID:8222 发表于 2023-4-20 22:29 | 只看该作者
这个更通俗易懂。用@更容易理解,但现在都在用*,干脆就把*当@吧。
回复

使用道具 举报

8#
ID:146878 发表于 2023-4-20 22:36 | 只看该作者
指针的根源是接寻址寄存器,没有这个寄存器的MCU无法用指针。
回复

使用道具 举报

9#
ID:883242 发表于 2023-4-20 22:40 | 只看该作者
pdwdzz 发表于 2023-4-20 22:36
指针的根源是接寻址寄存器,没有这个寄存器的MCU无法用指针。

有indirect/index register也不好用,比如51强行上指针代码密度和执行效率都是直线下降,得不偿失。
回复

使用道具 举报

10#
ID:970057 发表于 2023-4-21 09:52 | 只看该作者
其实就是间接寻址方式,给这么多教材解释得绕来绕去
回复

使用道具 举报

11#
ID:266429 发表于 2023-4-21 11:59 | 只看该作者
Y_G_G 发表于 2023-4-20 22:10
纯技术问题不要搞这种花里胡哨的东西,对于学习意义不大
什么"老王教你用示波器","七天学会单片机"......之 ...

我写的是有关单片机底层的工作原理和运行过程的东西,是比较接近其本质的东西。如果不懂这些,在C中那有些动作可能是不知道如何去做的,比如说,结合指针操作把一个四字节变量中的第三个字节的内容快速单独地读出来。
在单片机运行程序中,i=*p,这个语句执行时操作的数据并不是p本身,而是另一个数据:这个数据的首地址是p所指向的单元。
指针的本质是操作地址,所以,了解单片机芯片存储结构及地址之类的概念,才能从底层上深入了解指针。
回复

使用道具 举报

12#
ID:883242 发表于 2023-4-21 12:41 | 只看该作者
真要研究指针,怎么也要找个正经的c语言编译器吧,如STM32用的三大编译器IAR、GCC和MDK,找个天生残疾的%的不正规c语言编译器搞指针是%吗?
回复

使用道具 举报

13#
ID:401564 发表于 2023-4-21 13:35 | 只看该作者
慢慢思考 发表于 2023-4-21 11:59
我写的是有关单片机底层的工作原理和运行过程的东西,是比较接近其本质的东西。如果不懂这些,在C中那有 ...

1: 汇编不需要C指针的功能,用法不一样
2: 汇编的指针在不同单片机中,用法是不一样的,你说的只能是8051的汇编,别的汇编用的不是这方法,像PIC的汇编压根就没有像8051这种指针寻址方式,它是由固定文件寄存器来进行操作的,你的说法很容易误导别人
3:C语言很少有人去动内存,都是由编译器自动分配,闲着没事干才会去动内存
回复

使用道具 举报

14#
ID:1073144 发表于 2023-4-21 14:14 | 只看该作者
指针的根源是接寻址寄存器
回复

使用道具 举报

15#
ID:73992 发表于 2023-4-21 18:49 来自手机 | 只看该作者
c51地址指针xdat区是两个字节。变量的值存储位置是编译器决定的,但程序员要存储的值是可以存到指定位置的。开机时候需要清理64k内存,如果没有地址指针,程序员就需要写64k行代码。0000h=0,,,,ffffh=0  .有地址指针只需要地址加一。
回复

使用道具 举报

16#
ID:1073299 发表于 2023-4-22 15:04 来自手机 | 只看该作者
一句话,指针就是内存地址,然后通过地址取这个地址的数据,说一大堆没用的云山雾绕的
回复

使用道具 举报

17#
ID:266429 发表于 2023-4-24 09:42 | 只看该作者
Hephaestus 发表于 2023-4-20 22:04
C与汇编有两个根本的区别,造成了C中指针的产生:一个是数据所存储的地址是由C编辑器自动分配的,程序员没 ...

关于EQU是什么意思有什么用法,看下面一段程序:
  tryy EQU 0A0H
  abyte EQU 0A0H
  abit EQU 0A0H

ORG tryy
START:
   MOV tryy,#tryy
   MOV abyte,#tryy
   MOV C,tryy
   MOV C,abit
DB  1,2,3,4,tryy
ORG 0B0H  
   ddat: ds tryy
   ACALL tryy
END

伪指令EQU如果用于分配存储单元,那它是用于RAM;伪指令data用于RAM;伪指令ds则是用于ROM。
我的主贴最后有一段话,指针究竟是只针对RAM,还是包括ROM?
回复

使用道具 举报

18#
ID:883242 发表于 2023-4-24 12:42 | 只看该作者
慢慢思考 发表于 2023-4-24 09:42
关于EQU是什么意思有什么用法,看下面一段程序:
  tryy EQU 0A0H
  abyte EQU 0A0H

老老实实看c51的帮助文件吧,你的回复就没有任何正确的地方。

EQU就是简单的替换,相当于c语言的#define,如果纯汇编那么可以用,因为内存分配完全是你的责任,发生冲突也跟编译器无关。如果汇编和c混合编程那么绝对不可以使用EQU定义变量,因为这么做连接器BL51.exe不知道这些地址已经被占用,会发成冲突。

ds就是用于RAM,可读可写,用于ROM是很不合理的,让编译器分配一个字节内容未知也不能写的ROM你想干什么?
回复

使用道具 举报

19#
ID:266429 发表于 2023-4-24 15:05 | 只看该作者
Hephaestus 发表于 2023-4-24 12:42
老老实实看c51的帮助文件吧,你的回复就没有任何正确的地方。

EQU就是简单的替换,相当于c语言的#defi ...

北航2012年出版的《单片机系统设计基础》中强调DS及DW只能用于程序存储器,不能用于数据存储器,这是目前我找到的唯一说明了DS应用存储目标的书。
我的其它的错误,还是希望一条条地说明,有讨论就会有进步。DS在编程中有什么实际用途,的确如你所说,本人也困惑。
回复

使用道具 举报

20#
ID:709761 发表于 2023-4-24 16:12 | 只看该作者
编程这么久了,也没感觉到指针有多么厉害
回复

使用道具 举报

21#
ID:883242 发表于 2023-4-24 18:28 | 只看该作者
慢慢思考 发表于 2023-4-24 15:05
北航2012年出版的《单片机系统设计基础》中强调DS及DW只能用于程序存储器,不能用于数据存储器,这是目前 ...

你直接看Keil自带的帮助文件好了,中文的资料都是%为了晋升%灌水出来的,毫无价值的%。
回复

使用道具 举报

22#
ID:1074328 发表于 2023-4-26 23:16 | 只看该作者
很少有人把这个讲的这么好了
回复

使用道具 举报

23#
ID:888585 发表于 2023-4-27 08:34 | 只看该作者
题主讲的不错,对于初学者来说是很有用的
回复

使用道具 举报

24#
ID:402993 发表于 2023-4-27 20:15 | 只看该作者
讲得很明细。
回复

使用道具 举报

25#
ID:291668 发表于 2023-4-28 13:26 | 只看该作者
争论的焦点就是指针的用途。我理解指针就像上学时的楼管阿姨,手里有一大串钥匙,对应每个宿舍门,至于里边住几个人,从哪里来的。她不关心。只要找到能打开这扇门的钥匙就行了。
回复

使用道具 举报

26#
ID:689425 发表于 2023-5-4 09:26 | 只看该作者
li64331218 发表于 2023-4-28 13:26
争论的焦点就是指针的用途。我理解指针就像上学时的楼管阿姨,手里有一大串钥匙,对应每个宿舍门,至于里边 ...

很形象
回复

使用道具 举报

27#
ID:721848 发表于 2023-5-10 11:53 | 只看该作者
比较难懂,不好理解
回复

使用道具 举报

28#
ID:1079700 发表于 2023-5-24 21:59 | 只看该作者
讲的很好
回复

使用道具 举报

29#
ID:1083793 发表于 2023-6-13 14:12 | 只看该作者
讲的很清楚,新手可以看一看。
回复

使用道具 举报

30#
ID:333446 发表于 2023-7-12 11:57 | 只看该作者
c语言引入指针是为了程序设计简单,因为cpu里可以间接寻址,这样在类似查表之类的操作时,直接把间接寻址寄存器加减相应的值就行了。可以快速取得数据地址。
回复

使用道具 举报

31#
ID:73833 发表于 2023-7-19 14:51 | 只看该作者
指针就是钟表上的指针,指向哪里就可表示出哪里的数字。比如,指针指向3点钟那里(存放3这个数据的地址),人们就知道是3点(3,就是存放3 de地址里 de 数据)
回复

使用道具 举报

32#
ID:73833 发表于 2023-7-19 14:52 | 只看该作者
wydev 发表于 2023-4-22 15:04
一句话,指针就是内存地址,然后通过地址取这个地址的数据,说一大堆没用的云山雾绕的

言简意赅
回复

使用道具 举报

33#
ID:475306 发表于 2023-7-28 08:47 | 只看该作者
wydev 发表于 2023-4-22 15:04
一句话,指针就是内存地址,然后通过地址取这个地址的数据,说一大堆没用的云山雾绕的

都快让他们搞蒙了,刚入学
回复

使用道具 举报

34#
ID:123289 发表于 2023-8-25 15:49 | 只看该作者
【北航2012年出版的《单片机系统设计基础》中强调DS及DW只能用于程序存储器,不能用于数据存储器】
个人不同意这个说法。
回复

使用道具 举报

35#
ID:1096033 发表于 2023-10-18 10:20 | 只看该作者
解释的挺好
回复

使用道具 举报

36#
ID:1098507 发表于 2023-11-5 13:18 | 只看该作者
好资料,51黑有你更精彩!!!
回复

使用道具 举报

37#
ID:1105351 发表于 2023-12-20 13:56 | 只看该作者
看懂啦!感谢楼主
回复

使用道具 举报

38#
ID:1088185 发表于 2024-1-6 17:19 | 只看该作者
也许从应用倒过来理解"为什么要用指针"更加容易让你了解指针。

比如就不让你用指针写C代码, 可以吗?
不用指针的话有什么功能是做不到的?
有什么功能不给你用指针做就效率大大的降低?

能正确的回答以上问题,你才算懂指针是个啥玩意儿。
回复

使用道具 举报

39#
ID:311731 发表于 2024-2-4 18:47 | 只看该作者
东莞朋友 发表于 2023-4-21 09:52
其实就是间接寻址方式,给这么多教材解释得绕来绕去

就是汇编语言的间接寻址
回复

使用道具 举报

40#
ID:123036 发表于 2024-2-5 10:35 | 只看该作者
wydev 发表于 2023-4-22 15:04
一句话,指针就是内存地址,然后通过地址取这个地址的数据,说一大堆没用的云山雾绕的

同意。一大堆去说间接/寻址/寄存器。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表