我知道C++ 中的"未定义行为"几乎可以让编译器做任何想做的事情.但是,我遇到了让我感到惊讶的崩溃,因为我认为代码足够安全.
在这种情况下,真正的问题仅发生在使用特定编译器的特定平台上,并且仅在启用了优化时才发生.
我尝试了几件事来重现问题并将其简化到最大程度.这是一个名为的函数的摘录Serialize
,它将获取bool参数,并将字符串true
或复制false
到现有的目标缓冲区.
如果bool参数是未初始化的值,那么这个函数是否会在代码审查中,没有办法告诉它实际上可能会崩溃?
// Zero-filled global buffer of 16 characters
char destBuffer[16];
void Serialize(bool boolValue) {
// Determine which string to print based on boolValue
const char* whichString = boolValue ? "true" : "false";
// Compute the length of the string we selected
const size_t len = strlen(whichString);
// Copy string into destination buffer, which is zero-filled (thus already null-terminated)
memcpy(destBuffer, whichString, len);
}
Run Code Online (Sandbox Code Playgroud)
如果使用clang 5.0.0 +优化执行此代码,它将/可能崩溃.
boolValue ? "true" …
通过考虑将内存分为四个部分:数据,堆,堆栈和代码,全局变量,静态变量,常量数据类型,局部变量(在函数中定义和声明),变量(在main函数中),指针,并动态分配空间(使用malloc和calloc)存储在内存中?
我认为他们将分配如下:
char *arr
,int *arr
)------->堆我只是从C的角度来指这些变量.
如果我错了,请纠正我,因为我是C的新手.
考虑这两种可以代表"可选int
"的方法:
using std_optional_int = std::optional<int>;
using my_optional_int = std::pair<int, bool>;
Run Code Online (Sandbox Code Playgroud)
鉴于这两个功能......
auto get_std_optional_int() -> std_optional_int
{
return {42};
}
auto get_my_optional() -> my_optional_int
{
return {42, true};
}
Run Code Online (Sandbox Code Playgroud)
... g ++ trunk和clang ++ trunk (with -std=c++17 -Ofast -fno-exceptions -fno-rtti
)产生以下程序集:
get_std_optional_int():
mov rax, rdi
mov DWORD PTR [rdi], 42
mov BYTE PTR [rdi+4], 1
ret
get_my_optional():
movabs rax, 4294967338 // == 0x 0000 0001 0000 002a
ret
Run Code Online (Sandbox Code Playgroud)
为什么get_std_optional_int()
需要三个mov
指令,而get_my_optional()
只需要一个 …
就编译器优化而言,将堆分配更改为堆栈分配是否合法和/或可能?或者会破坏as-if规则?
例如,假设这是代码的原始版本
{
Foo* f = new Foo();
f->do_something();
delete f;
}
Run Code Online (Sandbox Code Playgroud)
编译器是否能够将此更改为以下内容
{
Foo f{};
f.do_something();
}
Run Code Online (Sandbox Code Playgroud)
我不这么认为,因为如果原始版本依赖于自定义分配器之类的东西,那将会产生影响.标准是否对此有具体说明?
在这段代码中,我正在比较两个功能相同的循环的性能:
for (int i = 1; i < v.size()-1; ++i) {
int a = v[i-1];
int b = v[i];
int c = v[i+1];
if (a < b && b < c)
++n;
}
Run Code Online (Sandbox Code Playgroud)
和
for (int i = 1; i < v.size()-1; ++i)
if (v[i-1] < v[i] && v[i] < v[i+1])
++n;
Run Code Online (Sandbox Code Playgroud)
在优化标志设置为O2
:的许多不同C++编译器中,第一个运行速度明显慢于第二个编译器:
我很困惑,现代C++优化器在处理这种情况时遇到了问题.任何线索为什么?我是否必须编写丑陋的代码而不使用临时变量才能获得最佳性能?
现在,使用临时变量可以使代码更快,有时甚至更快.到底是怎么回事?
我正在使用的完整代码如下:
#include <algorithm>
#include <chrono>
#include <random>
#include <iomanip>
#include <iostream>
#include …
Run Code Online (Sandbox Code Playgroud) 我正在读Fedor Pikus 的这本书,他有一些非常非常有趣的例子,对我来说是一个惊喜。
特别是这个基准测试吸引了我,唯一的区别是在其中一个我们使用 || 在 if 和另一个中我们使用 |。
void BM_misspredict(benchmark::State& state)
{
std::srand(1);
const unsigned int N = 10000;;
std::vector<unsigned long> v1(N), v2(N);
std::vector<int> c1(N), c2(N);
for (int i = 0; i < N; ++i)
{
v1[i] = rand();
v2[i] = rand();
c1[i] = rand() & 0x1;
c2[i] = !c1[i];
}
unsigned long* p1 = v1.data();
unsigned long* p2 = v2.data();
int* b1 = c1.data();
int* b2 = c2.data();
for (auto _ : state)
{
unsigned long a1 …
Run Code Online (Sandbox Code Playgroud) 我正在尝试理解常规规则。根据cppreference:
as-if规则
允许进行任何和所有不改变程序可观察行为的代码转换解释
只要满足以下条件,C ++编译器就可以对程序进行任何更改:[...]
我很难理解“说明”部分的第二个技巧:
2)在程序终止时,写入文件的数据与在执行程序时完全一样。
我只是不明白“程序是按书面形式执行”的意思。
假设A
,B
,a
,和b
都是变量,的地址A
,B
,a
,和b
都是不同的.然后,对于以下代码:
A = a;
B = b;
Run Code Online (Sandbox Code Playgroud)
C和C++标准是否明确要求A=a
在之前严格执行B=b
?考虑到的地址A
,B
,a
,和b
都不同,编译器允许交换两个语句的执行顺序为某种目的,如优化?
如果我的问题的答案在C和C++中有所不同,我想知道两者.
编辑:问题的背景如下.在棋盘游戏AI设计中,对于优化,人们使用无锁共享哈希表,如果我们不添加volatile
限制,其正确性很大程度上取决于执行顺序.
考虑以下代码片段:
bool foo(const std::string& s) {
return s == "hello"; // comparing against a const char* literal
}
bool bar(const std::string& s) {
return s == "hello"s; // comparing against a std::string literal
}
Run Code Online (Sandbox Code Playgroud)
在乍一看,它看起来像比对并const char*
需要更少的组装说明1,作为使用字符串字面量会导致就地建设std::string
。
(编辑:正如答案中指出的那样,我忘记了有效地s.compare(const char*)
将被调用的事实foo()
,因此在这种情况下当然不会进行就地构建。因此,请在下面删除一些行。)
但是,请operator==(const char*, const std::string&)
参阅参考资料:
所有比较都是通过
compare()
成员函数完成的。
根据我的理解,这意味着我们将需要构造一个结构 std::string
来执行比较,因此我怀疑最终的开销将是相同的(尽管对的调用已将其隐藏了operator==
)。
1我知道更少的汇编指令并不一定意味着更快的代码,但是我不想在这里进行微基准测试。
在Effective C++一书中,我看到了以下段落:
结果,如果你写
Run Code Online (Sandbox Code Playgroud)class Empty{};
它基本上和你写的一样:
Run Code Online (Sandbox Code Playgroud)class Empty { public: Empty() { ... } Empty(const Empty& rhs) { ... } ~Empty() { ... } Empty& operator=(const Empty& rhs) { ... } // copy assignment operator };
以下代码将导致生成每个函数:
Run Code Online (Sandbox Code Playgroud)Empty e1; Empty e2(e1); e2 = e1;
但是在拆解通过编译上面的代码创建的可执行文件之后,我意识到并非如此:没有任何函数被调用.
这是主要的汇编代码:
00000000004006cd <main>:
4006cd: 55 push %rbp
4006ce: 48 89 e5 mov %rsp,%rbp
4006d1: b8 00 00 00 00 mov $0x0,%eax
4006d6: 5d pop %rbp
4006d7: c3 retq
Run Code Online (Sandbox Code Playgroud)
段中没有任何名为"Empty"的函数.text
.
那么在我们调用构造函数或赋值空类之后,编译器的行为究竟是什么?这本书说它会产生一些功能吗?如果是这样,他们存放在哪里?
c++ ×9
optimization ×3
c ×2
c++17 ×2
performance ×2
abi ×1
assembly ×1
benchmarking ×1
c++14 ×1
empty-class ×1
llvm ×1
llvm-codegen ×1
memory ×1
standards ×1
types ×1
x86-64 ×1