Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/71.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 编译器如何知道不将关键部分内的代码移动到关键部分外?_C_Winapi - Fatal编程技术网

C 编译器如何知道不将关键部分内的代码移动到关键部分外?

C 编译器如何知道不将关键部分内的代码移动到关键部分外?,c,winapi,C,Winapi,根据我的理解: 编译器可以对我的代码进行任何重新排序 临界区内的代码不会移动到临界区外 现在假设我有以下代码: printf("Hi"); EnterCriticalSection(&CriticalSection); printf("Inside the critical section"); printf("Also inside the critical section"); LeaveCriticalSection(&CriticalSection); print

根据我的理解:

  • 编译器可以对我的代码进行任何重新排序

  • 临界区内的代码不会移动到临界区外

现在假设我有以下代码:

printf("Hi");

EnterCriticalSection(&CriticalSection);
printf("Inside the critical section");
printf("Also inside the critical section");
LeaveCriticalSection(&CriticalSection);

printf("Bye");

现在,编译器是否真的会查找函数
EnterCriticalSection()
LeaveCriticalSection()
,而不会将其中的代码移到外部?

假设编译器对EnterCriticalSection没有任何专门的知识;它无法对函数的功能做任何假设

因此,它不能对
printf
语句进行重新排序,因为它必须考虑
EnterCriticalSection
也可能写入
stdout
,并且
stdout
上的输出必须以与
printf
语句被写入时相同的顺序显示


这同样适用于全局变量的访问,编译器必须允许函数可能访问这些变量。

假设编译器没有任何关于EnterCriticalSection的特殊知识;它无法对函数的功能做任何假设

因此,它不能对
printf
语句进行重新排序,因为它必须考虑
EnterCriticalSection
也可能写入
stdout
,并且
stdout
上的输出必须以与
printf
语句被写入时相同的顺序显示

这同样适用于访问全局变量,编译器必须允许函数访问这些变量

编译器可以对我的代码进行任何重新排序

这是不正确的。编译器是有限的

临界区内的代码不会移动到临界区外

这也不正确,这取决于关键部分内部/外部的代码

优化与约束 编译器对函数中的每段代码都有一系列约束。这些约束可能是输入和输出(除非先执行X,否则不能执行Y)或更一般的语句,如“这会影响内存的某个地方的内容”。编译器将在编译代码时保留这些约束。如果编译器不知道函数做什么,它将使用最重的约束集

通常,这意味着编译器不会翻转两个函数调用的顺序1

这还意味着,一般来说,如果访问内存,这些内存访问必须相对于函数调用进行排序

void myfunc(X *ptr) {
    my_lock(ptr->mutex);   // Maybe affects memory somewhere.
    ptr->field++;          // Definitely affects memory at ptr->field.
    my_unlock(ptr->mutex); // Maybe affects memory somewhere.
}
但是,它可以在关键部分之外重新排序:

int i = 5;
my_lock();   // Maybe affects memory somewhere.
i++;         // This is not "memory somewhere", this is my variable,
             // it's a register or on the stack and nobody else can
             // change it.
my_unlock(); // Maybe affects memory somewhere.
因此,它可以将其重新排序为:

int i = 6;
my_lock();
my_unlock();
上面的代码可以重新排序,因为编译器对允许修改
i
的人有专门的知识。如果您在其他地方有一些特殊的代码,这些代码沿着堆栈向上移动,试图创建指向
&i
的指针,即使
&i
从未出现在您的程序中,您也违反了与编译器的合同(也就是说,您的程序具有未定义的行为)

我希望这能澄清问题

脚注 1:您可以添加注释,如
\uu declspec(noalias)
,这些注释可以更改此规则,LTO/整个程序优化/过程间优化也可以更改

编译器可以对我的代码进行任何重新排序

这是不正确的。编译器是有限的

临界区内的代码不会移动到临界区外

这也不正确,这取决于关键部分内部/外部的代码

优化与约束 编译器对函数中的每段代码都有一系列约束。这些约束可能是输入和输出(除非先执行X,否则不能执行Y)或更一般的语句,如“这会影响内存的某个地方的内容”。编译器将在编译代码时保留这些约束。如果编译器不知道函数做什么,它将使用最重的约束集

通常,这意味着编译器不会翻转两个函数调用的顺序1

这还意味着,一般来说,如果访问内存,这些内存访问必须相对于函数调用进行排序

void myfunc(X *ptr) {
    my_lock(ptr->mutex);   // Maybe affects memory somewhere.
    ptr->field++;          // Definitely affects memory at ptr->field.
    my_unlock(ptr->mutex); // Maybe affects memory somewhere.
}
但是,它可以在关键部分之外重新排序:

int i = 5;
my_lock();   // Maybe affects memory somewhere.
i++;         // This is not "memory somewhere", this is my variable,
             // it's a register or on the stack and nobody else can
             // change it.
my_unlock(); // Maybe affects memory somewhere.
因此,它可以将其重新排序为:

int i = 6;
my_lock();
my_unlock();
上面的代码可以重新排序,因为编译器对允许修改
i
的人有专门的知识。如果您在其他地方有一些特殊的代码,这些代码沿着堆栈向上移动,试图创建指向
&i
的指针,即使
&i
从未出现在您的程序中,您也违反了与编译器的合同(也就是说,您的程序具有未定义的行为)

我希望这能澄清问题

脚注
1:您可以添加注释,如
\uu declspec(noalias)
,这些注释可以更改此规则,LTO/整个程序优化/过程间优化也可以更改内容。

编译器对您的关键部分一无所知。它在更微观的层面上运作。它在装配级别运行。它可能会将单个装配说明移动到关键部分之外。但它不会影响代码的正确性,因为这是它的核心保证之一。它非常简单,不能进行“任何重新排序”。优化器不会在它不知道的函数调用之间移动代码。它必须假设它有明显的副作用。就像它永远不会在“Hi”之前打印“Bye”。@Hans Passant所以即使我使用了
foo()
boo()
而不是
EnterCriticalSection()