_attribute_((format(printf, 2, 3)))用法詳解
在閱讀redis源碼的時候,遇到_attribute_((format(printf, 2, 3)))用法,在此記錄下該用法的作用以及需要注意的點。
1. 用法及作用
在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指針,這個也要考慮到位置中。
2.使用示例
2.1 普通函數使用示例
在普通函數中,string-index和first-to-check參數值即爲實際的位置,如下面的測試樣例string-index=1,first-to-check=2
// test_format.cpp
#include <iostream>
#include <stdarg.h>
#include <stdint.h>
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,可去掉自行測試。
2.2 類成員函數使用示例
在類成員函數中,string-index和first-to-check參數值要比實際的位置向後移動一位,因爲類成員函數展開後第一個參數爲this指針,如下面的測試樣例string-index=2,first-to-check=3
// class_test_format.cpp
#include <iostream>
#include <stdarg.h>
#include <stdint.h>
#include <string>
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,可去掉自行測試。