在阅读redis源码的时候,遇到_attribute_((format(printf, 2, 3)))用法,在此记录下该用法的作用以及需要注意的点。
在C语言编程过程中,我们常常会实现一些可变参数的函数调用(类scanf、printf函数),变参函数在我们编程过程中带来了很大的方便,但是也有一些问题,即我们在调用可变参数的函数的时候,默认情况下编译器检查不出可变参数的类型或者个数是否正确,这就导致使用变参函数的时候,若稍不注意写错了参数,很难被发现和定位。
_attribute_((format(printf, 2, 3)))的作用就是用来解决这个问题,它用于函数声明,作用是提示编译器检查函数调用的过程中,可变参数部分按照printf的规则进行检查,若参数的个数或者类型不匹配,编译过程中将会发出警告,这就使得上面提到的问题在编译期间就能发现。注意编译时要加上 –Wall才可以。
// 用法原型
// archetype:为按照那种风格进行校验,如printf/scanf等
// string-index:格式化format字符串所在的位置,如void test(testA, format,...),此时为2
// first-to-check:第一个可变参数的位置,如void test(testA, format,...),此时为3
__attribute__((format(archetype, string-index, first-to-check)))
注意:string-index和first-to-check值选取的时候,若变参函数是类成员函数,这时函数展开后第一个参数为this指针,这个也要考虑到位置中。
在普通函数中,string-index和first-to-check参数值即为实际的位置,如下面的测试样例string-index=1,first-to-check=2
// test_format.cpp
#include
#include
#include
void test(const char* format, ...) __attribute__((format(printf, 1, 2)));
void test(const char* format, ...)
{
va_list ap;
std::string str;
while (true)
{
char* buffer = new char[1024];
va_start(ap, format);
int expected = vsnprintf(buffer, 1024, format, ap);
va_end(ap);
str += std::string(buffer);
if (expected > -1 && expected < 1024)
break;
}
std::cout << "result:" << str << std::endl;;
}
int main()
{
std::string s = "123%s%d%s%s%s%s%s%0d";
test("%s%d",s.c_str());
}
上述测试样例的编译结果如下:
g++ test_format.cpp -Wall
test_format.cpp: In function ‘int main()’:
test_format.cpp:27: warning: too few arguments for format
如去掉__attribute__((format(printf, 1, 2))); 修饰编译结果将没有warning,可去掉自行测试。
在类成员函数中,string-index和first-to-check参数值要比实际的位置向后移动一位,因为类成员函数展开后第一个参数为this指针,如下面的测试样例string-index=2,first-to-check=3
// class_test_format.cpp
#include
#include
#include
#include
class Test
{
public:
void test(const char* format, ...) __attribute__((format(printf, 2, 3)));
};
void Test::test(const char* format, ...)
{
va_list ap;
std::string str;
while (true)
{
char* buffer = new char[1024];
va_start(ap, format);
int expected = vsnprintf(buffer, 1024, format, ap);
va_end(ap);
str += std::string(buffer);
if (expected > -1 && expected < 1024)
break;
}
std::cout << "result:" << str << std::endl;;
}
int main()
{
std::string s = "123%s%d%s%s%s%s%s%0d";
Test t;
t.test("%s%d",s.c_str());
}
上述测试样例的编译结果如下:
g++ class_test_format.cpp -Wall
class_test_format.cpp: In function ‘int main()’:
class_test_format.cpp:34: warning: too few arguments for format
如去掉__attribute__((format(printf, 2, 3))); 修饰编译结果将没有warning,可去掉自行测试。