重新定义标准名称是不确定的行为?

很容易理解这样的代码是如何工作的:

#include  #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怎么样?

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

该标准并没有试图确定哪些代码在...以及什么不可行的情况下确实可行,而是为您的恶作剧提前做好准备。

另请注意,除了标准的限制禁止它之外,你的#define strcmp my_strcmp可能是一个宏重定义,因为string.h被允许做#define strcmp __strcmp或其他什么。 因此,在一些符合要求的实现中,您的代码是不正确的。

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

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

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

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

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

是的,它是未定义的行为。 可能它会起作用,但你无法确定。

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

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

我建议您在源代码中搜索并替换strcmpmy_strcmp 。 如果你想“回去” #define my_strcmp(s1, s2) strcmp(s1, s2)你总是可以#define my_strcmp(s1, s2) strcmp(s1, s2)使用#define my_strcmp(s1, s2) strcmp(s1, s2)