当前位置:网站首页>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字符解释
*成地址显然是牛头不对马嘴。
*/
边栏推荐
猜你喜欢
PL/SQL Some Advanced Fundamental
什么是数字孪生智慧城市应用场景
7-3 LVS+Keepalived Cluster Description and Deployment
【机器学习】21天挑战赛学习笔记(一)
【21天学习挑战赛】图像的旋转问题(二维数组)
day13--postman接口测试
drools从下载到postman请求成功
Deep learning -- CNN clothing image classification, for example, discussed how to evaluate neural network model
2003. 每棵子树内缺失的最小基因值 DFS
42. 接雨水
随机推荐
XSS related knowledge points
Introduction and application of go module
Turn: Management is the love of possibility, and managers must have the courage to break into the unknown
8.Haproxy 搭建Web集群
Enterprise live broadcast is on the rise: Witnessing focused products, micro-like embracing ecology
7.LVS负载均衡群集之原理叙述
如何打造一篇优秀的简历
软件测试如何系统规划学习呢?
There is an 8 hour difference between the docker installation of mysql and the host.
系统设计.如何设计一个秒杀系统(完整版 转)
文件系统的简单操作
Deep learning -- CNN clothing image classification, for example, discussed how to evaluate neural network model
2022 software test interview questions The latest ByteDance 50 real interview questions, 15k have been won after brushing, with explanation + Q&A
学会iframe并用其解决跨域问题
Structure function exercise
Tensors - Application Cases
深度学习环境配置
劝退背后。
基于 SSE 实现服务端消息主动推送解决方案
DataTable使用Linq进行分组汇总,将Linq结果集转化为DataTable