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

时间:2013-02-08 10:23:05

标签: c language-lawyer

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

#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和#34;所引用的内容如7.1.4&#34;所允许的那样,除了与函数在同一标题中的库函数的定义,即标准头,作为宏。

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

3 个答案:

答案 0 :(得分:8)

UB的至少一个原因是string.h可以引入宏。出于内部实现的原因,这些宏可能是在假设strcmp是“真正的”strcmp函数的情况下编写的。如果您将strcmp定义为其他内容,然后使用这些宏,strcmp将在宏中扩展为my_strcmp,并产生意外后果。

而不是试图确定...中哪些代码可以正常运行,哪些代码没有,标准会尽早停止你的恶作剧。

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

答案 1 :(得分:5)

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

dispute out of which this question arose不是关于声明或定义保留标识符是否是C未定义的行为,而是行为是否可以通过其他方式定义,例如特定C实现的文档,以及是否程序作者可以做到。

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

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

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

答案 2 :(得分:0)

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

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

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

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