- 代码中的do{}while(0)可避免使用goto语句
- 宏定义中的do{}while(0)可帮助定义复杂的宏以避免错误
避免由空宏引起的警告
1
#define EMPTYMICRO do{}while(0)
定义单一的函数块来完成复杂的操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15如果你有一个复杂的函数,变量很多,而且你不想要增加新的函数,可以使用do{...}while(0),将你的代码写在里面,里面可以定义变量而不用考虑变量名会同函数之前或者之后的重复。
这种情况应该是指一个变量多处使用(但每处的意义还不同),我们可以在每个do-while中缩小作用域。
int key;
string value;
int func()
{
int key = GetKey();
string value = GetValue();
dosomething for key,value;
do{
int key;string value;
dosomething for this key,value;
}while(0);
}
原码,反码和补码
http://www.itwendao.com/article/detail/8172.html
https://yq.aliyun.com/wenji/72426
http://blog.csdn.net/liushuijinger/article/details/7429197
https://www.cnblogs.com/zhangziqiu/archive/2011/03/30/ComputerCode.html
http://blog.chinaunix.net/uid-29603939-id-4216336.html
http://blog.sina.com.cn/s/blog_6b87c7eb010186mp.html1
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
64
65
66我们知道在计算机系统中,数值一律用补码来表示(存储)。主要原因是使用补码,可以将符号位和其它位统一处理;同时,减法也可按加法来处理。另外,两个用补码表示的数相加时,如果最高位(符号位)有进位,则进位被舍弃。
补码表示法(two's complement representation)可以防止0的机器数重码,同时又解决了原码和反码无法表示-8的问题,这样就极大的简化了计算机的硬件设计。
什么是数制?用来表示数值的规则,如十进制,二进制.
机器数
用二进制的形式来表示数;最高位为符号位,1表示负数,0表示正数.
如: 1---->00000001;-1---->1000 0001
真值
从上面我们知道了机器数是计算机用来表示数的一种形式,但是用于最高位是符号位。这样就出现这样情况,1000 0001表示的真正数值为1,而不是129.这样我们把一个机器数表示的真正的实际的意义的值称之为真值。
原码、反码、补码
总体上来讲原码反码补码都是计算机本身用来存储数的一种方式。
原码
规则:符号位+真值的绝对值
如:[+1]原码=0000 0001;
[-1]原码=1000 0001;
反码
规则:正数反码为本身
负数反码为原码的取反,但符号位不变
如:[+1]原码=[+1]反码=0000 0001;
[-1]原码=1000 0001=[-1]反码=1111 1110;
补码
规则:正数补码为本身
负数补码为原码的反码的基础之上加1
如:[+1]原码=[+1]补码;
[-1]原码=1000 0001=[-1]反码=1111 1110=[-1]补码=1111 1111;
为何会出现三种表示方式?
通过上面三种我们得到正数1在各种情况下都是一致的:[+1]原\反\补码=0000 00001;
而反码不是一致的。
在计算机内部,为了将运算变得简单。将符号位也投入到运算之中来,这样不需要计算机去分析符号位的作用。但是我们发现
[+1]原码=0000 0001+[-1]原码=1000 0001=0000 00001=1;而不是0,这样一来就出现了错误。如何解决这个问题呢?
为了解决这个问题,反码出现了
[+1]原码+[-1]原码=1000 0001=[+1]反码=0000 0001+[-1]反码=1111 1110=0000 0000(反码)=0000 0000 (原码)=-0;
这样一来解决了原码之间运算的问题,但是问题又出现了-0=1000 0000(原码);+0=0000 0000 (原码);这样在计算机看来不一样。是没有意义的。
[+1]原码+[-1]原码=1000 0001=[+1]补码=0000 0001+[-1]补码=1111 1111=0000 0000(补码)=[0000 0000](原码)
这样用[0000 0000](原码)表示0就不会出现问题了。
总的来说,在整数中,原码和补码、反码都是一样的,对于负数反码在符号位不变的情况下各位取反所得;补码为反码基础上加1.而原码补码反码的出现都是为了满足在计算机在运算过程更加简单和便捷而出现的。
浮点数在内存中存放格式
http://blog.sina.com.cn/s/blog_77abbf390100qwun.html
https://www.pediy.com/kssd/pediy03/forum669.htm
https://www.2cto.com/kf/201708/672079.html
http://blog.csdn.net/PfanAya/article/details/5770005
https://yq.aliyun.com/ziliao/123603
http://blog.csdn.net/hdutigerkin/article/details/76634541
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
64
65
66
67
68
69
70
71
72
73
74
75
76
77对于大小为32-bit的浮点数(32-bit为单精度,64-bit浮点数为双精度,80-bit为扩展精度浮点数),
1、其第31 bit为符号位,为0则表示正数,反之为复数,其读数值用s表示;
2、第30~23 bit为幂数,其读数值用e表示;
3、第22~0 bit共23 bit作为系数,视为二进制纯小数,假定该小数的十进制值为x;
则按照规定,该浮点数的值用十进制表示为:
= (-1)^s * (1 + x) * 2^(e - 127)
对于49E48E68来说,
1、其第31 bit为0,即s = 0
2、第30~23 bit依次为100 1001 1,读成十进制就是147,即e = 147。
3、第22~0 bit依次为110 0100 1000 1110 0110 1000,也就是二进制的纯小数0.110 0100 1000 1110 0110 1000,其十进制形式为0.78559589385986328125,即x = 0.78559589385986328125。
这样,该浮点数的十进制表示
= (-1)^s * (1 + x) * 2^(e - 127)
= (-1)^0 * (1+ 0.78559589385986328125) * 2^(147-127)
= 1872333
任何数据在内存中都是以二进制的形式存储的,例如一个short型数据1156,其二进制表示形式为00000100 10000100。则在Intel CPU架构的系统中,存放方式为 10000100(低地址单元) 00000100(高地址单元),因为Intel CPU的架构是小端模式。但是对于浮点数在内存是如何存储的?目前所有的C/C++编译器都是采用IEEE所制定的标准浮点格式,即二进制科学表示法。
在二进制科学表示法中,S=M*2^N 主要由三部分构成:符号位+阶码(N)+尾数(M)。对于float型数据,其二进制有32位,其中符号位1位,阶码8位,尾数23位;对于double型数据,其二进制为64位,符号位1位,阶码11位,尾数52位。
31 30-23 22-0
float 符号位 阶码 尾数
63 62-52 51-0
double 符号位 阶码 尾数
符号位:0表示正,1表示负
阶码:这里阶码采用移码表示,对于float型数据其规定偏置量为127,阶码有正有负,对于8位二进制,则其表示范围为-128-127,double型规定为1023,其表示范围为-1024-1023。比如对于float型数据,若阶码的真实值为2,则加上127后为129,其阶码表示形式为10000010
尾数:有效数字位,即部分二进制位(小数点后面的二进制位),因为规定M的整数部分恒为1,所以这个1就不进行存储了。
下面举例说明:
float型数据125.5转换为标准浮点格式
125二进制表示形式为1111101,小数部分表示为二进制为 1,则125.5二进制表示为1111101.1,由于规定尾数的整数部分恒为1,则表示为1.1111011*2^6,阶码为6,加上127为133,则表示为10000101,而对于尾数将整数部分1去掉,为1111011,在其后面补0使其位数达到23位,则为11110110000000000000000
则其二进制表示形式为
0 10000101 11110110000000000000000,则在内存中存放方式为:
00000000 低地址
00000000
11111011
01000010 高地址
而反过来若要根据二进制形式求算浮点数如0 10000101 11110110000000000000000
由于符号为为0,则为正数。阶码为133-127=6,尾数为11110110000000000000000,则其真实尾数为1.1111011。所以其大小为
1.1111011*2^6,将小数点右移6位,得到1111101.1,而1111101的十进制为125,0.1的十进制为1*2^(-1)=0.5,所以其大小为125.5。
同理若将float型数据0.5转换为二进制形式
0.5的二进制形式为0.1,由于规定正数部分必须为1,将小数点右移1位,则为1.0*2^(-1),其阶码为-1+127=126,表示为01111110,而尾数1.0去掉整数部分为0,补齐0到23位00000000000000000000000,则其二进制表示形式为
0 01111110 00000000000000000000000
由上分析可知float型数据最大表示范围为1.11111111111111111111111*2^127=3.4*10^38
对于double型数据情况类似,只不过其阶码为11位,偏置量为1023,尾数为52位。
测试程序:
复制代码 代码如下:
/*测试浮点型数据在内存中存放方式 2011.10.2*/
#include <iostream>
using namespace std;
int main(int argc, char *argv[])
{
float a=125.5;
char *p=(char *)&a;
printf("%d\n",*p);
printf("%d\n",*(p+1));
printf("%d\n",*(p+2));
printf("%d\n",*(p+3));
return 0;
}
输出结果为:
0
0
-5
66
在上面已经知道float型125.5在内存中存放方式为:
00000000 低地址
00000000
11111011
01000010 高地址
因此对于p和p+1指向的单元,其中存储的二进制数表示的十进制整数为0;
而对于p+2指向的单元,由于为char型指针,为带符号的数据类型,因此11111011,符号位为1,则为负数,由于在内存中二进制是以补码存储的,所以其真值为-5.
对于p+3指向的单元,01000010,为正数,则其大小为66。上面程序输出结果验证了其正确性。
C数组初始化
http://blog.csdn.net/sibylle/article/details/2026915
http://lib.csdn.net/article/c/19784
http://www.cnblogs.com/youxin/p/3235817.html
http://www.cnblogs.com/Harley-Quinn/p/6766705.html1
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
34int a[100] = {0}; // 第一个元素初始化为0,后面的元素进行默认初始化,为0
int a[100] = {1, 2, 3, 4}; //前四个元素依次初始化为1,2,3,4;后面的所有元素进行默认初始化,为0
int a[] = {1,2,3,4,5}; // 1, 2, 3, 4, 5
int chr[] = {'a', 'b', 'c'}; // a, b, c, \0
初始化值的个数可少于数组元素个数.当初始化值的个数少于数组元素个数时,前面的按序初始化相应值, 后面的初始化为0(全局或静态数组)或为不确定值(局部数组).
一直以为 int a[256]={0};是把a的所有元素初始化为0,int a[256]={1};是把a所有的元素初始化为1.
调试的时查看内存发现不是那么一回事,翻了一下《The C++ Programming Language》总算有定论。PDF的竟然不然复制,就把它这章翻译了,如下
5.2.1 数组初始化
数组可以用一个列值来初始化,例如
int v1[] ={1,2,3,4};
char v2[]={'a','b','c',0};
当数组定义时没有指定大小,当初始化采用列表初始化了,那么数组的大小由初始化时列表元素个数决定。所以v1和v2分别为 int[4] 和char[4]类型。如果明确指定了数组大小,当在初始化时指定的元素个数超过这个大小就会产生错误。例如:
char v3[2] ={'a','b',0}; //错误:太多的初始化值了
char v3[3] ={'a','b',0}; //正确
如果初始化时指定的的元素个数比数组大小少,剩下的元素都回被初始化为 0。例如
int v5[8]={1,2,3,4};
等价于
int v5[8]={1,2,3,4,0,0,0,0};
注意没有如下形式的数组赋值:
void f()
{
v4={'c','d',0}; //错误:不是数组赋值
}
如果你想这样的复制的话,请使用 vector(16章第三节) 或者 valarray(22章第四节)。
字符数组可以方便地采用字符串直接初始化(参考第五章 2.2小节)
译注: 就是 这样啦 char alpha []="abcdefghijklmn";
*/
结构体成员赋值
1 | struct CMUnitTestState { |
C语言实现变长数组
1 | typedef struct |
特殊符号”#、##”
1 | http://www.cnblogs.com/Anker/p/3418792.html |
strstr和strchr的区别
http://blog.csdn.net/wusuopubupt/article/details/387410151
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通过函数的定义来区分:
1.strstr:
[cpp] view plain copy
char *strstr(const char *haystack, const char *needle)
可见,strstr函数搜索的是一个const char*型的数据,即字符串常量
2.strchr:
[cpp] view plain copy
char *strchr(const char *str, int c)
而strchr搜索的是一个int型的数据,即字符
3.strrchr
[cpp] view plain copy
char *strrchr(const char *str, int c)
另外,strrchr返回字符c在字符串str中最后出现的位置
__FILE__, __LINE__ and __func__
1 | https://gcc.gnu.org/onlinedocs/cpp/Standard-Predefined-Macros.html |
do{…}while(0)
http://www.cnblogs.com/lizhenghn/p/3674430.html
空语句
1 | if (condition) |
gcc -Wl option and Link wrap option
https://gcc.gnu.org/onlinedocs/gcc-7.1.0/gcc/Link-Options.html
https://sourceware.org/binutils/docs/ld/Options.html
hogan@ubuntu:/tmp/chef_wrap$ gcc -o waiter_test_wrap chef.c waiter_test_wrap.c -Wl,–wrap,chef_cook -lcmocka
hogan@ubuntu:/tmp/chef_wrap$ gcc -o waiter_test_wrap chef.c waiter_test_wrap.c -Wl,–wrap=chef_cook -lcmocka
hogan@ubuntu:~/share/framework/cmocka/example/chef_wrap$ gcc -o waiter_test_wrap waiter_test_wrap.c chef.c -lcmocka -Wl,–wrap=chef_cook
1 | Wl: Link Options |
大小端转换的宏
1 | typedef unsigned short int uint16; |
一段代码判断大小端
1 | #include <stdio.h> |
GCC编译动态链接库
1 | gcc stub.c -fPIC -shared -o libstub.so |
结构体初始化
1 | struct point { |
数组名取地址
1 | 在C语言中,对数组名取地址,得到的地址也是数组第一个元素的地址,如果对这个地址执行+1操作,这个+1的步长是整个数组的长度。即对整个数组取地址。可以对比结构体指针理解。 |