的tricks不多,但大多比较晦涩,就跟数学一样需要循序渐进的理解,同时也需要对编译器、C语言标准都有一定的理解。而C语言的雷区较多,也即有很多我们经常碰到的未定义行为(undefined behavior)。在看tricks前,我们先看个雷:
1
|
a
=
b
+
c
;
|
它看起来很简单,对吗?但如果b和c加起来大于了上限,如INT_MAX,那编译器会做什么?事实上这里会得到一个负数。这是非常令人烦躁的未定义举动,尤其是你因为它而要debug的时候。当然,标准会告诉你每次都去检查它没有意义,我们要的只是一个高速语言。恩,读者要记住一件事,玩标准的人不写代码。而且,千万不要轻易的忽略它们,它们都有可能造成致命的问题。——所以说C语言是高手才能真正玩好的语言。如果没有对C语言较好的了解,不要轻易的使用各类tricks,说不定里面就埋了个debug不出来的雷。
我们开始,先看个小技巧,舒缓一下心情:
1
2
|
#define likely(x) __builtin_expect((x),1)
#define unlikely(x) __builtin_expect((x),0)
|
这个技巧就是在循环判断时去期望是或否。这涉及到了编译器优化,以及很经典的火车选路。在linux内核中十分常见。你可以给知道它的人加5分,懂一点内核挺好的。
再来一个我们在强类型时不能忽略的:
1
2
3
4
5
6
7
8
9
10
11
12
|
#ifndef __int8_t_defined
# define __int8_t_defined
typedef
signed
char
int8_t
;
typedef
short
int
int16_t
;
typedef
int
int32_t
;
# if __WORDSIZE == 64
typedef
long
int
int64_t
;
# else
__extension__
typedef
long
long
int
int64_t
;
# endif
#endif
|
这些类型非常棒,比起char, short, int, long的意思清晰十倍,尤其在bitmap运算时,最好用这些强类型(记得在算bits时要用UINT)。
但你可以看到项目组里往往用
1
2
|
#define INT16 short
#define INT32 long
|
这样的代码来定义它们,实际上是错误的,乖乖的#include
像这样用是可以的。注意逗号取最后一个值返回。
1
2
3
4
|
for
(
int
i
=
0
;
i
&
lt
;
10
;
i
++
,
doSomethingElse
(
)
)
{
/* whatever */
}
|
注意它不是一般意义上的全0初始化,而是逻辑0初始化,这里引用一段stackoverflow上的英文:
memset
/calloc
do “all bytes zero” (i.e. physical zeroes), which is indeed not defined for all types. { 0 }
is guaranteed to intilaize everything with proper logical zero values. Pointers, for example, are guranteed to get their proper null values, even if the null-value on the given platform is 0xBAADFOOD
1
|
struct
mystruct
a
=
{
0
}
;
|
很有用的bit定义,尤其是用在某些算法中
1
2
3
4
5
6
7
8
9
10
11
12
13
|
struct
cat
{
unsigned
int
legs
:
3
;
// 3 bits for legs (0-4 fit in 3 bits)
unsigned
int
lives
:
4
;
// 4 bits for lives (0-9 fit in 4 bits)
// ...
}
;
cat
make_cat
(
)
{
cat
kitty
;
kitty
.
legs
=
4
;
kitty
.
lives
=
9
;
return
kitty
;
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
FSM
{
STATE
(
x
)
{
.
.
.
NEXTSTATE
(
y
)
;
}
STATE
(
y
)
{
.
.
.
if
(
x
==
0
)
NEXTSTATE
(
y
)
;
else
NEXTSTATE
(
x
)
;
}
}
|
that can be achieved with the following macros:
1
2
3
|
#define FSM
#define STATE(x) s_##x :
#define NEXTSTATE(x) goto s_##x
|
Interlacing structures like Duff’s Device:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
strncpy
(
to
,
from
,
count
)
char
*
to
,
*
from
;
int
count
;
{
int
n
=
(
count
+
7
)
/
8
;
switch
(
count
%
8
)
{
case
0
:
do
{
*
to
=
*
from
++
;
case
7
:
*
to
=
*
from
++
;
case
6
:
*
to
=
*
from
++
;
case
5
:
*
to
=
*
from
++
;
case
4
:
*
to
=
*
from
++
;
case
3
:
*
to
=
*
from
++
;
case
2
:
*
to
=
*
from
++
;
case
1
:
*
to
=
*
from
++
;
}
while
(
--
n
&
gt
;
0
)
;
}
}
|
很好用的技巧,GCC早期已经实现,后合入C99,但其他很多编译器没有实现
1
2
3
4
5
6
7
|
#define FOO 16
#define BAR 3
myStructType_t
myStuff
[
]
=
{
[
FOO
]
=
{
foo1
,
foo2
,
foo3
}
,
[
BAR
]
=
{
bar1
,
bar2
,
bar3
}
,
.
.
.
|
同上:
1
2
3
4
5
6
7
8
9
|
struct
foo
{
int
x
;
int
y
;
char
*
name
;
}
;
void
main
(
)
{
struct
foo
f
=
{
.
y
=
23
,
.
name
=
"awesome"
,
.
x
=
-
38
}
;
}
|
1
2
|
int
my_printf
(
void
*
my_object
,
const
char
*
my_format
,
.
.
.
)
__attribute__
(
(
format
(
printf
,
2
,
3
)
)
)
;
|
1
2
3
4
5
6
7
8
|
#include <stdio.h>
int
main
(
)
{
int
a
=
3
;
float
b
=
6.412355
;
printf
(
"%.*f\n"
,
a
,
b
)
;
return
0
;
}
|
编译时而非运行时
1
2
3
4
5
6
7
8
|
//--- size of static_assertion array is negative if condition is not met
#define STATIC_ASSERT(condition) \
typedef
struct
{
\
char
static_assertion
[
condition
?
1
:
-
1
]
;
\
}
static_assertion_t
//--- ensure structure fits in
STATIC_ASSERT
(
sizeof
(
mystruct_t
)
&
lt
;
=
4096
)
;
|
1
2
|
#define PATH "/some/path/"
fd
=
open
(
PATH
"/file"
,
flags
)
;
|
1
2
3
4
5
6
7
|
struct
{
int
a
:
3
;
int
b
:
2
;
int
:
0
;
int
c
:
4
;
int
d
:
3
;
}
;
|
which will give a layout of
1
|
000aaabb
0ccccddd
|
instead of without the :0;
1
|
0000aaab
bccccddd
|
The 0 width field tells that the following bitfields should be set on the next atomic entity (char
)
Lambda’s (e.g. anonymous functions) in GCC:
1
2
|
#define lambda(return_type, function_body) \
(
{
return_type
fn
function_body
fn
}
)
|
This can be used as:
1
|
lambda
(
int
,
(
int
x
,
int
y
)
{
return
x
&
gt
;
y
;
}
)
(
1
,
2
)
|
Which is expanded into:
1
|
(
{
int
fn
(
int
x
,
int
y
)
{
return
x
&
gt
;
y
}
fn
;
}
)
(
1
,
2
)
|
1
|
hexDigit
=
"0123456789abcdef"
[
someNybble
]
;
|
1
2
3
|
double
normals
[
]
[
]
=
{
#include "normals.txt"
}
;
|
1
|
#define WHERE fprintf(stderr,"[LOG]%s:%d\n",__FILE__,__LINE__);
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
|
/*
* X Macro() data list
* Format: Enum, Value, Text
*/
#define X_ERROR \
X
(
ERROR_NONE
,
1
,
"Success"
)
\
X
(
ERROR_SYNTAX
,
5
,
"Invalid syntax"
)
\
X
(
ERROR_RANGE
,
8
,
"Out of range"
)
/*
* Build an array of error return values
* e.g. {0,5,8}
*/
static
int
ErrorVal
[
]
=
{
#define X(Enum,Val,Text) Val,
X_ERROR
#undef X
}
;
/*
* Build an array of error enum names
* e.g. {"ERROR_NONE","ERROR_SYNTAX","ERROR_RANGE"}
*/
static
char
*
ErrorEnum
[
]
=
{
#define X(Enum,Val,Text) #Enum,
X_ERROR
#undef X
}
;
/*
* Build an array of error strings
* e.g. {"Success","Invalid syntax","Out of range"}
*/
static
char
*
ErrorText
[
]
=
{
#define X(Enum,Val,Text) Text,
X_ERROR
#undef X
}
;
/*
* Create an enumerated list of error indexes
* e.g. 0,1,2
*/
enum
{
#define X(Enum,Val,Text) IDX_##Enum,
X_ERROR
#undef X
IDX_MAX
/* Array size */
}
;
void
showErrorInfo
(
void
)
{
int
i
;
/*
* Access the values
*/
for
(
i
=
0
;
i
&
lt
;
IDX_MAX
;
i
++
)
printf
(
" %s == %d [%s]\n"
,
ErrorEnum
[
i
]
,
ErrorVal
[
i
]
,
ErrorText
[
i
]
)
;
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
/*
* Test validity of an error value
* case ERROR_SUCCESS:
* case ERROR_SYNTAX:
* case ERROR_RANGE:
*/
switch
(
value
)
{
#define X(Enum,Val,Text) case Val:
X_ERROR
#undef X
printf
(
"Error %d is ok\n"
,
value
)
;
break
;
default
:
printf
(
"Invalid error: %d\n"
,
value
)
;
break
;
}
|