unsigned int 到 unsigned long long 定义明确吗?
Unsigned int to unsigned long long well defined?
我想看看当 unsigned long long
被赋予 unsigned int
的值时幕后发生了什么。我制作了一个简单的 C++ 程序来试用它并将所有 io 移出 main():
#include <iostream>
#include <stdlib.h>
void usage() {
std::cout << "Usage: ./u_to_ull <unsigned int>\n";
exit(0);
}
void atoiWarning(int foo) {
std::cout << "WARNING: atoi() returned " << foo << " and (unsigned int)foo is " <<
((unsigned int)foo) << "\n";
}
void result(unsigned long long baz) {
std::cout << "Result as unsigned long long is " << baz << "\n";
}
int main(int argc, char** argv) {
if (argc != 2) usage();
int foo = atoi(argv[1]);
if (foo < 0) atoiWarning(foo);
// Signed to unsigned
unsigned int bar = foo;
// Conversion
unsigned long long baz = -1;
baz = bar;
result(baz);
return 0;
}
生成的程序集为 main 生成了这个:
0000000000400950 <main>:
400950: 55 push %rbp
400951: 48 89 e5 mov %rsp,%rbp
400954: 48 83 ec 20 sub [=11=]x20,%rsp
400958: 89 7d ec mov %edi,-0x14(%rbp)
40095b: 48 89 75 e0 mov %rsi,-0x20(%rbp)
40095f: 83 7d ec 02 cmpl [=11=]x2,-0x14(%rbp)
400963: 74 05 je 40096a <main+0x1a>
400965: e8 3a ff ff ff callq 4008a4 <_Z5usagev>
40096a: 48 8b 45 e0 mov -0x20(%rbp),%rax
40096e: 48 83 c0 08 add [=11=]x8,%rax
400972: 48 8b 00 mov (%rax),%rax
400975: 48 89 c7 mov %rax,%rdi
400978: e8 0b fe ff ff callq 400788 <atoi@plt>
40097d: 89 45 f0 mov %eax,-0x10(%rbp)
400980: 83 7d f0 00 cmpl [=11=]x0,-0x10(%rbp)
400984: 79 0a jns 400990 <main+0x40>
400986: 8b 45 f0 mov -0x10(%rbp),%eax
400989: 89 c7 mov %eax,%edi
40098b: e8 31 ff ff ff callq 4008c1 <_Z11atoiWarningi>
400990: 8b 45 f0 mov -0x10(%rbp),%eax
400993: 89 45 f4 mov %eax,-0xc(%rbp)
400996: 48 c7 45 f8 ff ff ff movq [=11=]xffffffffffffffff,-0x8(%rbp)
40099d: ff
40099e: 8b 45 f4 mov -0xc(%rbp),%eax
4009a1: 48 89 45 f8 mov %rax,-0x8(%rbp)
4009a5: 48 8b 45 f8 mov -0x8(%rbp),%rax
4009a9: 48 89 c7 mov %rax,%rdi
4009ac: e8 66 ff ff ff callq 400917 <_Z6resulty>
4009b1: b8 00 00 00 00 mov [=11=]x0,%eax
4009b6: c9 leaveq
4009b7: c3 retq
C++中的-1
明确表示-0x8(%rbp)
对应于baz
(由于[=17=]xffffffffffffffff
)。 -0x8(%rbp)
被 %rax
写入,但 %rax
的前四个字节似乎尚未分配,%eax
已分配
这是否表明 -0x8(%rbp)
的前 4 个字节未定义?
在 Intel® 64 and IA-32 Architectures Software Developer Manuals 第 1 卷第 3.4.1.1 章(64 位模式下的通用寄存器)中,它说
32-bit operands generate a 32-bit result, zero-extended to a 64-bit result in the destination general-purpose register.
所以在mov -0xc(%rbp),%eax
之后,rax
的上半部分是定义的,它是零。
这也适用于 xchg eax, eax
的 87 C0
编码,但不适用于其 90
编码(定义为 nop
,否决上述规则) .
从 C++98(和 C++11 似乎没有变化)4.7/2(积分转换 - 没有相关的提升)我们了解到:
If the destination type is unsigned, the resulting value is the least
unsigned integer congruent to the source integer (modulo 2n where n is
the number of bits used to represent the unsigned type).
这清楚地表明,只要源和目标是无符号的,并且目标至少与源一样大,值就不会改变。如果编译器生成的代码未能使较大的值相等,则编译器有问题。
我想看看当 unsigned long long
被赋予 unsigned int
的值时幕后发生了什么。我制作了一个简单的 C++ 程序来试用它并将所有 io 移出 main():
#include <iostream>
#include <stdlib.h>
void usage() {
std::cout << "Usage: ./u_to_ull <unsigned int>\n";
exit(0);
}
void atoiWarning(int foo) {
std::cout << "WARNING: atoi() returned " << foo << " and (unsigned int)foo is " <<
((unsigned int)foo) << "\n";
}
void result(unsigned long long baz) {
std::cout << "Result as unsigned long long is " << baz << "\n";
}
int main(int argc, char** argv) {
if (argc != 2) usage();
int foo = atoi(argv[1]);
if (foo < 0) atoiWarning(foo);
// Signed to unsigned
unsigned int bar = foo;
// Conversion
unsigned long long baz = -1;
baz = bar;
result(baz);
return 0;
}
生成的程序集为 main 生成了这个:
0000000000400950 <main>:
400950: 55 push %rbp
400951: 48 89 e5 mov %rsp,%rbp
400954: 48 83 ec 20 sub [=11=]x20,%rsp
400958: 89 7d ec mov %edi,-0x14(%rbp)
40095b: 48 89 75 e0 mov %rsi,-0x20(%rbp)
40095f: 83 7d ec 02 cmpl [=11=]x2,-0x14(%rbp)
400963: 74 05 je 40096a <main+0x1a>
400965: e8 3a ff ff ff callq 4008a4 <_Z5usagev>
40096a: 48 8b 45 e0 mov -0x20(%rbp),%rax
40096e: 48 83 c0 08 add [=11=]x8,%rax
400972: 48 8b 00 mov (%rax),%rax
400975: 48 89 c7 mov %rax,%rdi
400978: e8 0b fe ff ff callq 400788 <atoi@plt>
40097d: 89 45 f0 mov %eax,-0x10(%rbp)
400980: 83 7d f0 00 cmpl [=11=]x0,-0x10(%rbp)
400984: 79 0a jns 400990 <main+0x40>
400986: 8b 45 f0 mov -0x10(%rbp),%eax
400989: 89 c7 mov %eax,%edi
40098b: e8 31 ff ff ff callq 4008c1 <_Z11atoiWarningi>
400990: 8b 45 f0 mov -0x10(%rbp),%eax
400993: 89 45 f4 mov %eax,-0xc(%rbp)
400996: 48 c7 45 f8 ff ff ff movq [=11=]xffffffffffffffff,-0x8(%rbp)
40099d: ff
40099e: 8b 45 f4 mov -0xc(%rbp),%eax
4009a1: 48 89 45 f8 mov %rax,-0x8(%rbp)
4009a5: 48 8b 45 f8 mov -0x8(%rbp),%rax
4009a9: 48 89 c7 mov %rax,%rdi
4009ac: e8 66 ff ff ff callq 400917 <_Z6resulty>
4009b1: b8 00 00 00 00 mov [=11=]x0,%eax
4009b6: c9 leaveq
4009b7: c3 retq
C++中的-1
明确表示-0x8(%rbp)
对应于baz
(由于[=17=]xffffffffffffffff
)。 -0x8(%rbp)
被 %rax
写入,但 %rax
的前四个字节似乎尚未分配,%eax
已分配
这是否表明 -0x8(%rbp)
的前 4 个字节未定义?
在 Intel® 64 and IA-32 Architectures Software Developer Manuals 第 1 卷第 3.4.1.1 章(64 位模式下的通用寄存器)中,它说
32-bit operands generate a 32-bit result, zero-extended to a 64-bit result in the destination general-purpose register.
所以在mov -0xc(%rbp),%eax
之后,rax
的上半部分是定义的,它是零。
这也适用于 xchg eax, eax
的 87 C0
编码,但不适用于其 90
编码(定义为 nop
,否决上述规则) .
从 C++98(和 C++11 似乎没有变化)4.7/2(积分转换 - 没有相关的提升)我们了解到:
If the destination type is unsigned, the resulting value is the least unsigned integer congruent to the source integer (modulo 2n where n is the number of bits used to represent the unsigned type).
这清楚地表明,只要源和目标是无符号的,并且目标至少与源一样大,值就不会改变。如果编译器生成的代码未能使较大的值相等,则编译器有问题。