带有()和{}的C预处理程序宏代码

时间:2018-10-25 12:37:40

标签: c

#include <stdio.h>
#define a (1,2,3)
#define b {1,2,3}

int main()
{
    unsigned int c = a;
    unsigned int d = b;
    printf("%d\n",c);
    printf("%d\n",d);
    return 0;
}

以上C代码会将输出打印为31

但是#define a (1,2,3)#define b {1,2,3}在没有构建警告的情况下如何取a = 3和b = 1,以及(){}如何给出不同的值?

3 个答案:

答案 0 :(得分:8)

请记住,预处理器只是替换宏。因此,根据您的情况,您的代码将被转换为此:

#include <stdio.h>

int main()
{
    unsigned int c = (1,2,3);
    unsigned int d = {1,2,3};
    printf("%d\n",c);
    printf("%d\n",d);
    return 0;
}

在第一种情况下,您从,运算符获得结果,因此c等于3。但是在第二种情况下,您将获得d的初始值设定项列表的第一个成员,因此您将获得1作为结果。

如果您将代码编译为c++,则第二行会产生错误。但似乎您可以在c中编译此代码。

答案 1 :(得分:4)

除了其他答案,

unsigned int d = {1,2,3};

(在宏替换之后)

在C语言中无效。它违反了6.7.9 Initialization

  

初始化器不得尝试为未包含在正在初始化的实体中的对象提供值。

使用更严格的编译选项(gcc -std=c17 -Wall -Wextra -pedantic test.c),gcc会产生:

warning: excess elements in scalar initializer
     unsigned int d = {1,2,3};
                         ^

但是请注意

unsigned int d = {1};

有效,因为允许initializing scalar with braces。只是前一片段存在问题的额外初始化器值。

答案 2 :(得分:1)

对于c,初始化器是一个表达式,其值为3。对于d,初始化器是一个用大括号括起来的列表,并且它提供了太多的值,其中只有第一个是使用。

宏扩展后,cd的定义为:

unsigned int c = (1,2,3);
unsigned int d = {1,2,3};

在C语法中,出现在unsigned int c =unsigned int d =之后的初始化器可以是 assignment-expression 或{{1} } initializer-list {(并且该列表中可能有最后一个逗号)。 (来自C 2018 6.7.9 1。)

第一行中的} assignment-expression 。特别是,它是(1,2,3) expression (形式的 primary-expression 。也就是说, expression 使用逗号运算符;它的格式为 expression ) assignment-expression 。我将省略语法的继续扩展。可以说,是用逗号运算符构建的表达式,而逗号运算符的值只是其右手操作数。因此1,2,3的值为2,1,2的值为3。括号表达式的值是其内部表达式的值,因此1,2,3的值为3.因此,(1,2,3)被初始化为3。

相反,在第二行中,c{1,2,3} initializer-list {。根据C子句6.7.9中的文字, initializer-list 提供用于初始化所定义对象的值。 }{形式用于初始化数组和结构,但也可用于初始化标量对象。如果我们写了},这会将unsigned int d = {1};初始化为1。

但是,6.7.9 2是一个约束条件,上面写着:“初始化程序不得尝试为未包含在正在初始化的实体中的对象提供值。”这意味着您提供的初始值可能不会超过要提供的值。初始化。因此,d违反了约束。需要编译器才能生成诊断消息。另外,您的编译器似乎继续运行,仅使用列表中的第一个值来初始化unsigned int d = {1,2,3};。其他的都是多余的,被忽略了。

(另外,6.7.9 11说“标量的初始化程序应该是单个表达式,可以选择用大括号括起来。”)