C 展开带引号字符串内的整数宏

C 展开带引号字符串内的整数宏,c,macros,c-preprocessor,c-strings,exim,C,Macros,C Preprocessor,C Strings,Exim,在为exim做贡献时,我看到了许多硬编码的值: uschar filebuffer[256]; (void)sprintf(CS filebuffer, "%.256s.db", filename); rc = lf_check_file(-1, filebuffer, S_IFREG, modemask, owners, owngroups, "dbm", errmsg); if (rc < 0) /* stat() failed */ { (void)spr

在为exim做贡献时,我看到了许多硬编码的值:

uschar filebuffer[256];
(void)sprintf(CS filebuffer, "%.256s.db", filename);
 rc = lf_check_file(-1, filebuffer, S_IFREG, modemask, owners, owngroups,
  "dbm", errmsg);
if (rc < 0)        /* stat() failed */
  {
  (void)sprintf(CS filebuffer, "%.256s.dir", filename);
  rc = lf_check_file(-1, filebuffer, S_IFREG, modemask, owners, owngroups,
    "dbm", errmsg);
   if (rc == 0)     /* x.dir was OK */
     {
     (void)sprintf(CS filebuffer, "%.256s.pag", filename);
     rc = lf_check_file(-1, filebuffer, S_IFREG, modemask, owners, owngroups,
       "dbm", errmsg);
     }
   }
 }
但是,像这样的事情:

"%."PATH_MAX".db"
不应该工作,因为
PATH\u MAX
扩展为整数,而不是字符串。

那么,有没有一种方法可以在不调用将整数转换为C字符串的函数的情况下实现这一点 ?

您可以使用
#
运算符对宏参数进行字符串化。但您需要一个间接宏调用来扩展参数:

#define Q(x) Q_(x)
#define Q_(x) #x
所以你可以这样做:

char filebuffer[PATH_MAX + 10];
sprintf(filebuffer, "%." Q(PATH_MAX)"s.db", filename);

现有代码将字符串sibstitution限制为256个字符,但随后添加了一个文件扩展名(和NUL终止符),当长度接近256时,这将导致缓冲区溢出。我在上面使用了任意10字节的过度分配,但最好使用检查长度的sprintf,如
snprintf
。这样做的另一个好处是不需要宏游戏。

正确的方法是在格式字符串中使用
*
,这将使它从参数列表中获取值。例如:

printf("%.*s\n", 3, "abcde");
这相当于:

printf("%.3s\n", "abcde");

这样,您就可以使用
PATH\u MAX
或任何其他值来控制格式,而不必担心它们是如何定义的(例如,它们是否包含括号或加法运算符等)

为什么您要强制执行函数调用而不是分析它们的返回值?@iharob:因为我没有编写这段代码。请阅读第一行,这是我没有修改的exim代码的一部分。很多事情都做得不对:大多数代码根本没有缩进,甚至还有对
malloc()
调用的返回值没有被选中。@user2284570不,不会<代码>#字符串化附加到它的值。按照您显示的方式执行将生成
“%.PATH\u MAX.db”
,而不是
“%.256.db”
。您需要将
PATH\u MAX
传递给宏,以将其计算为其值,然后将该值字符串化。@RemyLebeau:实际上,OP在该注释中所写的内容只会字符串化
(假设它出现在宏替换中,因为至少在理论上,#是宏替换之外的一个错误。这应该是公认的答案,因为它像原始的一样使用编译时处理。如果您只是想再详细说明一下。@Olaf您是在开玩笑吧?如果
PATH_MAX
被定义为
(2*512)该怎么办
?那么这个解决方案永远都不可能奏效。这是一个错误的方法。@汤姆:我同意有比依赖宏作为简单数字更好的解决方案。但是,我不认为长度插值真的更好,因为它取决于手动计算格式字符串需要多少额外字符;总的来说,snprintf是一种解决方案,即使你必须为那些太旧而缺少它的系统找到一个实现,而这些系统已经没有那么多了。对不起,必须进行虚拟编辑才能删除DV。你真的有道理
printf("%.3s\n", "abcde");