正如标题所说,
一个典型的答案是:
允许任何和所有代码转换不会改变程序的可观察行为的规则
我们会不时地从某些实现中获取行为,这些行为归因于此规则.好多次错了.那么,这个规则究竟是什么呢.标准没有明确地将此规则作为一个部分或段落提及,那么究竟什么属于这条规则的范围?对我来说,这似乎是一个灰色区域,标准没有详细定义.有人可以根据标准的参考资料详细说明细节.
注意:将其标记为C和C++,因为它与两种语言都相关.
我正在阅读Agner Fog的" 用C++优化软件 "(特定于英特尔,AMD和威盛的x86处理器),它在第34页说明
布尔变量存储为8位整数,值0表示false,1表示true.布尔变量是超定的,因为所有具有布尔变量作为输入的运算符检查输入是否具有除0或1之外的任何其他值,但是具有布尔值作为输出的运算符不能产生除0或1之外的其他值.布尔变量作为输入效率低于必要的效率.
这今天仍然适用于编译器吗?你能举个例子吗?作者说
如果确定操作数没有除0和1之外的其他值,则可以使布尔运算更有效.编译器没有做出这样的假设的原因是变量可能具有其他值,如果它们是未初始化或来自不明来源.
这是否意味着如果我拿一个函数指针bool(*)()作为示例并调用它,那么对它的操作会产生效率低下的代码?或者是通过取消引用指针或从引用读取然后对其进行操作来访问布尔值的情况?
x86-64 System V ABI(用于除Windows之外的所有内容)过去常常访问http://x86-64.org/documentation/abi.pdf,但该网站现已脱离互联网.
该文件是否有新的权威主页?
在C中,当左侧操作数具有负值时,左移位操作会调用未定义的行为.
ISO C99相关引用(6.5.7/4)
E1 << E2的结果是E1左移E2位位置; 腾出的位用零填充.如果E1具有无符号类型,则结果的值为E1×2 E2,比结果类型中可表示的最大值减少一个模数.如果E1具有带符号类型和非负值,并且E1×2 E2可在结果类型中表示,那么这就是结果值; 否则,行为未定.
但在C++中,行为定义明确.
ISO C++ - 03(5.8/2)
E1 << E2的值是E1(解释为位模式)左移E2位位置; 空位是零填充的.如果E1具有无符号类型,则结果的值为E1乘以上升到功率E2的数量2,如果E1的类型为无符号长,则减少模ULONG_MAX + 1,否则为UINT_MAX + 1.[注意:标题中定义了常量ULONG_MAX和UINT_MAX).]
这意味着
int a = -1, b=2, c;
c= a << b ;
Run Code Online (Sandbox Code Playgroud)
在C中调用未定义的行为,但行为在C++中定义良好.
是什么迫使ISO C++委员会考虑与C中的行为相对应的行为?
另一方面,implementation defined当左操作数为负时,行为是按位右移操作,对吗?
我的问题是为什么左移操作在C中调用未定义的行为,为什么右移操作符只调用实现定义的行为?
PS:请不要给出答案,例如"这是未定义的行为,因为标准是这样说的".:P
我有以下代码:
uint8_t buffer[16];
uint8_t data[16];
uint8_t buffer_length = 16;
uint8_t data_length = 0;
memcpy(buffer + buffer_length, data, data_length);
Run Code Online (Sandbox Code Playgroud)
memcpy应该是空操作,因为data_length为零。然而buffer + buffer_length,点就在分配的内存之外。我想知道它是否会触发某种未定义的行为?我应该memcpy用一个额外的包起来吗if?
我知道任何合理的实现memcpy都会很好地工作,但是这个问题更多地是从代码正确性的角度出发并避免未定义的行为。
简介:我正在查看汇编代码来指导我的优化,并在将int32添加到指针时看到许多符号或零扩展.
void Test(int *out, int offset)
{
out[offset] = 1;
}
-------------------------------------
movslq %esi, %rsi
movl $1, (%rdi,%rsi,4)
ret
Run Code Online (Sandbox Code Playgroud)
起初,我认为我的编译器在添加32位到64位整数时遇到了挑战,但我已经用Intel ICC 11,ICC 14和GCC 5.3证实了这种行为.
这个帖子证实了我的发现,但不清楚是否需要符号或零扩展.仅当尚未设置高32位时,才需要此符号/零扩展.但x86-64 ABI难道不够聪明吗?
我有点不愿意将所有指针偏移更改为ssize_t,因为寄存器溢出会增加代码的缓存占用空间.
正如文档中已经讨论过的,bool数据类型占用至少一个字节的内存.之前在SO上问了一个类似的问题(bool类型变量如何存储在内存中?(C++)),但是这个讨论和文档似乎只讨论了布尔数据类型占用的空间量,而不是实际发生的事情.我这样做的记忆:
bool b = true;
那么在记忆中实际发生了什么?7位未用于存储此信息的情况会怎样?标准是否规定了此行为?
他们是不确定的?或者在C++总部有人这样做:
enum bool : char
{
false = 0,
true = 1
};
Run Code Online (Sandbox Code Playgroud) 当我在 x86-64 linux 上使用 libc 的 system() 函数时,我注意到一个非常奇怪的行为,有时调用system()失败并出现分段错误,这是我在使用gdb.
我注意到在这一行中出现了分段错误:
=> 0x7ffff7a332f6 <do_system+1094>: movaps XMMWORD PTR [rsp+0x40],xmm0
Run Code Online (Sandbox Code Playgroud)
根据手册,这是 SIGSEGV 的原因:
当源或目标操作数是内存操作数时,操作数必须在 16 字节边界上对齐,否则会生成通用保护异常 (#GP)。
再往下看,我注意到我的rsp值确实不是 16 字节填充的(也就是说,它的十六进制表示没有以 结尾0)。rsp在调用之前手动修改权限system实际上使一切正常。
所以我写了以下程序:
#include <stdio.h>
#include <stdlib.h>
int main(void) {
register long long int sp asm ("rsp");
printf("%llx\n", sp);
if (sp & 0x8) /* == 0x8*/
{
printf("running system...\n");
system("touch hi");
}
return 0;
}
Run Code Online (Sandbox Code Playgroud)
用gcc 7.3.0编译,果然,观察输出时:
sha@sha-desktop:~/Desktop/tda$ ltrace -f ./o_sample2 …Run Code Online (Sandbox Code Playgroud) 我正在关注Linux 64系统中的《开始x64汇编编程》一书。我正在使用 NASM 和 gcc。
在关于浮点运算的章节中,本书指定了以下用于添加 2 个浮点数的代码。在本书和其他在线资源中,我读到寄存器 RAX 根据调用约定指定要使用的 XMM 寄存器的数量。
书中的代码如下:
extern printf
section .data
num1 dq 9.0
num2 dq 73.0
fmt db "The numbers are %f and %f",10,0
f_sum db "%f + %f = %f",10,0
section .text
global main
main:
push rbp
mov rbp, rsp
printn:
movsd xmm0, [num1]
movsd xmm1, [num2]
mov rdi, fmt
mov rax, 2 ;for printf rax specifies amount of xmm registers
call printf
sum:
movsd xmm2, [num1]
addsd xmm2, [num2]
printsum:
movsd …Run Code Online (Sandbox Code Playgroud)