关于C语言attribute format specifier编译警告的疑问咨询
-Wformat-extra-args警告:你的format属性用错了 咱们直接戳中问题核心:你遇到的警告,根源是对GCC的__attribute__((format))属性的参数理解有误,尤其是第三个参数的含义。
先搞懂format(printf, m, n)到底是什么意思
这个属性是用来告诉编译器,按照printf的规则检查函数的格式字符串和参数是否匹配,三个参数的作用是:
printf:指定用printf风格的格式规则来检查m:格式字符串在函数参数里的位置(从1开始计数,你的代码里是3,对应ax参数)n:第一个需要和格式字符串匹配的可变参数的位置(同样从1开始计数)。这里划重点:如果n=0,意思是这个函数没有可变参数——也就是说,格式字符串后面不应该有任何需要用来匹配%s/%d这类占位符的参数。
分析你的两个调用为什么结果不同
你的test函数有4个参数,当你用format(printf,3,0)修饰时,编译器会默认认为:第3个参数ax是格式字符串,且它后面不允许有任何可变参数。那两个调用的差异就很好解释了:
test(1,2,NULL,NULL)触发警告:
当格式字符串是NULL时,编译器没法解析里面有没有占位符(比如%s),所以它会保守地认为你可能传递了多余的参数——毕竟按照属性约定,这个函数不该有可变参数,但你又传了第4个NULL,于是就抛出too many arguments for format警告。test(1,2,a,NULL)不触发警告:
这里格式字符串是"abc",里面没有任何需要参数的占位符(比如%s、%d)。编译器检查后发现,即使后面有第4个参数NULL,也没有需要匹配的格式项,所以不会触发警告——多余的参数并没有违反格式字符串的要求。
怎么解决这个警告?
取决于你的test函数的实际用途:
如果
bx是用来匹配ax中的占位符的(比如ax是"%s",bx是对应的字符串),那你需要把format属性的第三个参数改成4,也就是:void test (int a, int b, const char* ax, const char* bx)__attribute__((format (printf, 3, 4)));这样编译器就会知道:格式字符串在第3位,第一个需要匹配的可变参数在第4位,它会自动检查
ax的占位符和bx是否匹配,也不会再报多余参数的警告。如果
bx和格式字符串完全无关,那你要么去掉format属性(因为它不需要格式检查),要么在传递NULL作为ax时,不要传递bx参数。
修正后的示例代码
#include <stdio.h> // 调整format属性的第三个参数为4,指定第一个可变参数在第4位 void test (int a, int b, const char* ax, const char* bx)__attribute__((format (printf, 3, 4))); int main() { char *a = "abc"; char* b = "cde"; test(1, 2, NULL, NULL); // 现在不会触发警告 test(1, 2, "Hello %s", b); // 编译器会检查b是否符合%s的要求 } void test(int a, int b, const char* ax, const char* bx) { if (ax != NULL) { printf(ax, bx); } }
内容的提问来源于stack exchange,提问作者Arul Moondra

