gcc 为什么连这种代码都能编译通过?

2022-06-18 11:14:39 +08:00
 kgdb00
#include <stdio.h>

int main(int argc, char *argv[])
{
	char *s = argv[argc-1];
	printf("%s\n", s);

	char *s2 = argc[argv-1];
	printf("%s\n", s2);

	return 0;
}

打印最后一个参数,上面的写法是正确的,问题是下面的写法也能编译通过,而且打印输出和上面的写法一样,不明白为什么编译器允许 argc[argv-1] 这种写法。

7098 次点击
所在节点    Linux
42 条回复
realradiolover
2022-06-18 11:19:31 +08:00
管理指针是程序员的责任
ecloud
2022-06-18 11:25:56 +08:00
x[y]应该等价于 (x 地址数+y*x 定义的位长)的地址
那么这里是不是可以理解 argc 也是个地址,这个传进来的所谓 int 并不在堆里?
kgdb00
2022-06-18 11:26:45 +08:00
@realradiolover 问题是把 int 类型直接当成数组用,不符合 c 语言的语法规范啊
XiLingHost
2022-06-18 11:27:48 +08:00
@kgdb00 实际上数组是个语法糖,本质是指针操作
stein42
2022-06-18 11:28:52 +08:00
a[b] 等价于 *(a + b)。
C 语言标准就是这样规定的。
https://en.cppreference.com/w/c/language/operator_member_access
msg7086
2022-06-18 11:29:40 +08:00
a[b] 就是 *(a+b)。
加法的两端是可以交换的。所以 a[b]就是 b[a]。
hardwork
2022-06-18 11:30:41 +08:00
a[3]等价于 3[a],c 语言混乱编码里经常看到,最终都是*(a+3)的意思,至于是不是符合语言规范就不知道了,反正 gcc 可以通过
adoal
2022-06-18 11:30:49 +08:00
什么类型不类型的,C 又不是 Haskell
ecloud
2022-06-18 11:32:34 +08:00
这么看起来这段内存是这样的:
argc | argv[0] | argv[1] | ...
argv-1 的地址就相当于 argc 的地址
你的 argc 是不是等于 1 呢?你试试它等于 3 的时候是不是就显示不全 argv[0]了
Caturra
2022-06-18 11:34:18 +08:00
我没看过 C 标准,但是
可不可以这么写是标准负责的事情
要不要这么写是程序员负责的事情
kgdb00
2022-06-18 11:36:53 +08:00
@ecloud 不管 argc 等于几,两种写法的输出都是一样的。
knir
2022-06-18 11:41:12 +08:00
翻了一下 C 标准,6.2.5.1 Array subscripting
1: 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’’.
2: ... The definition of the subscript operator [] is that E1[E2] is identical to (*((E1)+(E2))) ...
kgdb00
2022-06-18 11:44:39 +08:00
@knir 感谢,Array subscripting 是在 6.5.2.1 节
xiri
2022-06-18 11:48:12 +08:00
@ecloud 没有你想的那种巧合,楼上已经讲的很清楚了,因为 a[b]的本质还是指针操作,有:a[b]=*(a+b)=*(b+a)=b[a]
qbqbqbqb
2022-06-18 12:19:17 +08:00
首先很显然 (argv-1)[argc] 和 argv[argc-1]访问的是同一个东西。然后根据 C 语言数组下标操作的定义,(argv-1)[argc]和 argc[argv-1]又是等价的。
chronosphere
2022-06-18 12:30:13 +08:00
@knir 这种下标运算的写法和结果和汇编语言里的指针寻址很像,可能 C 语言就照搬过来了吧。把 argc 对应的二进制数据看作相应的内存地址,就好理解一些了
zyeros1991
2022-06-18 13:54:13 +08:00
跟我一起背:指针是合法的整形,整形是合法的指针
geelaw
2022-06-18 14:14:32 +08:00
argv[argc-1] 是正确的,这不必多说。

argc[argv-1] 是语法正确的,楼上已经解释过了。然而这段代码是错误的(不可移植),见 C89 2.1.2.2 和 3.3.6 。

2.1.2.2 规定了 int main(int argc, char *argv[]) 的实参含义,满足该要求的实现里,argv 可以是一个长度为 (argc + 1) 的数组,此时依 3.3.6 对指针加减一个整数定义,(argv - 1) 是 undefined behavior ,因为它不能指向 argv 数组的任何一个元素,也不能指向 argv 数组最后一个元素之后的位置。该 undefined behavior 不要求算出的指针被解引用。

注意 argc[argv-1] 等同于 *(argc + (argv - 1)),因此是错误的。但 *(argc + argv - 1) 就不是错误的了(除非 argc 等于 0 )。
evan1024
2022-06-18 19:20:12 +08:00
换 rust😏
mingl0280
2022-06-18 23:42:08 +08:00
@geelaw 你这个理解是错误的,C99 6.5.6 (8):When an expression that has integer type is added to or subtracted from a pointer, the result has the type of the pointer operand. In other words, if the expression P points to
the i-th element of an array object, the expressions (P)+N (equivalently, N+(P)) and (P)-N (where N has the value n) point to, respectively, the i+n-th and i−n-th elements of the array object, provided they exist.

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/860466

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX