鱼C论坛

 找回密码
 立即注册
查看: 1950|回复: 1

[技术交流] 这些年C语言中被误解的数组和指针

[复制链接]
发表于 2022-11-13 20:45:14 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
本帖最后由 xiaotubie 于 2023-11-7 16:31 编辑

误解1:指针就是地址。
指针是一种数据类型,包含了指向对象的数据类型和地址,单一个地址是无法知道该地址空间上存的是什么东西,到底是int类型数据还是其他类型。
通过间接访问运算符*,也不知道怎么访问该内容,是提取1个字节当做char类型数据,还是说提取4个字节当做int类型数据。
ISO的C标准中这样定义的:A pointer type is a complete object type.


误解2:&a 是一个地址
int a=1;
int *p=&a;
很多人都会说这里通过&a获取到对象a的地址,然后赋值给指针变量p,乍看也没什么问题。但是其实&a就是一个指针,这里是将指向a的指针赋值给int*类型的指针变量p。
如果你这样写int p=&a; 编译器提示无法将int* 类型初始化给int类型实体,&a本身就是一个int*类型指针,不是因为赋值给int* p以后才变为指针。
ISO的C标准中定义:The unary & operator yields the address of its operand. If the operand has type "type", the result has
type "pointer to type".
C++ Primer中是这样说的:通过&地址运算符,获得指向对象的指针
Milo Yip (叶劲峰) C++Primer中文版的审校者,也明确解释了&a是地址这种说法是错误的。
当然如果理解了指针是怎么回事,怎么叫也无所谓,就怕很多初学者误解.


误解3:数组名是地址、数组名是指针
int  a[2]={7,8};
申明并初始化一个数组a,   数组名a是该数组对象的标识符。就像我们如果定义int b=1;  那么b就是这个int类型对象1的标识符。给这个1对象取了一个名字b,代表这个对象,这块空间。
为什么数组名a会被很多人误认为地址或者指针呢?  因为当数组名表达式,通常都会转换为数组首元素的指针。
int n=a[0];  这个时候a就会转换成首元素指针。当然也有不转换的时候,譬如用于sizeof和&,这个时候数组名a因为没发生转换,代表的原来的意思:数组对象。
所以sizeof输出a数组的大小,&a则是指向数组a的指针。


误解4:数组名不是左值

很多人认为左值应该就是可以修改的,而数组名是无法重新赋值的,所以肯定不是一个左值。
ISO的C标准中这样定义的左值:An lvalue is an expression (with an object type other than void) that potentially designates an
object;68) if an lvalue does not designate an object when it is evaluated, the behavior is undefined.
When an object is said to have a particular type, the type is specified by the lvalue used to designate
the object. A modifiable lvalue is an lvalue that does not have array type, does not have an incomplete
type
大概就是左值是标识为对象的表达式,数组属于不可修改的左值,也就是左值其实分了可修改左值和不可修改左值。当然因为是一个不可修改左值,自然无法用于赋值=  ++   --等运算。
在C标准的一行小字中解释了左值的由来:左值的最初来源赋值表达式,因为在左边所以就叫左值了,是可以修改的。
用于&和sizeof的时候数组名是作为左值表达式,返回的结果不是左值。


误解5:二维数组名是二级指针,或者指针的指针。
这个被误解的也很多,首先数组名是一个数组对象的标识,在表达式中通常会转换为指向首元素的指针。
二级指针,顾名思义,一个指针指向的对象还是一个指针。而二维数组名转换成指针后,指向的是一个子数组,所以根本不会是二级指针,是一个一级指针。
int a[2][3]={{1,2,3},{4,5,6}};
int  **ptr=a;
编译器提示 int (*)[3] 类型的值不能用于初始化 int ** 类型的实体。这里a解释为指向数组的指针
那为什么int n=**a; 可以这样写呢?这后面那个看着确实像二级指针的使用方式。原因还是那个: 数组对象在表达式中通常会被转换为指向首元素的指针。
1、a作为表达式首先解释为指向首个子数组{1,2,3}的指针。
2、*a  通过间接访问运算符,结果返回的是数组{1,2,3},并不是指针。如果不参与其他运算,此刻*a就到此为止,只是一个数组。
3、*a这个数组对象,又参与到第2个*中运算。*a数组{1,2,3},这个时候先被转换为指向首元1的指针
4、转换好的指向首元素1的指针进行*运算,返回元素1。
总结:a就是一个数组标识符,只是在表达中中常常会转换成首元素指针

误解6:[ ]下标运算符是用于数组的。
根据C 标准的定义:One of the expressions shall have type "pointer to complete object type", the other expression shall
have integer type, and the result has type "type".
意思[ ]运算符用于两个操作数,其中一个操作数为指针,另一个操作数为int类型。
int a[3]={1,2,3};
int n=a[1];   被转换为  int n=*(a+1)使用,[ ]只不过是后面这种方式的语法糖而已,a[1],  这里的a被转换为了指针。
因为[]下标运算符,跟操作数顺序无关,所以也可以写int n=1[a]; 因为只要保证另一个操作数int类型,所以也可以写int n=a[-1]; int n=(-1)[a];
教科书上只会说a[1]也可以写成1[a],但是不会告诉为什么,因为a[1]它就是*(a+1)  ,至于1在前面、后面当然没关系了
至少从语法上来说没有问题。
int *p=(int*)malloc(sizeof(int)*3);
p[1]=2;
*(p+1)=2;
两种写法没差别,虽然一般称呼[]为数组下标运算符,其实是用于指针的运算符。

结尾:有些人,喜欢用汇编去解释c语言中概念,然后说XX不是就是啥吗!首先这是两种不同语言,汇编是机器语言的助记符,编译器生成汇编的时候会进行各种优化,可能优化的完全不像原来的样子。
C语言本来就是自治的,  解释语言的概念要通过语言规范标准,编译器也是依据规范标,在此基础上进行合理的去实现,不同编译器可能实现方式也会不一样,产生的汇编代码也有不同。
如果都用汇编去解释,那么所有高级语言中的概念都没有了,没有重载,没有多态,没有虚函数。当然学编程语言,看看背后生成的汇编代码也很有必要,加深理解,也可以让我们写出更好的代码.






想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2023-4-10 13:12:56 | 显示全部楼层
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

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

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-12-25 01:36

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

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