msc*_*msc 71 c assembly function-call c11 noreturn
我读了关于属性的这个问题noreturn
,它用于不返回调用者的函数.
然后我用C做了一个程序.
#include <stdio.h>
#include <stdnoreturn.h>
noreturn void func()
{
printf("noreturn func\n");
}
int main()
{
func();
}
Run Code Online (Sandbox Code Playgroud)
并使用以下代码生成代码的汇编:
.LC0:
.string "func"
func:
pushq %rbp
movq %rsp, %rbp
movl $.LC0, %edi
call puts
nop
popq %rbp
ret // ==> Here function return value.
main:
pushq %rbp
movq %rsp, %rbp
movl $0, %eax
call func
Run Code Online (Sandbox Code Playgroud)
为什么函数func()
在提供noreturn
属性后返回?
Sou*_*osh 120
C中的函数说明符是编译器的提示,接受程度是实现定义的.
首先,_Noreturn
函数说明符(或者,noreturn
使用<stdnoreturn.h>
)是编译器关于程序员将永远不会返回的理论承诺的提示.基于此承诺,编译器可以做出某些决定,对代码生成执行一些优化.
IIRC,如果用函数说明符指定的noreturn
函数最终返回其调用者
return
声明该行为是不确定的.你不能从函数返回.
为清楚起见,使用noreturn
函数说明符不会停止返回其调用者的函数表单.它是程序员对编译器的承诺,允许它更自由地生成优化代码.
现在,如果您早先和之后做出承诺,选择违反此规则,结果是UB.当_Noreturn
函数似乎能够返回其调用者时,鼓励但不要求编译器产生警告.
根据章节§6.7.4 C11
,第8段
使用
_Noreturn
函数说明符声明的函数不应返回其调用者.
并且,第12段,(注意评论!!)
Run Code Online (Sandbox Code Playgroud)EXAMPLE 2 _Noreturn void f () { abort(); // ok } _Noreturn void g (int i) { // causes undefined behavior if i <= 0 if (i > 0) abort(); }
因为C++
,行为非常相似.引自第7.6.4节,C++14
第2段(强调我的)
如果
f
调用函数f
先前使用该noreturn
属性声明并f
最终返回,则行为未定义. [注意:该函数可以通过抛出异常来终止. - 尾注][注意:如果标记的函数
[[noreturn]]
可能返回,则鼓励实现发出警告. - 尾注]3 [例如:
Run Code Online (Sandbox Code Playgroud)[[ noreturn ]] void f() { throw "error"; // OK } [[ noreturn ]] void q(int i) { // behavior is undefined if called with an argument <= 0 if (i > 0) throw "positive"; }
- 末端的例子]
And*_*nle 48
为什么在提供noreturn属性后函数func()会返回?
因为你编写的代码告诉了它.
如果你不想让你的函数返回,请致电exit()
或abort()
或相似的,所以它不会返回.
什么别的将你的函数做的比其他的回报,它已被称为后printf()
?
的C标准在6.7.4功能说明符,段落12具体包括的一个例子noreturn
,实际上可以返回功能-并标记行为未定义:
例2
_Noreturn void f () {
abort(); // ok
}
_Noreturn void g (int i) { // causes undefined behavior if i<=0
if (i > 0) abort();
}
Run Code Online (Sandbox Code Playgroud)
总之,noreturn
是一个限制是你放置你的代码-它告诉编译器"我的代码永远不会返回".如果你违反了这个限制,那一切都在你身上.
Ste*_*mit 25
noreturn
是一个承诺.你告诉编译器,"它可能会也可能不会很明显,但我知道,基于我编写代码的方式,这个函数永远不会返回." 这样,编译器可以避免设置允许函数正确返回的机制.省略这些机制可能允许编译器生成更有效的代码.
一个函数怎么能不返回?一个例子是,如果它打电话exit()
.
但是如果你向编译器承诺你的函数不会返回,并且编译器没有安排它可以使函数正确返回,然后你去编写一个确实返回的函数,那么编译器应该怎样做?它基本上有三种可能性:
编译器可能会执行1,2,3或某种组合.
如果这听起来像未定义的行为,那是因为它是.
编程与现实生活中的底线是:不要做出你无法保留的承诺.其他人可能会根据您的承诺做出决定,如果您违背承诺,可能会发生不好的事情.
Gro*_*roo 15
该noreturn
属性是您向编译器提供有关函数的承诺.
如果你这样做,从这样一个函数返回,行为是不确定的,但是这并不意味着一个成熟的编译器可以让你乱了应用程序的状态完全通过移除ret
声明,特别是因为编译器会经常甚至可以推断出回归确实是可能的.
但是,如果你这样写:
noreturn void func(void)
{
printf("func\n");
}
int main(void)
{
func();
some_other_func();
}
Run Code Online (Sandbox Code Playgroud)
那么编译器some_other_func
完全删除它是完全合理的,如果感觉就好了.
nne*_*neo 11
正如其他人所提到的,这是经典的未定义行为.你承诺func
不会回来,但无论如何你都让它回来了.当你休息时,你可以拿起碎片.
虽然编译器func
以通常的方式编译(尽管你的noreturn
),但noreturn
影响调用函数.
您可以在装配清单中看到这一点:编译器承担,中main
,那func
将不会返回.因此,它确实删除了之后的所有代码call func
(请参阅https://godbolt.org/g/8hW6ZR).汇编列表没有被截断,它实际上只是在之后结束,call func
因为编译器假定之后的任何代码都无法访问.因此,当func
实际返回时,main
将开始执行跟随main
函数的任何垃圾- 无论是填充,立即常量还是大量00
字节.再次 - 非常未定义的行为.
这是可传递的 - noreturn
在所有可能的代码路径中调用函数的函数本身可以被假定为noreturn
.
ret
只是意味着该函数将控制权返回给调用者.因此,main
确实call func
,CPU执行的功能,然后用ret
,CPU继续执行main
.
编辑
因此,事实证明,noreturn
不会使函数根本不返回,它只是一个说明器,告诉编译器该函数的代码是以函数不会返回的方式编写的.所以,你应该做的是确保这个函数实际上不会将控制权返回给被调用者.例如,你可以exit
在里面打电话.
另外,鉴于我所读到的关于这个说明符的内容,似乎为了确保函数不会返回到它的调用点,应该调用其中的另一个 noreturn
函数并确保后者始终运行(按顺序)避免未定义的行为)并且不会导致UB本身.
归档时间: |
|
查看次数: |
10595 次 |
最近记录: |