12

很容易推断这样的代码是如何工作的:

#include <string.h>

#define strcmp my_strcmp

int my_strcmp(const char *, const char *)

...
strcmp(str1, str2);
...

但这个问题是这在技术上是否正确。

从 C11:

7.1.3.1(关于保留名称):

...

  • 如果包含任何关联的头文件,则以下任何子条款(包括未来的库方向)中的每个宏名称都保留用于指定使用;除非另有明确说明(见 7.1.4)。
  • 以下任何子条款(包括未来的库方向)和 errno 中的所有具有外部链接的标识符始终保留用作具有外部链接的标识符。184 )
  • 如果包含任何关联的标头,则保留在以下任何子条款(包括未来的库方向)中列出的具有文件范围的每个标识符用作宏名称和具有相同名称空间中的文件范围的标识符。

184具有外部链接的保留标识符列表包括 math_errhandling、setjmp、va_copy 和 va_end。

所以这意味着这strcmp是一个保留字,因为string.h被包含在内。

7.1.3.2:

...如果程序在保留标识符的上下文中声明或定义标识符(7.1.4 允许的除外),或将保留标识符定义为宏名称,则行为未定义。

现在这似乎说重新定义strcmp是未定义的行为,除非它在 ​​7.1.4 中以某种方式被允许。

7.1.4可能相关的内容有:

7.1.4.1:

...在头文件中声明的任何函数都可以另外实现为头文件中定义的类似函数的宏,因此如果在包含头文件时显式声明库函数,则可以使用下面显示的技术之一来确保声明不受此类宏的影响。函数的任何宏定义都可以通过将函数的名称括在括号中来在本地抑制,因为该名称后面没有表示宏函数名称扩展的左括号。出于同样的语法原因,即使库函数也被定义为宏,也允许获取库函数的地址。185 ) 使用#undef 删除任何宏定义也将确保引用实际函数。...

185这意味着实现应为每个库函数提供一个实际函数,即使它还为该函数提供了一个宏。

7.1.4.2:

如果可以在不引用头文件中定义的任何类型的情况下声明库函数,则也允许声明函数并在不包括其关联头文件的情况下使用它。

其余条款无关紧要。我看不到 7.1.3.2 所指的“7.1.4 所允许的”,除了库函数在与函数相同的头文件中的定义,即标准头文件,作为宏。

总之,上面的代码在技术上是未定义的行为吗?如果string.h不包括怎么办?

4

3 回答 3

8

它是 UB 的至少一个原因是它string.h可以引入宏。出于内部实现的原因,这些宏可能是在假设strcmp是“真正的”strcmp 函数的情况下编写的。如果你定义strcmp为别的东西然后使用那些宏,strcmpmy_strcmp在宏中展开,产生意想不到的后果。

该标准并没有试图准确地确定哪些代码可以使用,哪些代码...不能使用,而是尽早停止您的恶作剧。

另请注意,除了标准完全禁止它之外,您#define strcmp my_strcmp可能是宏重新定义,因为string.h允许这样做#define strcmp __strcmp或其他任何事情。因此,在某些符合要求的实现中,您的代码格式不正确。

于 2013-02-08T11:37:22.857 回答
5

声明或定义保留标识符的程序不严格符合 (C 2011 4 5) 但可能符合 (C 2011 4 7)。

引发这个问题的争论不在于声明或定义保留标识符是否是 C 未定义的行为,而是该行为是否可以通过其他方式定义,例如特定 C 实现的文档,以及程序作者可以做到。

有些人将“未定义的行为”视为“您可能不会这样做”。这是对“未定义行为”的错误解释。未定义的行为不是标准要求您避免的;这是 C 标准无法帮助您解决的问题。

C 标准明确声明它对未定义的行为没有要求。特别是,这意味着没有要求你不能这样做,也没有要求另一个规范不能定义行为。几乎每个实际程序都使用 C 标准未定义的行为,当它进行操作系统文档定义的系统调用或库文档定义的库调用或依赖于其设计的特定 C 实现定义的数据类型格式时为了。

在 C 中,“未定义的行为”只是 C 标准设置的规则的结束。这是一个开放的领域,您可以使用其他方式导航,而不是阻碍您前进的墙。

于 2013-02-08T16:45:33.533 回答
0

是的,这是未定义的行为。它很可能会起作用,但你不能确定。

我相信这样做的原因是允许编译器具有str函数如何工作的内置知识。当然,strcmp可能已经是一个宏memcmp(s1, s2, strlen(s1))或类似的东西[我不是说它就是这样,只是可能是这样。

我不相信“不包括 string.h”实际上有帮助。

我建议您在源代码中进行搜索和strcmp替换。如果您希望“返回”,my_strcmp您总是可以反过来使用。#define my_strcmp(s1, s2) strcmp(s1, s2)

于 2013-02-08T11:24:21.683 回答