C語言冷知識之預處理字符串操作符詳解
當年學習C語言的第一門課就提到過標記(Token)的概念,不過,相信在多年之后你再次聽到這個術(shù)語時會一臉懵逼,比如我。
因此特地翻了翻資料,整理下來這些筆記。
在C語言中什么是標記
標記是編程語言處理的基本單元,也叫最小劃分元素,比如關(guān)鍵字、操作符、變量名、函數(shù)名、字符串、數(shù)值等等。
下面舉例說明一下:
printf("hello world!");
對上面的語句進行標記劃分,可分為5個標記,如下:
printf // 函數(shù)名 ( // 左小括號操作符 "hello world!" // 字符串 ) // 右小括號操作符 ; // 分號
預處理字符串操作符
在C語言中,預處理字符串操作符有兩個,#和##。
#字符串化操作符
用途是,將標記(Token)轉(zhuǎn)成字符串。
Syntax:
#define TOKEN_NAME(param) #param
Basic Usage:
#include <stdio.h>
#define MACRO_NAME(param) #param
int main()
{
printf(MACRO_NAME(hello world));
return 0;
}
Output:
hello world
在項目實踐中,用宏定義的值的同時也需要將宏名轉(zhuǎn)成字符串使用,對日志的輸出尤其管用。
Best Practice:
#include <stdio.h>
#define NAME(param) #param
#define LEN_MAX 10
int main()
{
int array[LEN_MAX] = {0};
int index = 10;
if (index >= LEN_MAX) {
printf("error: %s:%d is over %s:%d\n", NAME(index), index, NAME(LEN_MAX), LEN_MAX);
} else {
printf("read %s[%d]=%d\n", NAME(array), index, array[index]);
}
return 0;
}
Output:
error: index:15 is over LEN_MAX:10
如果修改如下:
int index = 9;
Output:
read array[9]=0
##標記(Token)連接操作符
用途是,將##前后的標記(Token)串接成新的單一標記。
syntax:
#define TOKEN_CONCATENATE(param1, param2) param1##param
Basic Usage:
#include <stdio.h>
#define TOKEN_CONCATENATE(param1, param2) param1##param2
int main()
{
printf("%d\n", TOKEN_CONCATENATE(12, 34));
return 0;
}
Output:
1234
通常,編碼實踐中,代碼中會出現(xiàn)一些書寫看上去雷同的片段,極其啰嗦冗余。為了壓縮源碼篇幅,可以參考代碼生成器的思想,在預編譯階段用宏定義代碼片段展開替換,同時根據(jù)輸入的參數(shù)用##組合各種標記。
假設(shè)有個需求是聲明定義一組同一類型的結(jié)構(gòu)體的變量,并初始化其內(nèi)部成員。既然聲明定義的這些變量屬于同一類型的結(jié)構(gòu)體,那么按照直接編碼的方式,就會有多次重復的代碼片段出現(xiàn),里邊包括了聲明定義語句,以及初始化各個成員的語句,不同的只是變量名或者參數(shù)而已。
舉個栗子,下面基于同一類型的結(jié)構(gòu)體,聲明定義兩個變量,并初始化,看代碼
#include <stdio.h>
#include <string.h>
#define NAME(param) #param
typedef struct {
char *data;
int data_size; /* number of byte real */
int max_size; /* maximnm data size.*/
} my_type;
#define my_type_create(name, size) \
char name ## _ ## data[size] = {0}; \
my_type name; \
memset(&name, 0x00, sizeof(name)); \
name.data = name ## _ ## data; \
name.max_size = size; \
printf("variable name=%s\nmember data=%s, data_size=%d, max_size=%d\n", \
NAME(name), NAME(name ## _ ## data), name.data_size, name.max_size); \
int main() {
my_type_create(var1, 10)
my_type_create(var2, 20)
}
上面的代碼中,定義了宏my_type_create,內(nèi)部實現(xiàn)了結(jié)構(gòu)體變量的聲明定義,以及內(nèi)部成員的初始化。如果按照直接編碼的方式,代碼量相對于上面的代碼量會虛增n-1倍,n=變量的個數(shù)。
在main函數(shù)中,調(diào)用宏的時候輸入?yún)?shù)var和10,那么在編譯預處理階段,根據(jù)輸入的參數(shù),宏my_type_create會展開為以下的代碼段。
char var_data[10] = {0}; \
my_type var; \
memset(&var, 0x00, sizeof(var)); \
var.data = var_data; \
var.max_size = 10; \
printf("variable name=%s\nmember data=%s, data_size=%d, max_size=%d", \
“var”, var_data, var.data_size, var.max_size); \
Output:
variable name=var1
member data=var1_data, data_size=0, max_size=10
variable name=var2
member data=var2_data, data_size=0, max_size=20
##還有個特殊的用途
在宏定義中,也支持用...代表可變參數(shù)。
#define MY_PRINT(fmt, ...) printf(fmt, __VA_ARGS__)
由于可變參數(shù)數(shù)目不確定,所以沒有具體的標記。于是為了引用可變參數(shù),語言層面提供了可變宏(Variadic macros)__VA_ARGS__來引用它。
但是,在宏定義時,如果直接使用__VA_ARGS__來引用可變參數(shù),一旦可變參數(shù)為空就會引起編譯器報錯,看看下面的例子
#include <stdio.h>
#define LOG_INFO(fmt, ...) printf("[I]" fmt "\n", __VA_ARGS__)
int main() {
LOG_INFO("info...");
LOG_INFO("%s, %s", "Hello", "world");
}
Output:
main.c: In function ‘main’:
main.c:3:62: error: expected expression before ‘)’ token
3 | #define LOG_INFO(fmt, ...) printf("[I]" fmt "\n", __VA_ARGS__)
| ^
main.c:6:3: note: in expansion of macro ‘LOG_INFO’
6 | LOG_INFO("info...");
| ^~~~~~~~
為了解決上面的問題,在__VA_ARGS__前面添加上##,這樣的目的是告訴預處理器,如果可變參數(shù)為空,那么前面緊跟者的逗號,在宏定義展開時會被清理掉。
到此這篇關(guān)于C語言冷知識之預處理字符串操作符詳解的文章就介紹到這了,更多相關(guān)C語言預處理字符串操作符內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C++中vector和數(shù)組之間的轉(zhuǎn)換及其效率問題詳解
c++?vector轉(zhuǎn)數(shù)組是一種將vector容器的元素轉(zhuǎn)換為數(shù)組的方法,主要能幫助提高程序的性能和效率,下面這篇文章主要給大家介紹了關(guān)于C++中vector和數(shù)組之間的轉(zhuǎn)換及其效率問題的相關(guān)資料,需要的朋友可以參考下2023-03-03
C++使用extern實現(xiàn)源文件變量與類成員函數(shù)的巧妙共享
C++中使用extern關(guān)鍵字可實現(xiàn)在源文件之間共享變量與類成員函數(shù),通過聲明變量或類在頭文件中,再在一個源文件中定義,其他源文件通過extern引用,促使模塊化、可維護的代碼組織,這篇文章主要介紹了C++用extern實現(xiàn)源文件變量與類成員函數(shù)的巧妙共享,需要的朋友可以參考下2024-03-03

