__attribute__((format(printf, 2, 3)))用法詳解

_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,可去掉自行測試。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章