【问题标题】:Can it cause problems to pass the address to an array instead of the array?将地址传递给数组而不是数组会导致问题吗?
【发布时间】:2023-03-14 22:41:01
【问题描述】:

我遇到了这段代码:

char str[600];
scanf("%s", &str);

当然,这会发出以下警告:

a.c:6:17: warning: format specifies type 'char *' but the argument has type
      'char (*)[600]' [-Wformat]
    scanf("%s", &str);
           ~~   ^~~~~~~

我知道正确的方法是删除& 并改为输入scanf("%s", str)。但它确实有效,所以我的问题是这是否会导致任何问题。它是未定义的行为吗?当我将str 切换为指针而不是数组时,它(显然)不起作用。但这会在使用数组时引起任何问题吗?

【问题讨论】:

    标签: c arrays


    【解决方案1】:

    是的,代码是undefined behaviour。对应于%s 的参数必须具有char * 类型。这在 C17 7.21.6.2/12 中的 s 说明符下进行了描述:

    [...] 对应的参数应该是一个指向字符数组的初始元素的指针,该数组的大小足以接受序列和一个终止的空字符,它将自动添加。

    相当清楚地表明指针应该具有指向字符的类型,而不是指向整个数组。

    未定义的行为意味着任何事情都可能发生。它可能表现得好像您省略了&,或者它可能会格式化您的硬盘。

    鉴于在这种情况下避免未定义行为非常容易,我真的没有理由争论在这种情况下依赖未定义行为的行为是否可以。

    【讨论】:

    • 7.19.6.1?你的意思是7.21.6.1 fprintf函数?为什么 6.2.7 兼容类型和复合类型 不“保存”从 UB 发布的代码?数组的地址是第一个元素的地址,它必须与指向与数组元素相同类型的指针的类型兼容?我倾向于同意你的回答,那就是 UB,因为 7.21.6.1 说它是 UB,但我可以看到如何通过兼容类型和数组衰减进行参数。这就是语言律师兔子洞的方式,方式,waaay ...
    • @AndrewHenle 不错的收获。我要去那里。
    • @AndrewHenle -- “数组的地址是第一个元素的地址,它必须与指向与数组元素相同类型的指针的类型兼容?”:我不确定您在这里要说什么,但听起来您是在说指向数组的指针和指向该数组第一个元素的指针是兼容的类型。鉴于two types have compatible type if their types are the same(加上一些似乎不适用于此处的规则),我看不出标准支持这一点的任何方式。
    • 标准对fscanf 的描述如下:如果此对象没有适当的类型,或者转换的结果无法在对象中表示,则行为未定义。
    • @AnttiHaapala 措辞似乎也允许unsigned char *,因为这符合“字符”的描述。
    【解决方案2】:

    在这种情况下,使用&str 而不是str 不会导致任何问题,因为这两个地址是相同的。有关说明,请参阅 this past question。但正如您所注意到的,&str 的类型不同,编译器会抛出警告,实际行为将取决于架构和实现。

    【讨论】:

    • 它可能不会在常见架构中引起问题 - 但严格来说,这是不允许的。还有一件事可能会破坏它:不能保证不同的指针类型都对相同的地址使用相同的表示。所以这里有问题的类型char*char (*)[600] 可能有不同的大小或值表示。
    • @aschepler 够公平的。我想规范中未定义的任何行为都将是特定于实现或架构的。最好以正确的方式开始编写它。
    • @Paul -- “规范中未定义的任何行为”:并不是说该行为未定义,而是标准明确表示该行为未定义。将错误的类型传递给函数不是灰色地带。
    • 是的,虽然未定义的行为和明确未定义的行为之间的区别似乎并没有那么不同。我会更新我的答案,说在这种情况下它“没有”造成任何问题,而不是“不会”造成任何问题。
    • @Paul -- 编译器编写者可能会利用某些构造会导致显式未定义行为(因此不应该出现在有效的 C 程序中)的事实来进行一些优化。我想说,与未定义的遗漏行为相比,这是一个显着的差异,当然也与标准中给出的许多实现定义的行为相比。
    【解决方案3】:

    在 C 中,数组的名称​​是也是它的地址(指向数组的开头)。

    【讨论】:

    • 这不是真的。数组是 C 中的对象(在 C 意义上),数组标识符指的是数组对象。在大多数表达式(但不是在所有表达式中),数组确实衰减指向指向它们的第一个元素的指针。特别是,对于char arr[] = "abc"; size_t arr_sz = sizeof arr;,标识符arr 不仅引用一个数组,它不会衰减到sizeof 表达式中的指针。
    • 你是对的。我想说的是“数组的名称”可以作为它的地址,而不是数组是(仅)地址。
    • @XsOuLp 仍然不正确。实际例子:sizeof(arr) 这里arr 是数组类型,不会衰减。它不是任何形式的地址。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-11-25
    • 1970-01-01
    • 2020-03-31
    • 1970-01-01
    • 2020-06-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多