C笔记
# 1.关键字
# 1.register
register修饰符暗示编译程序相应的变量将被频繁地使用,如果可能的话,应将其保存在CPU的寄存器中,以加快其存储速度。
几点限制如下:
(1)register变量必须是能被CPU所接受的类型。
这通常意味着register变量必须是一个单个的值,并且长度应该小于或者等于整型的长度。不过,有些机器的寄存器也能存放浮点数。
(2)==因为register变量可能不存放在内存中,所以不能用“&”来获取register变量的地址。==
(3)==只有局部自动变量和形式参数可以作为寄存器变量,其它(如全局变量)不行。==
在调用一个函数时占用一些寄存器以存放寄存器变量的值,函数调用结束后释放寄存器。此后,在调用另外一个函数时又可以利用这些寄存器来存放该函数的寄存器变量。
(4)局部静态变量不能定义为寄存器变量。不能写成:register static int a, b, c;
(5)由于寄存器的数量有限(不同的cpu寄存器数目不一),不能定义任意多个寄存器变量,而且某些寄存器只能接受特定类型的数据(如指针和浮点数),因此真正起作用的register修饰符的数目和类型都依赖于运行程序的机器,而任何多余的register修饰符都将被编译程序所忽略。
注意:
早期的C编译程序不会把变量保存在寄存器中,除非你命令它这样做,这时register修饰符是C语言的一种很有价值的补充。然而,随着编译程序设计技术的进步,在决定哪些变量应该被存到寄存器中时,现在的C编译环境能比程序员做出更好的决定。实际上,许多编译程序都会忽略register修饰符,因为尽管它完全合法,但它仅仅是暗示而不是命令。
# 2.标准操作
# 1.设置大小端
static void setEndian(void) {
int n = 0x11223344;
char *c = (char *)&n;
if(c[0] == 0x11) {
disksim->endian = _BIG_ENDIAN;
}
else {
disksim->endian = _LITTLE_ENDIAN;
}
}
2
3
4
5
6
7
8
9
10
11
12
# 3. Llinux中结构体对齐
参考: https://www.cnblogs.com/sky-heaven/p/9712960.html
// 根据编译器的对齐方式不同,下面以8字节对齐为例
// 注释数字的单位是字节
// GCC支持用__attribute__为变量、类型、函数、标签指定特殊属性。
// 可以指定结构体是按字节对齐还是紧凑压缩。
struct __attribute__ ((__packed__)) a {
int aa[2];
char bb[3];
};
struct __attribute__ ((aligned(8))) aa {
int aa[2];
char bb[3];
};
struct MemAlign1 // 如果是八字节对齐,注意如果可以继续放,就继续放
{
char a[18]; // 18 bytes -> 24bytes
double b; // 08 bytes -> 8bytes
int d; // 04 bytes -> 4/8 bytes
short e; // 02 bytes -> 2/8 bytes
char c; // 01 bytes -> 1/8 bytes
char g; // 01 bytes -> 1/8 bytes
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 4.标准库函数
# 1.动态内存相关
# 1.需要动态分配已存在数组的大小 【 realloc 】
//头文件: <string.h>
//void *realloc(void *ptr, size_t size)
//尝试重新调整之前调用 malloc 或 calloc 所分配的 ptr 所指向的内存块的大小。
char *str;
/* 最初的内存分配 */
str = (char *) malloc(15);
strcpy(str, "runoob");
printf("String = %s, Address = %p\n", str, str);
/* 重新分配内存 */
str = (char *) realloc(str, 25);
strcat(str, ".com");
printf("String = %s, Address = %p\n", str, str);
free(str);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 2.动态创建数组 【calloc】
// 分配所需的内存空间,并返回一个指向它的指针。malloc 和 calloc 之间的不同点是,malloc 不会设置内存为零,而 calloc 会设置分配的内存为零。
int *a;
a = (int*)calloc(n, sizeof(int));
a = (int*)malloc(sizeof(int) * n);
2
3
4
# 5. 混淆区分
# 1. strcpy和strncpy
1.strcpy函数:顾名思义字符串复制函数:原型:extern char *strcpy(char *dest,char *src); 功能:把从src地址开始且含有NULL结束符的字符串赋值到以dest开始的地址空间,返回dest(地址中存储的为复制后的新值)。要求:src和dest所指内存区域不可以重叠且dest必须有足够的空间来容纳src的字符串。
该函数的参数是字符指针,也就是可以是字符串变量和字符数组,因为它们的变量名代表首字符地址。字符串默认有一个null结束符,字符数组没有。所以此处需要注意:因为src要求有null结束符,所以字符数组的长度必须大于等于src包含null结束符的总长度。例如,char* src="abcd"; char dest[5]; 这里dest的长度就至少为5。
2.strncpy函数:多个n代表可以指定字符个数进行赋值。原型:char * strncpy(char *dest, char src, size_tn); 功能:将字符串src中最多n个字符复制到字符数组dest中(它并不像strcpy一样遇到NULL才停止复制,而是等凑够n个字符才开始复制),返回指向dest的指针。要求:如果n > dest串长度,dest栈空间溢出产生崩溃异常。该函数注意的地方和strcpy类似,但是n值需特别注意,
1)src串长度<=dest串长度,(这里的串长度包含串尾NULL字符)
如果n=(0, src串长度),src的前n个字符复制到dest中。但是由于没有NULL字符,所以直接访问dest串会发生栈溢出的异常情况。这时,一般建议采取memset将dest的全部元素用null填充,如:memset(dest,0,7)(7为从dest起始地址开始前7个位置填充null,dest可以为字符指针和数组名)。注意:char pc="abc"; char chs[5]; sizeof(pc)为4(包含null)(有些编译器不行),sizeof(chs)为5。
如果n = src串长度,与strcpy一致。
如果n = dest串长度,[0,src串长度]处存放于desk字串,(src串长度, dest串长度]处存放NULL。
2)src串长度>dest串长度
如果n =dest串长度,则dest串没有NULL字符,会导致输出会有乱码。如果不考虑src串复制完整性,可以将dest最后一字符置为NULL。
所以,一般把n设为dest(含null)的长度(除非将多个src复制到dest中)。当2)中n=dest串长度时,定义dest为字符数组,因为这时没有null字符拷贝。
# 2.(\n\r)和换行(\r)的区别
在Windows中:
'\r' 回车,回到当前行的行首,而不会换到下一行,如果接着输出的话,本行以前的内容会被逐一覆盖;
'\n' 换行,换到当前位置的下一行,而不会回到行首;
Unix系统里,每行结尾只有“<换行>”,即"\n";Windows系统里面,每行结尾是“<回车><换行>”,即“\r\n”;Mac系统里,每行结尾是“<回车>”,即"\r";。一个直接后果是,Unix/Mac系统下的文件在Windows里打开的话,所有文字会变成一行;而Windows里的文件在Unix/Mac下打开的话,在每行的结尾可能会多出一个^M符号。
这里首先要理解:\n是换行,就是光标移动到下一行,\r是讲光标移动到本行的开始处。
printf("hello %d\r\n",NoPressKeyTime);这样就可以在串口软件上自动换行了。
# 3. 取模运算和取余运算
对于整型数a,b来说,取模运算或者求余运算的方法都是:
1.求整数商: c = a/b;
2.计算模或者余数: r = a - c*b.
求模运算和求余运算在第一步不同: 取余运算在取c的值时,向0方向舍入(fix()函数);而取模运算在计算c的值时,向**-∞**方向舍入(floor()函数)。
例如:计算-7 Mod 4
那么:a = -7;b = 4;
第一步:求整数商c,如进行求模运算c = -2(向无穷小方向舍入),求余c = -1(向0方向舍入);
第二部:计算模和余数的公式相同,但因c的值不同,求模时r = 1,求余时r = -3。
**归纳:**当a和b符号一致时,求模运算和求余运算所得的c的值一致,因此结果一致。
当符号不一致时,结果不一样。求模运算结果的符号和b一致,求余运算结果的符号和a一致。
另外各个环境下%运算符的含义不同,比如c/c++ 为取余,而java则为取模。
在C语言中,%符号表示的是求余运算,在python脚本中,%表示的是取模。
下面表格是一些典型的取模或者求余的值。
a | b | C语言:a%b (求余) | Python Shell: a%b(取模) |
---|---|---|---|
-3 | -5 | -3 | -3 |
-3 | 4 | -3 | 1 |
-3 | 2 | -1 | 1 |
-1 | 6 | -1 | 5 |
-4 | -3 | -1 | -1 |
2 | 4 | 2 | 2 |
5 | 3 | 2 | 2 |
4 | -7 | 4 | -3 |
4 | -3 | 1 | -2 |
-6 | -5 | -1 | -1 |