&C中数组的运算符定义

最近的一个问题促成了以数组和指针为中心的讨论。 问题是参考scanf("%s", &name) vs scanf("%s", name)

对于以下代码,Microsoft实际上在VS2010(也许是早期版本?)中为您解决了这个问题,

 #include  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 ++? 请暂时忽略缓冲区溢出问题。

name&name都应该给出相同的结果。 严格来说,只有name根据C语言标准有效且&name导致未定义的行为,因此您绝对应该使用name ,但实际上两者都有效。

name是一个数组,因此当你将它用作函数的参数时(就像你将它传递给printf ),它会“衰减”到指向其初始元素的指针(这里是一个char* )。

&name为您提供数组的地址; 此地址与初始元素的地址相同(因为在数组的初始元素之前或数组中的元素之间不能有填充字节),因此&namename具有相同的指针值。

但是,它们有不同的类型: &namechar (*)[30]类型char (*)[30] (指向30个char的数组的指针)而name ,当它衰减到指向其初始元素的指针时,类型为char* (指针)到一个char ,在这种情况下,是数组name的初始char元素)。

由于它们具有相同的值,并且由于printfscanf函数无论如何都将参数重新解释为char* ,因此传递name&name应该没有区别。

根据标准的未定义行为。

printf转换说明符"%p"需要void* :其他任何内容调用UB printf转换说明符"%s"需要一个char* ,它包含指向对象内部某处的空字节:其他任何调用UB scanf转换说明符"%s"期望一个char*具有足够的空间用于输入和一个额外的空终止字节:其他任何东西调用UB

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

通常使用printf("%p")打印char*char(*)[30]而不是void*导致UB表现与预期行为无法区分。

通常使用printf("%s")打印char(*)[30]而不是char*导致UB表现与预期行为无法区分。

根据你的说法,在这些情况下,UB的MS表现与预期相同。

但它仍然是未定义的行为。