【发布时间】:2013-02-08 10:23:05
【问题描述】:
很容易推断这样的代码是如何工作的:
#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 会怎样?
【问题讨论】:
-
这个问题是基于一个错误的前提。 here 引起的争议不是定义保留标识符是否由 C 未定义,而是它是否可以通过其他方式定义以及一个人是否可以这样做(即,在某些情况下,一个人是否可以进行生产性使用C 标准未定义的 C 实现的行为)。
标签: c language-lawyer