c/c++ 奇技淫巧(一些c语言的技巧)

2020-01-06 16:59:17刘景俊

一. 变长数组

严格说来,变长数组的实现在c++中并不是一件麻烦的事情。Stl中的vector本身就是一个变长数组,并且有自动管理内存的能力。
但是在c中,实现变长数组就稍显麻烦。用C实现,必然需要一个结构,结构当中应当有一个指针,指针分配一段内存空间,空间大小根据需要而定,而且必须有另外一个字段记录究竟开辟了多大多长的空间。
大致描述如下:


Struct MutableLenArray
{
  Int count;
  Char* p;
};

P = new Char[Count];

没什么问题,但是C语言的使用者有个最大的自豪就在于对于效率、空间使用的掌控。他们会有这样的疑问,如果count=0,那么p就没必要了,白白占了4(64位系统为8)个字节的空间,简直浪费。
那有没有更好的方式能实现上面的需求,又保证空间合理呢?答案是有的,用0长度


Struct MutableLenArray 
{ 
Int count; 
Char p[0]; 
}; 

和上面的结构使用方法一致,但是我们可以用sizeof尝试读取其大小,发现竟然只有count字段的长度4字节,p没有被分配空间。完美!

二. 宏的妙用

1. #和

“#”符号把一个符号直接转换为字符串,例如:


#define TO_STRING(x) #x 
const char *str = TO_STRING( test ); 

str的内容就是”test “,也就是说#会把其后的符号 直接加上双引号。
这个特性为c++反射的实现提供了极大便利,可以参考博主的下一篇文章,c++反射的简单实现。

##符号会连接两个符号,从而产生新的符号(词法层次),例如:


#define SIGN( x ) INT_##x 
  int SIGN( 1 ); 

宏被展开后将成为:int INT_1;
可以把##看成连字符,连字符为则为新符号的产生提供了方便。Google的Gtest框架就巧妙的运用了连字符来生成新的测试案例。

2. 变参宏


#define LOG( format, ... ) printf( format, __VA_ARGS__ ) 
  LOG( "%s %d", str, count ); 

VA_ARGS是系统预定义宏,被自动替换为参数列表。
经常需要进行输出格式化,重定义时,可以用到以上技巧。

3. 宏参数的prescan

prescan的定义:当一个宏参数被放进宏体时,这个宏参数会首先被全部展开(有例外,见下文)。当展开后的宏参数被放进宏体时, 预处理器对新展开的宏体进行第二次扫描,并继续展开。例如:


#define PARAM( x ) x 
  #define ADDPARAM( x ) INT_##x 
  PARAM( ADDPARAM( 1 ) ); 

因为ADDPARAM( 1 ) 是作为PARAM的宏参数,所以先将ADDPARAM( 1 )展开为INT_1,然后再将INT_1放进PARAM。
例外情况是,如果PARAM宏里对宏参数使用了#或##,那么宏参数不会被展开: