在引用的字符串中扩展整数宏

Expand integer macro inside quoted string

在为 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);
     }
   }
 }

由于代码不是 windows 具体的,每个 256 值都应转换为 PATH_MAX

我知道在带引号的字符串中扩展宏是不可能的,但是字符串连接很简单:

#define STR "string"
size_t len=strlen("part"STR"part 2");

但是,诸如:

"%."PATH_MAX".db"

应该不起作用,因为PATH_MAX扩展为整数,而不是字符串。
那么有没有一种方法可以在不调用将整数转换为 C 字符串的函数的情况下执行此操作?

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

#define Q(x) Q_(x)
#define Q_(x) #x

所以你可以这样做:

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

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

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

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

这相当于:

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

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