#include <stdio.h>
int main()
{
signed char i = 0;
while (i <= 0)
{
printf("%d", i);
i = i - 1;
}
return 0;
}
#include <stdio.h>
int main()
{
signed char i = 0;
while (i <= 0)
{
printf("%d", i);
i = i - 1;
}
return 0;
}
#include <stdio.h>
int main()
{
signed char i = 0; // 定义类型为signed char的局部变量i并初始化为0
while (i <= 0) { // 把i的值整型提升为int,然后再与0比较,如果小于等于0,继续循环,否则退出循环
printf("%d", i); // 把i的值整型提升为int,再传入printf函数
i = i - 1; // 把i的值整型提升为int,然后减1,最后把结果转换为signed char类型赋值给i
// 当i-1的结果是-129时,这个值已经超出signed char类型可表示的值范围[-128, 127],
// C/C++标准规定对于有符号类型,这种情况其行为是implementation defined。
// 大多数编译器对这种情况是按截断处理来实现的,截取-129的低8位得到的结果是127,因此i的值变成127,然后再与0比较时退出循环
// https://en.cppreference.com/w/c/language/conversion#Integer_conversions
}
return 0;
}
// 下面是clang编译器生成的汇编码
/*
main: # @main
push rbp
mov rbp, rsp
sub rsp, 16
mov dword ptr [rbp - 4], 0
mov byte ptr [rbp - 5], 0 # rbp-5是局部变量i的地址,这里是初始化i为0
.LBB0_1: # =>This Inner Loop Header: Depth=1
movsx eax, byte ptr [rbp - 5] # movsx指令是按符号扩展拷贝,这里是整型提升i的值为int类型
cmp eax, 0 # 与0比较
jg .LBB0_3 # 如果大于0,跳出循环
movsx esi, byte ptr [rbp - 5] # 整型提升i的值为int类型,拷贝至esi寄存器,即printf的第二个参数
movabs rdi, offset .L.str # 把输出格式字符串地址拷贝至rdi,即printf的第一个参数
mov al, 0 # 返回值清零
call printf # 调用printf函数输出i的值
movsx eax, byte ptr [rbp - 5] # 整型提升i的值为int类型
sub eax, 1 # 整型提升的值减1
mov byte ptr [rbp - 5], al # 将计算结果转换为char类型赋给i
jmp .LBB0_1
.LBB0_3:
xor eax, eax
add rsp, 16
pop rbp
ret
.L.str:
.asciz "%d"
*/
参考
https://en.cppreference.com/w/c/language/conversion#Usual_arithmetic_conversions
https://en.cppreference.com/w/c/language/operator_arithmetic