我最近碰到了以下情况:
#include <iostream>
int *p = 0;
int f() {
p = new int(10);
return 0;
}
void g(int x, int *y = p) {
std::cout << y << std::endl;
}
int main() {
g(f());
}
Run Code Online (Sandbox Code Playgroud)
这非常微妙,因为您通常不希望默认参数在评估函数调用期间发生变化.我不得不看一下程序集来发现这个错误.
现在我的问题是:这是否真的是未定义的行为,因为对函数参数的评估顺序没有任何保证?
c++ operator-precedence undefined-behavior default-parameters
以下C++测试代码没有链接(gcc 4.9.2,binutils 2.25).错误是In function 'main': undefined reference to 'X::test'.
01: #include <string>
02: #include <iostream>
03:
04: namespace X
05: {
06: extern std::string test;
07: };
08:
09: using namespace X;
10: std::string test = "Test";
11:
12: int main()
13: {
14: std::cout << X::test << std::endl;
15: }
Run Code Online (Sandbox Code Playgroud)
由于第09行,我期待第10行定义X::test在第06行声明的变量.我相信test在全局命名空间中声明和定义了一个不相关的变量,因此链接错误.
问题:有人可以解释为什么我的期望不正确,究竟发生了什么?
不是答案:
std::string X::test = "Test";.我的理解std::memory_order_acquire和std::memory_order_release如下:
获取意味着获取围栏之后出现的内存访问不能重新排序到围栏之前.
释放意味着在释放围栏之前出现的内存访问不能在围栏之后重新排序.
我不明白为什么特别是对于C++ 11原子库,获取围栏与加载操作相关联,而释放围栏与存储操作相关联.
为了澄清,C++ 11 <atomic>库允许您以两种方式指定内存屏障:要么可以将fence指定为原子操作的额外参数,例如:
x.load(std::memory_order_acquire);
Run Code Online (Sandbox Code Playgroud)
或者您可以std::memory_order_relaxed单独使用和指定围栅,例如:
x.load(std::memory_order_relaxed);
std::atomic_thread_fence(std::memory_order_acquire);
Run Code Online (Sandbox Code Playgroud)
我不明白的是,鉴于上述获取和发布的定义,为什么C++ 11特别将获取与加载相关联,并与商店一起发布?是的,我已经看过许多示例,这些示例显示了如何使用获取/加载与发布/存储来在线程之间进行同步,但一般来说似乎是获取fences(防止语句后的内存重新排序)和发布的想法fences(在语句之前防止内存重新排序)与加载和存储的想法正交.
那么,为什么,例如,编译器不会让我说:
x.store(10, std::memory_order_acquire);
Run Code Online (Sandbox Code Playgroud)
我意识到我可以通过使用memory_order_relaxed,然后单独的atomic_thread_fence(memory_order_acquire)声明来完成上述操作,但同样,为什么我不能直接使用存储memory_order_acquire?
一个可能的用例可能是,如果我想确保某些存储,比如在执行可能影响其他线程的其他语句之前x = 10发生.
我目前正在阅读Anthony Williams的C++ Concurrency in Action.他的一个列表显示了这段代码,他声明z != 0可以解雇的断言.
#include <atomic>
#include <thread>
#include <assert.h>
std::atomic<bool> x,y;
std::atomic<int> z;
void write_x()
{
x.store(true,std::memory_order_release);
}
void write_y()
{
y.store(true,std::memory_order_release);
}
void read_x_then_y()
{
while(!x.load(std::memory_order_acquire));
if(y.load(std::memory_order_acquire))
++z;
}
void read_y_then_x()
{
while(!y.load(std::memory_order_acquire));
if(x.load(std::memory_order_acquire))
++z;
}
int main()
{
x=false;
y=false;
z=0;
std::thread a(write_x);
std::thread b(write_y);
std::thread c(read_x_then_y);
std::thread d(read_y_then_x);
a.join();
b.join();
c.join();
d.join();
assert(z.load()!=0);
}
Run Code Online (Sandbox Code Playgroud)
所以我能想到的不同执行路径是这样的:
1)
Run Code Online (Sandbox Code Playgroud)Thread a (x is now true) Thread c (fails to increment z) Thread b (y …
对齐和未对齐的内存访问有什么区别?
我在TMS320C64x DSP上工作,我想使用内部函数(汇编指令的C函数),它有
ushort & _amem2(void *ptr);
ushort & _mem2(void *ptr);
Run Code Online (Sandbox Code Playgroud)
_amem22字节的对齐访问在哪里进行_mem2未对齐访问.
我什么时候应该使用哪个?
我相信表达式T()创建了一个rvalue(由标准).但是,以下代码编译(至少在gcc4.0上):
class T {};
int main()
{
T() = T();
}
Run Code Online (Sandbox Code Playgroud)
我在技术上知道这是可能的,因为成员函数可以在temporaries上调用,而上面只是调用operator =在从第一个创建的rvalue临时T().
但从概念上讲,这就像为rvalue分配一个新值.是否允许这样做有充分的理由吗?
编辑:我发现这个奇怪的原因是它在内置类型上被严格禁止,但允许在用户定义的类型上使用.例如,int(2) = int(3)不会编译,因为这是"赋值中的无效左值".
所以我想真正的问题是,这种有点不一致的行为是否构成了语言的原因?还是出于某种历史原因?(例如,在rvalue表达式上只允许调用const成员函数在概念上会更健全,但是这可能无法完成,因为这可能会破坏一些现有代码.)
c++ language-design operator-overloading rvalue temporary-objects
我知道C和C++标准没有规定数字的特定表示(可以是二进制补码,符号和数量等).但我不清楚这些标准(并且无法确定是否已经说明)知道在使用位时是否存在任何特定的限制/保证/保留表示.尤其:
x,我想检查变量y以查看它是否是第5位是1,我想知道是否if (x & y)可行(因为据我所知,这取决于表示的值而不是该位是否实际上是1或0))char c所有位置为真(设置为c = c | ~c)并c = c << (CHAR_BIT - 1)设置高位和c = c ^ (c << 1)低位更简单的方法,假设我没有做任何假设,我不应该这样,给出这些问题)我想我的整体问题是:C和C++标准是否有关于位和整数的限制/保证/保留表示,尽管事实上没有强制表示整数的表示(如果C和C++标准在这方面有所不同) ,他们的区别是什么?)
我在完成作业时提出了这些问题,这需要我做一些操作(注意这些不是我作业的问题,这些都是"抽象的").
编辑:至于我称之为"位",我的意思是"值形成"位而不包括"填充"位.
a的控制块shared_ptr保持活动,同时存在至少一个weak_ptr存在.如果创建共享指针make_shared,则意味着保持分配对象的整个内存.(对象本身被正确销毁,但由于控件块和对象的内存分配在一个块make_shared中,因此它们只能一起解除分配.)
我的理解是否正确?
看起来这种行为代表了一个问题,例如在着名的"缓存示例"中.对象的内存将永远分配.
这在任何实际情况下都是一个问题吗?应该shared_ptr在这种情况下使用构造函数创建(大对象和意图使用weak_ptrs)?
8.1.2总线锁定
Intel 64和IA-32处理器提供LOCK#信号,该信号在某些关键存储器操作期间自动置位,以锁定系统总线或等效链路.当该输出信号被断言时,来自其他处理器或总线代理的用于控制总线的请求被阻止.软件可以指定在遵循LOCK语义的其他情况下将LOCK前缀添加到指令之前.
它来自英特尔手册,第3卷
听起来内存上的原子操作将直接在内存(RAM)上执行.我很困惑,因为当我分析装配输出时,我看到"没什么特别的".基本上,生成的汇编输出std::atomic<int> X; X.load()只会产生"额外"的影响.但是,它负责正确的内存排序,而不是原子性.如果我理解得X.store(2)恰到好处mov [somewhere], $2.就这样.它似乎没有"跳过"缓存.我知道将对齐(例如int)移动到内存是原子的.但是,我很困惑.
所以,我提出了疑问,但主要问题是:
我在以下代码中有关于操作顺序的问题:
std::atomic<int> x;
std::atomic<int> y;
int r1;
int r2;
void thread1() {
y.exchange(1, std::memory_order_acq_rel);
r1 = x.load(std::memory_order_relaxed);
}
void thread2() {
x.exchange(1, std::memory_order_acq_rel);
r2 = y.load(std::memory_order_relaxed);
}
Run Code Online (Sandbox Code Playgroud)
鉴于std::memory_order_acquirecppreference页面上的描述(https://en.cppreference.com/w/cpp/atomic/memory_order),
具有此内存顺序的加载操作会对受影响的内存位置执行获取操作:在此加载之前,不能对当前线程中的读取或写入进行重新排序.
很明显,r1 == 0 && r2 == 0在跑步thread1和thread2同时之后永远不会有结果.
但是,我在C++标准中找不到任何措辞(现在查看C++ 14草案),这保证了两个宽松的加载不能与获取 - 释放交换重新排序.我错过了什么?
编辑:正如评论中所建议的那样,实际上可以使r1和r2都等于零.我已经更新了程序以使用load-acquire,如下所示:
std::atomic<int> x;
std::atomic<int> y;
int r1;
int r2;
void thread1() {
y.exchange(1, std::memory_order_acq_rel);
r1 = x.load(std::memory_order_acquire);
}
void thread2() {
x.exchange(1, std::memory_order_acq_rel);
r2 = y.load(std::memory_order_acquire);
}
Run Code Online (Sandbox Code Playgroud)
现在是有可能得到两个和r1以及 …
c++ ×10
atomic ×3
c ×2
c++11 ×2
memory-model ×2
stdatomic ×2
integer ×1
make-shared ×1
memory ×1
namespaces ×1
omap ×1
rvalue ×1
scope ×1
shared-ptr ×1
weak-ptr ×1
x86 ×1