总结一些遇到的C语言的“奇妙”例子,虽然应用上没什么卵用,但是可以考察对细节的理解

i++和++i谁快

  • 结论

    ++i更快:

    i++相比++i,多一步将自加的i赋值给寄存器的指令,相当于创建临时变量。

    虽然在这里只相差一条指令,但是如果i为类对象,那么i++多出的创建临时变量的指令会更多


a = a++ 和 a = ++a 中,a自增了吗?

  • 结论

    a = a++ 是先压栈a,然后自增,最后出栈a。所以a自增了,但是结果放在中间寄存器没有赋值回来,所以可以说没自增😂

    a = ++a 是先自增,然后压栈,最后出栈。所以a的确自增了。


fork后进程共享文件描述符吗?

  • 结论

    fork后进程共享文件描述符,同时也共享文件表项(意味着共享文件位移量等等)


各种类型数值间的转换

从有符号数转换

源类型

目标类型

行为

低位宽有符号数 → 高位宽有符号数

符号扩展:复制符号位到高位以保持原值

(int8_t)(-1)(int32_t)0xFF → 0xFFFFFFFF

低位宽有符号数 → 高位宽无符号数

符号扩展后按无符号解释

(int8_t)(-1)(uint32_t)0xFF → 0xFFFFFFFF(值为 4294967295

高位宽有符号数 → 低位宽有符号数

截断高位,保留低位有效值

(int32_t)(-1)(int8_t)0xFFFFFFFF → 0xFF(值为 -1

高位宽有符号数 → 低位宽无符号数

截断高位,按无符号解释低位

(int32_t)(-1)(uint8_t)0xFFFFFFFF → 0xFF(值为 255

从无符号数转换

源类型

目标类型

行为

低位宽无符号数 → 高位宽无符号数

零扩展:高位补 0

(uint8_t)(255)(uint32_t)0xFF → 0x000000FF

低位宽无符号数 → 高位宽有符号数

零扩展后按有符号解释

(uint8_t)(255)(int32_t)0xFF → 0x000000FF(值为 255

高位宽无符号数 → 低位宽无符号数

截断高位

(uint32_t)(1023)(uint8_t)0x000003FF → 0xFF(值为 255

高位宽无符号数 → 低位宽有符号数

截断高位,按有符号解释低位

(uint32_t)(255)(int8_t)0xFF → 0xFF(值为 -1

总结

高转低,先截断高位,再根据目标有无符号解释值

低转高,先根据有无符号扩展高位,再根据目标有无符号解释值

相同位宽,只根据目标有无符号解释值


你真的了解typedef吗?

同时存在同名函数和变量?

通常我们会使用typedef void (*foo_t)(void);来定义一个函数指针的类型(或者说别名),但你有没有想过如果去掉那个解引用符号* ,会发生什么?

奇怪的是,如下的代码能正常工作,foo_t foo; 表现得好像定义一个全局变量一样:

typedef void (foo_t)(void);
foo_t foo;

int main(){
    foo();
    return 0;
}

void foo(void){
	...
}

原理其实很好理解,因为typedefdefine 一样,本质上是一种别名(但不等同于字符替换),所以这里foo_t foo; 其实是void (foo)(void); ,也就是函数声明。

引人思考的是,变量的定义和函数的定义在C语言中,真的十分类似。这里又引申出来一种可能的用法:搭配X宏,在需要热重载的时候,使用typedef定义函数指针;在需要静态链接的时候,使用typedef定义函数声明。再通过测试宏在这两者之间切换。

Weird Parts of C you probably never heard of...

函数的参数被替换成什么了?

typedef char *T

void foo(const T arg){
    ...
}
  1. 如果是单纯的字符替换,那就是const char *arg

  2. 又或者是char * const arg ???

答案是2

芯片技术从业者,本站签约作者