当前位置:网站首页>C专家编程 第4章 令人震惊的事实:数组和指针并不相同 4.3 什么是声明,什么是定义
C专家编程 第4章 令人震惊的事实:数组和指针并不相同 4.3 什么是声明,什么是定义
2022-08-04 04:44:00 【weixin_客子光阴】
什么是声明,什么是定义
C语言中的对象必须有且只有一个定义,但可以有多个extern声明。
这里的对象只是跟链接器有关的“东西”,比如函数和变量
定义是一种特殊的声明,它创建了一个对象;
声明简单明了地说明了在其他地方创建的对象的名字,它允许你使用这个名字。
定义 只能出现在一个地方 确定对象的类型并分配内存,用于创建新的对象。例如 int my_array[100];
声明可以多次出现描述对象的类型,用于指代其他地方定义的对象(例如在其他文件里)例如 extern int my_array[].
/*小启发*/
声明相当于普通的声明,它所说明的并非自身,而是描述其他地方创建的对象
定义相当于特殊的声明,它为对象分配内存。
extern对象声明告诉编译器对象的类型和名字,对象的内存分配在别处进行。由于并未在声明中为数组分配内存,所以并不需要提供数组长度的信息。对于多维数组,需要多维数组,需要提供除最左边一维之外其他维的长度---这就给编译器足够的信息产生相应的代码
4.3.1 数组和指针是如何访问的
数组的引用和指针的引用的不同之处首先需要注意的是“地址y”和“地址y的内容”之间的区别。
这是一个相当微妙之处。因为大多数编程语言中我们使用同一个符号来表示这两样东西,然后由
编译器根据上下文环境判断他的具体含义。
X = Y;
在这个上下文环境里,符号X的含义 在这个上下文环境里,符号Y的
是X所代表的地址 含义是Y所代表的地址的内容
这被称为左值 这被称为右值
左值在编译时可知,左值表示存储 右值直到运行时才知,如无特别情况,
结果的地方 右值表示“Y的内容”
图4.1 地址(左值)和地址的内容(右值)之间的区别
C语言引入了“可修改的左值”这个术语。它表示左值允许出现在赋值语句的左边。这个奇怪的术语是为了与数组名区分,数组名也用于确定对象在内存中的位置,也是左值,但它不能作为赋值的
对象。因此,数组名是个左值但不是可修改的左值。标准规定赋值符必须用可修改的左值作为它左边一侧的操作数。用通俗的话说,只能给可以修改的东西赋值。
编译器为每个变量分配一个地址(左值)。这个地址在编译时可知,而且该变量在运行时一直保存于这个地址。相反,存储于变量中的值(它的右值)只有在运行时才可知。如果需要用到变量中存储的值,编译器就发出指令从指定地址读入变量值并将它存于寄存器中。
这里的关键之处在于每个符号的地址在编译器时可知。所以,如果编译器需要一个地址(可能还需要加上偏移量)来执行某种操作,它就可以直接操作,并不需要增加指令首先取得具体的地址。相反,对于指针,必须首先在运行时取得它的当前值,然后才能对他进行解除引用操作(作为以后进行查找的步骤之一)。
//4.2
char a[9] = "abcdefgh"; c = a[i];
编译器符号表具有一个地址9980
运行时步骤1:取i的值,将它与9980相加
运行时步骤2:取地址(9980+i)的内容
数组的下标引用
extern char a[]和extern char a[100]等价的原因。这两个声明都提示a是一个数组,也就是一个内存地址,数组内的字符可以从这个地址找到。编译器并不需要知道数组总共有多长,因为它只产生偏离起始地址的偏移地址。在从数组提取一个字符时,只要简单地将符号表显示的a的地址加上下标,需要的字符就位于这个地址中。
//4.3
extern char *p, 它将告诉编译器p是一个指针(在许多现代的机器里它是4字节的对象),它指向一个字符。为了取得这个字符,必须得到地址p的内容,把它作为字符的地址并从这个地址中取得这个字符。指针的访问要灵活的多,但需要一次额外的提取。
char *p; c = *p;
编译器符号表有一个符号p,它的地址为4624
运行时步骤1:取地址4624的内容,就是‘5081’
运行时步骤2:取地址5081的内容。
指针的下标引用
//4.4
4.3.2 当“定义为指针,但以数组方式引用”时会发生什么
需要对内存进行直接的引用,但这时编译器所执行的却是对内存进行间接引用。之所以会如此,是因为我们告诉编译器我们拥有的是一个指针。
char *p = "abcdefgh"; c = p[i];
编译器符号表具有一个p,地址为4624。
运行时步骤1:取地址4624的内容,即‘5081’。
运行时步骤2:取得i的值,并将它与5081相加。
运行时步骤3:取地址[5081+i]的内容。
对指针进行下标引用
//进行4.4的操作
char *p = "abcdefgh"; ...p[3]
//进行4.2的操作
char a[] = "abcdefgh"; ...a[3]
extern char *p; p[3]; /*进行4.4的操作 */
/*编译器将会执行如下操作:
*取得符号表中p的地址,提取存储于此处的指针。
*把下标所表示的偏移量与指针的值相加,产生一个地址。
*访问这个地址,取得字符。
*/
/*编译器已被告知p是一个指向字符的指针(相反,数组定义告诉编译器p是一个字符序列)。
*p[i]表示“从p所指的地址开始,前进i步,每步都是一个字符(即每个元素的长度为一个
*字节)”。如果是其他类型的指针(如int或double等),其步长(每步的字节数)也各不相同
*/
/*既然把p声明为指针,那么不管p原先是定义为指针还是数组,都会按照上面所示的3个步骤
*进行操作,但是只有当p原来定义为指针时这个方法才是正确的。
*/
//声明
extern char *p;
//定义
char p[10];
/*这种情况用p[i]提取这个声明的内容是,实际上得到的是一个字符。
*但按照上面的方法,编译器却把它当做一个指针,把ASCII字符解释
*成地址显然是牛头不对马嘴。
*/
边栏推荐
- XSS related knowledge points
- 深度学习21天——卷积神经网络(CNN):实现mnist手写数字识别(第1天)
- 7-2 LVS+DR Overview and Deployment
- 路网编辑器技术预研
- How class only static allocation and dynamic allocation
- Learn iframes and use them to solve cross-domain problems
- OpenGL绘制一个圆锥
- sql语句查询String类型字段小于10的怎么查
- Cache pool of unity framework
- System design. Seckill system
猜你喜欢
随机推荐
Structure function exercise
外卖店优先级
拿捏JVM性能优化(自己笔记版本)
Implementing a server-side message active push solution based on SSE
JVM Notes
21 days learning challenge 】 【 sequential search
图像处理之Bolb分析(一)
Towards Real-Time Multi-Object Tracking(JDE)
帮助企业实现数字化转型成功的八项指导原则
10 Convolutional Neural Networks for Deep Learning 3
元宇宙“吹鼓手”Unity:疯狂扩局,悬念犹存
小程序 + 电商,玩转新零售
Bolb analysis of image processing (1)
Embedded database development programming MySQL (full)
数据集类型转换—TFRecords文件
Shell 函数
如何简化现代电子采购的自动化?
【id类型和NSObject指针 ObjectIve-C中】
mysql索引笔记
The Shell function