一些C的小技巧

数组的指定初始化

C99中可以按以下方法指定初始化数组:

inta[]={[3]=2,[10]=1};

数组大小会自动调整为与最大下标适应,未指定的元素初始化为0。

类似的方法可以应用在结构体与联合的初始化中。

地址:http://blog.jobbole.com/16035/

ANSI C中的一些预定义宏

FILE 源文件的名称 如XXX.cpp

LINE 代码在源文件中是第几行

DATE 源文件完成日期如Aug 17 2011

TIME 源文件完成时间如19:31:13

TIMESTAMP 源文件完成日期时间如Wed Aug 17 19:27:36 2011

地址:http://www.cnblogs.com/morewindows/archive/2011/08/17/2143523.html

C/C++宏中的#与

的功能是将其后面的宏参数进行字符串化操作(Stringfication),简单说就是在对它所引用的宏变量通过替换后在其左右各加上一个双引号。比如下面代码中的宏:

#define WARN_IF(EXP)

do{if(EXP)

fprintf(stderr, "Warning: "#EXP "

"); }while(0)

那么实际使用中会出现下面所示的替换过程:

WARN_IF (divider == 0);

被替换为

do{

if(divider == 0)

fprintf(stderr, "Warning" "divider == 0" "

");

}while(0);

还有一个#@是加单引号(Charizing Operator)

define makechar(x) #@x

char ch = makechar(b);与char ch = 'b';等价。

但要注意,宏中遇到###时就不会再展开宏中嵌套的宏了。比如使用

char *pChar = STRING(FILE);

虽然FILE本身也是一个宏,但编译器不会展开它,所以pChar将指向"FILE"而不是你要想的形如"D:\XXX.cpp"的源文件名称。因此要加一个中间转换宏,先将FILE解析成"D:\XXX.cpp"字符串。

定义如下所示二个宏:

define _STRING(x) #x

define STRING(x) _STRING(x)

再调用下面语句将输出带""的源文件路径

char* pChar = STRING(FILE);

printf("%s %s\n", pChar, FILE);

可以比较下STRING(FILE)与__FILE__的不同,前将带双引号,后一个没有双引号。

被称为连接符(concatenator),用来将两个Token连接为一个Token。

struct command

{

char * name;

void (*function) (void);

};

#define COMMAND(NAME) { NAME, NAME ## _command }



// 然后你就用一些预先定义好的命令来方便的初始化一个command结构的数组了:

struct command commands[] = {

COMMAND(quit),

COMMAND(help),

...

}

对__FILE__可以方便的转化成wchar_t类型,MSDN就有这个例子:

define WIDEN2(x) L ## x

define WIDEN(x) WIDEN2(x)

define WFILE WIDEN(FILE)

wchar_t *pwsz = WFILE;

地址:http://www.cnblogs.com/morewindows/archive/2011/08/18/2144112.html

变参宏...的使用

...在C宏中称为Variadic Macro,也就是变参宏。比如:

define myprintf(templt,...) fprintf(stderr,templt,VA_ARGS)

// 或者

define myprintf(templt,args...) fprintf(stderr,templt,args)

第一个宏中由于没有对变参起名,我们用默认的宏__VA_ARGS__来替代它。第二个宏中,我们显式地命名变参为args,那么我们在宏定义中就可以用 args来代指变参了。同C语言的stdcall一样,变参必须作为参数表的最有一项出现。当上面的宏中我们只能提供第一个参数templt时,C标准要 求我们必须写成:

myprintf(templt,);

的形式。这时的替换过程为:

myprintf("Error!",);

替换为:

fprintf(stderr,"Error!",);

这是一个语法错误,不能正常编译。这个问题一般有两个解决方法。首先,GNU CPP提供的解决方法允许上面的宏调用写成:

myprintf(templt);

而它将会被通过替换变成:

fprintf(stderr,"Error!",);

很明显,这里仍然会产生编译错误(非本例的某些情况下不会产生编译错误)。除了这种方式外,c99和GNU CPP都支持下面的宏定义方式:

define myprintf(templt, ...) fprintf(stderr,templt, ##VAR_ARGS)

这时,##这个连接符号充当的作用就是当__VAR_ARGS__为空的时候,消除前面的那个逗号。那么此时的翻译过程如下:

myprintf(templt);

被转化为:

fprintf(stderr,templt);

这样如果templt合法,将不会产生编译错误。

地址:http://www.kuqin.com/language/20080721/11906.html

宏中消除多余的分号

通常情况下,为了使函数模样的宏在表面上看起来像一个通常的C语言调用一样,通常情况下我们在宏的后面加上一个分号,比如下面的带参宏:

MY_MACRO(x);

但是如果是下面的情况:

define MY_MACRO(x) {

/ line 1 /

/ line 2 /

/ line 3 / }

//...

if (condition())

MY_MACRO(a);

else

{...}

这样会由于多出的那个分号产生编译错误。为了避免这种情况出现同时保持MY_MACRO(x);的这种写法,我们需要把宏定义为这种形式:

define MY_MACRO(x) do {

/ line 1 /

/ line 2 /

/ line 3 / } while(0)

这样只要保证总是使用分号,就不会有任何问题。

地址:http://www.kuqin.com/language/20080721/11906.html

C语言中的typeof关键字

typeof关键字是C语言中的一个新扩展。

typeof的参数可以是两种形式:表达式或类型。如果将typeof用于表达式,则该表达式不会执行。只会得到该表达式的类型。

以下示例声明了int类型的var变量,因为表达式foo()是int类型的。由于表达式不会被执行,所以不会调用foo函数。

extern int foo();

typeof(foo()) var;

下面是用类型作参数的例子:

typeof(int ) a,b;//等价于 int a,*b;

typeof(int [10]) a1, a2;// 等价于int a1[10], a2[10];

注意:typeof('b') a; /* GCC中这个表达式的类型是int(自动提升为int)

一般情况下用typeof就可以了,但是如果要于ISO C兼容的话,最好是用双下划线的形式:typeof。typeof和typedef很像,事实上,只要能用typedef的地方就可以用typeof。

下面是另外一些例子:

typeof(*x) y;// 把y定义成x指向的数据类型

typeof(*x) y[4];// 把y定义成x指向数据类型的数组

typeof(typeof(char *)[4]) y;// 把y定义成一个字符指针数组

我们再换一种定义方式:

define pointer(T) typeof(T *)

define array(T,N) typeof(T [N])

array (pointer(char),4) y;

如果想把T定义成一个表达式的类型,则我们仅仅用typedef无法做到,但可以通过typeof做到:

typdef typeof(expr) T;

请注意,typeof构造中的类型名不能包含存储类说明符,如extern或static。不过允许包含类型限定符,如const或volatile。

typeof构造的主要应用是用在宏定义中。可以使用typeof关键字来引用宏参数的类型。因此,在没有将类型名明确指定为宏实参的情况下,构造带有所需类型的对象是可能的。

下面是一个交换两个变量的值的宏定义:

#define SWAP(a,b) {\

typeof(a) _t=a;\

a=b;\

b=_t;}

这个宏可以交换所有基本数据类型的变量(整数,字符,结构等)。

typeof在协助内嵌表达式的声明时非常有用。这里演示一个如何定义一个安全(在任何情况下,各参数只会被计算一次)的求最大值的宏。

define max(a,b)

({

typeof (a) _a = (a);

typeof (b) _b = (b);

_a > _b ? _a : _b;

})

地址:http://module77.is-programmer.com/posts/22102.html

http://blog.csdn.net/cx132123/article/details/6641735

编译时断言

有些时候,特别是在进行内核编程时,在编译时就能够进行条件检查的断言,而不是在运行时进行,这非常有用。

我们可以利用预处理来生成代码,这些代码只有在某些条件成立时才会通过编译(最好是那种不做实际功能的命令)。有各种各样不同的方式都可以做到这一点,通常都是建立一个大小为负的数组或结构体。最常用的方式如下:

/* Force a compilation error if condition is false, but also produce a result

  • (of value 0 and type size_t), so it can be used e.g. in a structure

  • initializer (or wherever else comma expressions aren't permitted). */

/ Linux calls these BUILD_BUG_ON_ZERO/_NULL, which is rather misleading. /

define STATIC_ZERO_ASSERT(condition) (sizeof(struct { int:-!(condition); }) )

define STATIC_NULL_ASSERT(condition) ((void *)STATIC_ZERO_ASSERT(condition) )

/ Force a compilation error if condition is false /

define STATIC_ASSERT(condition) ((void)STATIC_ZERO_ASSERT(condition))

如果(condition)计算结果为一个非零值(即C中的真值),即! (condition)为零值,那么代码将能顺利地编译,并生成一个大小为零的结构体。如果(condition)结果为0(在C真为假),那么在试图生成一个负大小的结构体时,就会产生编译错误。

地址:http://blog.jobbole.com/16035/
原文链接: https://www.cnblogs.com/fz-ywj/archive/2013/03/07/2948674.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/79952

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年2月9日 下午7:18
下一篇 2023年2月9日 下午7:18

相关推荐