C语言 宏工作不正常它只打印文件名

rkkpypqq  于 2023-10-16  发布在  其他
关注(0)|答案(5)|浏览(122)
#include <stdio.h>

#define LINE_FILE ("Line %d of file %s", __LINE__, __FILE__)

int main(void) {
    const char* str = LINE_FILE;
    printf("%s\n", str);
    printf("Line %d of file %s\n", __LINE__, __FILE__);
}

第一个print语句只打印文件名而不是整行。

5anewei6

5anewei61#

如果你想要一个像这样的字符串预格式化,这里是你如何做到这一点:

#include <stdio.h>

#define STR_IMPL(x) #x
#define STR(x) STR_IMPL(x)
#define LINE_FILE ("Line " STR(__LINE__) " of file " __FILE__)

int main() {
    printf("%s\n", LINE_FILE);
}
voj3qocg

voj3qocg2#

实际上 * 解释 * 为什么代码不工作以及如何使其工作:

  • 如前所述,宏基本上是文本替换,因此您最终将得到

const char* str = ("Line %d of file %s", __LINE__, __FILE__);结果是乱码。

  • string literals"these things")中的%d%s等格式说明符实际上在C中没有任何特殊意义。碰巧的是,printf系列函数为这些函数定义了特定的含义,因为printf系列函数在内部使用输入的运行时字符串解析。而且我们希望这也能在编译时完成,所以sprintf等是不好的。
  • 现在,事实证明,这一行实际上是有效的C偶然,但不是你所期望的。结果声明包含几个逗号,但这里它们既不是像char str[] = {'A','B','C','\0'};那样的初始化列表,也不是函数调用。因为你把表达式放在括号里,所以它最终会成为一个子表达式,编译器会理解这一点,就好像你想使用特殊的逗号运算符,一样。What does the comma operator , do?

TL;DR对逗号运算符的简化是,它丢弃了左边的所有操作数,只使用最右边的操作数作为结果。因此,您的表达式最终100%等效于const char* str = __FILE__;,因此,当您尝试打印str时,只有文件名的结果令人惊讶。

  • 无论哪种方式,这都不是我们在C中进行字符串连接的方式。它可以在运行时使用分配的字符串和strcpystrcat等函数完成,但也可以在编译时连接字符串。

C * 预处理器 * 有一些锦囊妙计。预处理器是指执行的第一个编译器阶段,当编译器通过你的代码,以确保一切似乎是有效的C“一眼”。预处理器将LINE_FILE替换为宏体,然后依次将__LINE____FILE__替换为实际数据。但它也能够连接字符串文字。在C语言中,这是通过写两个字符串字面量,中间有一个或多个空格来完成的,例如"hello" "world",然后将其连接为"helloworld"

  • 由于__FILE__是一个字符串,我们可以写"file " __FILE__,然后预处理器将其转换为"file test.c"等。然而,对于整数__LINE__,情况并非如此。
  • 因此,为了在编译时通过预处理器进行所有的连接,我们需要将__LINE__转换为字符串字面量。只要__LINE__是编译时常量,预处理器也可以这样做。我们将使用“字符串化宏”Stringification - how does it work?

关于字符串化宏的TL;DR是“字符串化运算符”#总是首先应用于它所在的宏中,然后才扩展该宏中的所有其他标记。因此,如果我们编写一个宏#define STR(s) #s并传递STR(__LINE__),我们 * 将 * 得到一个字符串字面量,但它将变成"__LINE__",这不是我们想要的。为了解决这个问题,我们需要创建一个帮助宏,在调用包含#的宏之前,它首先扩展所有预处理器标记。就像这样:

#define STR_(s) #s
#define STR(s) STR_(s)
  • 另外,我们可能不应该执行const char* str = LINE_FILE;,否则它将被分配到声明str的行和文件中,而不是我们选择打印它的行。

完整示例:

#include <stdio.h>

#define STR_(s) #s
#define STR(s) STR_(s)

#define LINE_FILE "Line " STR(__LINE__) " of file " __FILE__

int main(void) {
    puts(LINE_FILE);
}

输出量:

Line 9 of file example.c
1cosmwyk

1cosmwyk3#

请记住,宏是一个简单的替换,因此str实际上是

const char* str = ("Line %d of file %s", __LINE__, __FILE__);

该表达式由三个子表达式组成,使用,运算符连接,该运算符计算并忽略其第一个参数,然后计算并产生其第二个参数。所以它相当于

"Line %d of file %s";
    __LINE__;
    const char* str = __FILE__;

希望这能帮助你明白为什么你会得到这样的结果。

yptwkmov

yptwkmov4#

很难说你到底想要什么。请记住,宏本质上只是一个美化的复制和粘贴,所以这个宏不是很有用:

#define LINE_FILE ("Line %d of file %s", __LINE__, __FILE__)

我想你要找的是一个宏,它用__LINE____FILE__调用printf。像这样的东西应该可以做到:

#define LINE_FILE printf("Line %d of file %s", __LINE__, __FILE__)

或者你想把它存储在一个缓冲区中,用于其他具有“类似函数的宏”的东西:

#define LINE_FILE(buf_, n_) \
    snprintf(buf_, n_, "Line %d of file %s", __LINE__, __FILE__)

int main()
{
    char buf[50];
    LINE_FILE(buf, sizeof(buf));
}
9udxz4iz

9udxz4iz5#

("Line %d of file %s", __LINE__, __FILE__)使用printf创建字符串。
您需要:

printf LINE_FILE;

#define LL "Line %d of file %s\n", __LINE__, __FILE__
#define LINE_FILE (LL)

#define LINE_FILE1(name) char *name; { size_t len; name = malloc(len = (snprintf(NULL, 0, LL) + 1)); \
                         if(name) snprintf(name, len, LL);}

int main(void) 
{
    printf LINE_FILE;
    printf("Line %d of file %s\n", __LINE__, __FILE__);
    LINE_FILE1(str);
    printf("%s", str);
    free(str);
}

相关问题