通过令牌连接创建包含通用字符名称的标识符

Mik*_*CAT 19 c clang language-lawyer

我编写了这段代码,通过令牌连接创建包含通用字符名称的标识符.

//#include <stdio.h>
int printf(const char*, ...);

#define CAT(a, b) a ## b

int main(void) {
    //int \u306d\u3053 = 10;
    int CAT(\u306d, \u3053) = 10;

    printf("%d\n", \u306d\u3053);
    //printf("%d\n", CAT(\u306d, \u3053));

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

这个代码适用于gcc 4.8.2 with -fextended-identifiersoptiongcc 5.3.1,但是没有与clang 3.3一起使用时出现错误信息:

prog.c:10:17: error: use of undeclared identifier '??'
        printf("%d\n", \u306d\u3053);
                       ^
1 error generated.
Run Code Online (Sandbox Code Playgroud)

和本地clang(Apple LLVM版本7.0.2(clang-700.1.81)),错误消息:

$ clang -std=c11 -Wall -Wextra -o uctest1 uctest1.c
warning: format specifies type 'int' but the argument has type
      '<dependent type>' [-Wformat]
uctest1.c:10:17: error: use of undeclared identifier '??'
        printf("%d\n", \u306d\u3053);
                       ^
1 warning and 1 error generated.
Run Code Online (Sandbox Code Playgroud)

当我使用-E选项让编译器输出代码扩展宏时,gcc 5.3.1发出了这样的信息:

# 1 "main.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 1 "<command-line>" 2
# 1 "main.c"

int printf(const char*, ...);



int main(void) {

 int \U0000306d\U00003053 = 10;

 printf("%d\n", \U0000306d\U00003053);


 return 0;
}
Run Code Online (Sandbox Code Playgroud)

当地的铿锵声说:

# 1 "uctest1.c"
# 1 "<built-in>" 1
# 1 "<built-in>" 3
# 326 "<built-in>" 3
# 1 "<command line>" 1
# 1 "<built-in>" 2
# 1 "uctest1.c" 2

int printf(const char*, ...);



int main(void) {

 int \u306d\u3053 = 10;

 printf("%d\n", ??);


 return 0;
}
Run Code Online (Sandbox Code Playgroud)

如您所见,printf()在gcc输出中匹配时声明和使用的标识符,但它们在clang的输出中不匹配.

我知道通过令牌连接创建通用字符名称会调用未定义的行为.

N1570 5.1.1.2翻译阶段:

如果通过标记连接(6.10.3.3)生成与通用字符名称的语法匹配的字符序列,则行为未定义.

我认为这个字符序列\u306d\u3053可能"匹配通用字符名称的语法",因为它包含通用字符名称作为其子字符串.我还认为"匹配"可能意味着通过串联产生的整个令牌代表一个通用字符名称,因此在此代码中不会调用此未定义的行为.

阅读PRE30-C.不要通过连接创建通用字符名称,我发现一条评论说这种串联是允许的:

什么是禁止,通过串联创建一个新的UCN.喜欢做

分配(\ u0001,0401,A,B,4)

只是在任何地方连接恰好包含UCN的东西都没问题.

并且一个日志显示像这种情况(但有4个字符)的代码示例被替换为另一个代码示例.

我的代码示例是否调用了一些未定义的行为(不限于通过令牌串联生成通用字符名称而调用的行为)?或者这是clang中的错误?

use*_*915 7

您的代码不会触发您提到的未定义行为,因为通用字符名称(6.4.3)不是由标记串联生成的.

并且,根据6.10.3.3,由于运算符的左侧和右侧##都是标识符,并且生成的令牌也是有效的预处理标记(也是标识符),因此##操作符本身不会触发未定义的行为.

在阅读了关于标识符(6.4.2,D.1,D.2),通用字符名称(6.4.3)的描述之后,我很确定它更像是clang预处理器中的一个bug,它处理令牌生成的标识符串联和普通标识符不同.