C语言中数组的&运算符定义

4

最近有一个问题引起了关于数组和指针的讨论。该问题是关于scanf("%s", &name)scanf("%s", name)的区别。

对于下面的代码,在VS2010中(或者早期版本),微软实际上已经为您解决了这个问题,

#include <stdio.h>

int main()
{
    char name[30];

    printf("Scan \"name\" - ");
    scanf("%s", name);
    printf("Print \"&name\" - %s\n", &name);
    printf("Print \"name\"  - %s\n", name);

    printf("Pointer to &name - %p\n", &name);
    printf("Pointer to name  - %p\n", name);

    printf("\n\n");

    printf("Scan \"&name\" - ");
    scanf("%s", &name);
    printf("Print \"&name\" - %s\n", &name);
    printf("Print \"name\"  - %s\n", name);

    printf("Pointer to &name - %p\n", &name);
    printf("Pointer to name  - %p\n", name);

    return 0;
}

这个在ANSI C标准中有明确定义吗?还是允许与编译器相关?这是否有效,因为MS将所有内容视为C++?暂时请忽略缓冲区溢出问题。

2个回答

8
name&name应该给出相同的结果。 严格来说,根据C语言标准,只有name是有效的,而&name会导致未定义的行为,因此您应该绝对使用name,但在实践中两者都可以工作。 name是一个数组,因此当您将其用作函数参数(例如将其传递给printf时),它会“衰减”为指向其初始元素的指针(这里是char *)。 &name给出数组的地址;该地址与初始元素的地址相同(因为在数组的初始元素之前或在数组中的元素之间不能有填充字节),因此&namename具有相同的指针值。
但是,它们具有不同的类型:&name的类型为char (*) [30](指向30个char数组的指针),而name在衰减为指向其初始元素的指针时,类型为char *(指向char的指针,在本例中是数组name的初始char元素)。
由于它们具有相同的值,并且printfscanf函数将参数重新解释为char *,因此传递name&name应该没有区别。

2
实际上,你说得完全正确。然而,值得一提的是,从官方的角度来看,这种不匹配会导致未定义的行为,因此最好省略它。 - Jerry Coffin
是的 - 就像我说的,实际上它不会造成任何伤害。 - Jerry Coffin
这个人:http://stackoverflow.com/questions/5850800/writing-my-first-c-program-and-i-cant-get-past-this-dumb-bug/ 遇到了一个问题,似乎XCode无法正确解析地址,这促使我提出了关于实际C标准的问题。 - Jess
1
我无法重现该问题描述中的行为。语言标准规定必须传递一个 char*,因此 &name 是错误的,但我无法想象这会导致问题,特别是在编译 x86/x64 时。 - James McNellis
为什么&name会导致未定义的行为?%s需要一个参数,它是“指向字符数组初始元素的指针...”。无论name还是&name都可以做到这一点,即使它们具有不同的类型。 - Michael Burr
显示剩余4条评论

2

根据标准,未定义的行为。

"%p" 的 printf 转换说明符需要一个 void* 类型:其他任何类型都会引发 UB。 "%s" 的 printf 转换说明符需要一个包含空字符的 char* 类型指向的对象:其他任何类型都会引发 UB。 "%s" 的 scanf 转换说明符需要足够的空间来存储输入和额外的空字符终止符的 char* 类型:其他任何类型都会引发 UB。

如果任何实现定义了这种行为,那么在该实现中使用应该是可以的。

通常情况下,使用 printf("%p") 打印 char*char(*)[30] 而不是 void*,结果会产生与预期行为无法区分的 UB 表现形式。

通常情况下,使用 printf("%s") 打印 char(*)[30] 而不是 char*,结果会产生与预期行为无法区分的 UB 表现形式。

从您的说法中,似乎 MS 在这些情况下的 UB 表现与预期的相同。

但仍然是未定义的行为。


“未定义行为”和“实现定义行为”之间的界限非常微妙——如果有的话。 - sehe
1
@sehe: 如果标准规定它是实现定义的(例如,请参见n1256.pdf中的6.3.2.3/5),那么这个结构需要在所有实现中以一种定义的方式工作(尽管在不同的实现中可能有所不同)。如果标准规定它是未定义的(或者未定义行为),那么实现没有必要进行定义 - 在这种情况下可以发生任何事情。 - pmg

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接