c lang misc
c lang misc
补码
正数的补码就是其本身
负数的补码就是在其原码的基础上,符号位不变,取反后加一。
补码存在的意义就是为了统一计算机加减
比如(7)+(-7)=0
7 -> 0000 0111(7的补码)
-7 -> 1111 1001(-7的补码)
所以相加为1 0000 0000(超出的最高位默认去掉)
比如(2)+(-3)=(-1)
2 -> 0000 0010 (2的补码)
-3 -> 1111 1101 (-3的补码)
相加为 1111 1111(-1的补码)
这样我们就可以在减法运算的时候,把数化为补码进行计算,这样就在计算时统一了加减。
from:
https://www.cnblogs.com/QKSword/p/8149053.html
C volatile指针以及volatile指向的对象
一般说来,volatile用在如下的几个地方:
1、中断服务程序中修改的供其它程序检测的变量需要加volatile;
2、多任务环境下各任务间共享的标志应该加volatile;
3、存储器映射的硬件寄存器通常也要加volatile说明,因为每次对它的读写都可能由不同意义;
volatile修饰指针一般用在共享指针上面。
下面代码:
uchar * volatile reg;
行代码里volatile修饰的是reg这个变量。所以这里实际上是定义了一个uchar类型的指针,并且这个指针变量本身是volatile 的。但是指针所指的内容并不是volatile的!在实际使用的时候,编译器对代码中指针变量reg本身的操作不会进行优化,但是对reg所指的内容 reg却会作为non-volatile内容处理,对reg的操作还是会被优化。通常这种写法一般用在对共享指针的声明上,即这个指针变量有可能会被中断等函数修改。将其定义为volatile以后,编译器每次取指针变量的值的时候都会从内存中载入,这样即使这个变量已经被别的程序修改了当前函数用的时候也能得到修改后的值(否则通常只在函数开始取一次放在寄存器里,以后就一直使用寄存器内的副本)。
需要注意将上述代码与下面的代码进行区别
这行代码里volatile修饰的是指针所指的内容。所以这里定义了一个uchar类型的指针,并且这个指针指向的是一个volatile的对象。但是指针变量本身并不是volatile的。如果对指针变量reg本身进行计算或者赋值等操作,是可能会被编译器优化的。但是对reg所指向的内容 reg的引用却禁止编译器优化。因为这个指针所指的是一个volatile的对象,所以编译器必须保证对reg的操作都不被优化。通常在驱动程序的开发中,对硬件寄存器指针的定义,都应该采用这种形式。
而
volatile uchar * volatile reg;
这样定义出来的指针就本身是个volatile的变量,又指向了volatile的数据内容。
from:
https://www.wenjiangs.com/group/topic-9867.html
打印格式输出符中的#
区分不同进制数字的一个简单办法就是,在输出时带上特定的前缀。在格式控制符中加上#
即可输出前缀,例如 %#x、%#o、%#lX、%#ho 等,请看下面的代码:
#include <stdio.h> int main() { short a = 0b1010110; //二进制数字 int b = 02713; //八进制数字 long c = 0X1DAB83; //十六进制数字 printf("a=%#ho, b=%#o, c=%#lo\n", a, b, c); //以八进制形似输出 printf("a=%hd, b=%d, c=%ld\n", a, b, c); //以十进制形式输出 printf("a=%#hx, b=%#x, c=%#lx\n", a, b, c); //以十六进制形式输出(字母小写) printf("a=%#hX, b=%#X, c=%#lX\n", a, b, c); //以十六进制形式输出(字母大写) return 0; }
运行结果:
a=0126, b=02713, c=07325603
a=86, b=1483, c=1944451
a=0x56, b=0x5cb, c=0x1dab83
a=0X56, b=0X5CB, c=0X1DAB83
from:
http://c.biancheng.net/cpp/html/3421.html
void *指针算术运算
int a = 10; void *test_ptr = (void*)&a; printf("test_ptr is: 0x%p.\n", test_ptr); printf("test_ptr + 4 is: 0x%p.\n", test_ptr+4);
执行结果如下:
test_ptr is: 0x0x7fffbcd844a4. test_ptr + 4 is: 0x0x7fffbcd844a8.
可知void *指针加1实际也只加1
unsigned类型打印格式符
在您尝试的所有组合中, %ld
和%lu
是唯一有效的printf格式说明符。 %lu
(长无符号十进制), %lx
或%lX
(带有小写或大写字母的长十六进制)和%lo
(长八进制)是unsigned long类型变量的唯一有效格式说明符(当然,您可以添加字段%
和l
之间的宽度,精度等修饰符。
printk一些技巧
https://www.cnblogs.com/sky-heaven/p/7161373.html
%*d
此打印格式是为了让输出结果占满指定的宽度,比如如下这段表示%*d输出宽度占6个character,所以结果为....30,这里的一个点表示一个空格:
int a = 6;
int b = 30;
printf("b value: %*d.\n", a, b);
数组在define时所有元素都初始化为相同的值
u64 __cpu_logical_map[NR_CPUS] = { [0 ... NR_CPUS-1] = INVALID_HWID };
给bool类型赋值超过1的值
给bool类型赋值超过1的值,或者说是非0的值,此bool类型变量的值将为1;如果赋值为0,则bool变量值为0,此bool变量为C99新定义的关键字_Bool类型,比如linux kernel里bool类型其实就是_Bool。
如果bool变量为自己typedef的类型,比如将char类型typedef为bool,则这个变量其实是char类型,就不再是上面的情况了
typedef _Bool bool;
*C99标准定义了一个新的关键字_Bool,提供了布尔类型。
C/C++ 中长度为0的数组
struct Line{ uint32_t length; char contents[0]; };
在结构体中,长度为0的数组不会占用存储空间 ,在上述例子中 sizeof(Line)=4
在申请内存空间时,缓冲区的空间可以和结构体的空间一起申请,一次操作就可以完成.例如
uint32_t length = 10; struct Line *pLine = (struct Line *)malloc(sizeof (struct Line) + length); pLine->length = length;
上述代码就动态地为结构体申请了长度(length)为10byte的缓冲区,而且由于是同一次malloc操作,缓冲区与结构体的内存地址是连续的,而且可以按照数组下标访问缓冲区元素,例如
for(uint32_t i = 0;i < pLine->length;++i) { pLine->contents[i] = i; }
由于缓冲区与结构体的内存地址是连续的,在释放内存的时候,只需要一次free操作.
综上所述,比起在结构体中定义一个指针指向另一片缓冲区地址的做法,使用长度为0的数组有以下好处:
1->指针本身需要占用内存,而长度为0的数组不需要
2->长度为0的数组定义出的缓冲区可以和结构体处在同一片连续地址中,只要一次malloc操作和free操作.如果用指针,需要分别申请和释放结构体内存和指针指向的内存块,至少需要两次以上的内存操作.
c语言转义字符\x
\x是转义字符,告诉编译器需要用特殊的方式进行处理。\x表示后面的字符是十六进制数,\0表示后面的字符是八进制数。例如十进制的17用十六进制表示就是‘\x11’,用八进制表示就是‘\021’。