C中变量的内存表示和值之间的关系是什么?
What is the relationship between memory representation and value of a variable in C?
在 C 中,确实是:
[8-bit] signed char: -127 to 127
[8-bit] unsigned char: 0 to 255
但是内存中到底发生了什么? signed char是用补码表示的,unsigned char是没有具体表示的(即11111111的序列)吗?
可执行文件如何跟踪它正在读取的变量类型,以确定 CPU 寄存器中的值是否被解释为二进制补码?是否有一些元数据将变量名称与其类型相关联?
谢谢!
数字的内部表示不是C语言的一部分,它是机器本身架构的一个特征。大多数实现使用 2 的补码,因为它使加法和减法成为相同的二进制运算(有符号和无符号运算相同)。
仅供参考,几乎所有现有 CPU 硬件都使用二进制补码,因此大多数编程语言也这样做是有道理的。
没有元数据。最终执行是由底层硬件完成的,因为编译器在对这些类型进行某些操作时使用不同的指令。当你比较装配时,它会变得更加明显。
void test1()
{
char p = 0;
p += 3;
}
void test2()
{
unsigned char p = 0;
p += 3;
}
您在这里看到的是编译器根据上面发布的源代码编译的指令。编译时没有优化 -O0
这是 clang 3.7 创建的程序集。如果您不熟悉它们,您可以忽略大部分说明。继续关注 movsx
和 movzx
。这两条指令使内存位置的处理方式有所不同。
test1(): # Instructions for test1
push rbp
mov rbp, rsp
mov byte ptr [rbp - 1], 0
movsx eax, byte ptr [rbp - 1] <-- Move byte to word with sign-extension
add eax, 3
mov cl, al
mov byte ptr [rbp - 1], cl
pop rbp
ret
test2(): # Instructions for test2
push rbp
mov rbp, rsp
mov byte ptr [rbp - 1], 0
movzx eax, byte ptr [rbp - 1] <-- Move byte to word with zero-extension
add eax, 3
mov cl, al
mov byte ptr [rbp - 1], cl
pop rbp
ret
C 是一种强类型语言。记忆的解释完全由上下文决定。也就是说,类型在编译时是已知的(在动态调度的情况下足够好)并且编译器提前做出所有决定。为了性能,运行时检查减少到最低限度(在 C 中到 none,除非您手动实现动态调度或 RTTI)。
在 C(和 C++)中,您可以轻松地以不同方式解释相同的内存位置,您所要做的就是获取指向它的指针并将其转换为不同的类型。如果您不知道自己在做什么,那将非常不安全。
在 C 中,确实是:
[8-bit] signed char: -127 to 127
[8-bit] unsigned char: 0 to 255
但是内存中到底发生了什么? signed char是用补码表示的,unsigned char是没有具体表示的(即11111111的序列)吗?
可执行文件如何跟踪它正在读取的变量类型,以确定 CPU 寄存器中的值是否被解释为二进制补码?是否有一些元数据将变量名称与其类型相关联?
谢谢!
数字的内部表示不是C语言的一部分,它是机器本身架构的一个特征。大多数实现使用 2 的补码,因为它使加法和减法成为相同的二进制运算(有符号和无符号运算相同)。
仅供参考,几乎所有现有 CPU 硬件都使用二进制补码,因此大多数编程语言也这样做是有道理的。
没有元数据。最终执行是由底层硬件完成的,因为编译器在对这些类型进行某些操作时使用不同的指令。当你比较装配时,它会变得更加明显。
void test1()
{
char p = 0;
p += 3;
}
void test2()
{
unsigned char p = 0;
p += 3;
}
您在这里看到的是编译器根据上面发布的源代码编译的指令。编译时没有优化 -O0
这是 clang 3.7 创建的程序集。如果您不熟悉它们,您可以忽略大部分说明。继续关注 movsx
和 movzx
。这两条指令使内存位置的处理方式有所不同。
test1(): # Instructions for test1
push rbp
mov rbp, rsp
mov byte ptr [rbp - 1], 0
movsx eax, byte ptr [rbp - 1] <-- Move byte to word with sign-extension
add eax, 3
mov cl, al
mov byte ptr [rbp - 1], cl
pop rbp
ret
test2(): # Instructions for test2
push rbp
mov rbp, rsp
mov byte ptr [rbp - 1], 0
movzx eax, byte ptr [rbp - 1] <-- Move byte to word with zero-extension
add eax, 3
mov cl, al
mov byte ptr [rbp - 1], cl
pop rbp
ret
C 是一种强类型语言。记忆的解释完全由上下文决定。也就是说,类型在编译时是已知的(在动态调度的情况下足够好)并且编译器提前做出所有决定。为了性能,运行时检查减少到最低限度(在 C 中到 none,除非您手动实现动态调度或 RTTI)。
在 C(和 C++)中,您可以轻松地以不同方式解释相同的内存位置,您所要做的就是获取指向它的指针并将其转换为不同的类型。如果您不知道自己在做什么,那将非常不安全。