while(1);有什么作用? 我知道while(1)(无分号)无限循环,类似于自旋锁情况。 但是我看不到在哪里可以使用while(1);?
样例代码
1 2 3 4
| if(!condition)
{
while(1);
} |
注意:这不是do-while()或普通while(1)的情况。
-
这只是提供无限循环的另一种方法,该循环将从其代码块内中断。与for (;;)相似。手册页中有许多示例,您可以在其中找到无限循环,并使用更高版本的break在所需的迭代次数后退出循环
-
@ DavidC.Rankin while(1);(请注意;最后)循环没有可以中断的主体
-
啊..你是正确的先生。在这种情况下,您的do {...} while (1);似乎是唯一明显的用途。
-
我建议再次在问题中指出,您的意思恰好是while(1);,而不是while(1)。它容易错过,并且引起混乱。
-
恕我直言while (1);是耗尽笔记本电脑电池电量的最佳方法...
-
在您等待信号并设置信号并且您要暂停程序直到那时的情况下该怎么办?它不是最佳选择,但听起来确实像一个用例。
-
while(1)是未定义的行为,或者编译器可以假定此循环确实终止(无法记住哪个循环)。标准说。无论意图是什么,这都不是安全的代码。
-
实际上,您错了:while (1)是不完整的语句。分号终止(空)循环体。
-
现在,某些版本的GCC对此进行了优化,该语言可以假定每个函数都可以执行以下操作:编辑volatile,返回值,运行系统调用或退出(我可能会遗漏一个)。
-
#define ever ;; #define loop ; for (ever) loop
-
@RyanReich:也许是nitpick,但是循环体不是空的。这里的分号是空语句,循环主体由该空语句组成。
-
@Thomas我想我似乎知道一些技术术语,因为我试图做到精确,但实际上我不知道。我想重要的是,循环主体全部由其组成的单个语句内的表达式为空。这是由分号出现在何处而不是没有出现来表示的。
-
在过去(就像很久以前一样),当您使用BASIC在PC上编写程序时,该程序将根据关键事件执行操作,那么如何挂起"主"线程,以使程序不会结束?您执行的100 GOTO 100实际上与C中的while(1);相同。程序的所有功能都位于事件处理程序中。本质上,while(1);是Windows的"消息泵循环",它不会泵送消息。唯一有意义的时间是系统具有可以在此循环之外起作用的事件触发器。
-
在C ++中,@ usr是UB,但在C11中不是UB,因为它对控制语句使用常量表达式。
-
@RyanReich while循环的语法之后需要一个语句,在这种情况下,该语句为空语句。
请注意,该语言的所有有效陈述不一定是有目的的。根据该语言的语法,它们是有效的。
可以构建许多类似的"无用"语句,例如if (1);。
我将这样的语句看作是条件语句(if,while等)与空语句;(虽然显然没有特定目的)也是有效的语句的结合。
话虽如此,我在安全代码中遇到了while (1);。当用户使用嵌入式设备做非常不好的事情时,最好阻止他们尝试其他任何事情。
使用while (1);,我们可以无条件地阻止设备,直到获得授权的操作员手动重启设备为止。
while(1);也可以是内核恐慌的一部分,尽管for(;;) {}循环似乎是表达无限循环的更常用的方式,并且可能存在非空主体(例如panic_blink() )。
-
空无限循环的另一个用例是在实现assert()时,这与您的安全示例非常相似。
-
该安全代码不是很安全。编译器可以自由地优化while(1);。
-
@DavidHammen arent编译器优化应该在功能上保持等效吗?
-
@swordofpain他们应该的,是的。但请参阅groups.google.com/forum/#!topic/comp.std.c/vFfu3wMM0ZI
-
@DavidHammen Im在谈论非常特定的工具链和非常特定的编译器。人们通常会知道他们在做什么,而不是在编写可移植的代码。
-
@swordofpain应该保留可观察的行为。正如C / C ++标准所定义的,"该程序需要花费很长时间才能执行"是无法观察到的行为。即使"很长一段时间"等于无限,也不是这样。至少在C ++ 11中,有一些措辞允许实现假定所有无副作用的循环都将终止(并且,如果主体完全为空,则可以对其进行优化)。我不知道C标准的措词是否相同
-
执行无法访问的代码不会改变可观察的行为吗?哇,我每天都不太尊重C。
-
@DavidHammen据我了解,C ++编译器可以自由地优化该循环,但C语言编译器则可以。
-
cc @harold ^ C ++,而不是C。
-
顺便说一句,for (;;) {}更为流行,因为它不生成编译器警告
-
@harold:这可能是可观察到的行为的变化,但是由于程序调用了未定义的行为,因此该标准无关紧要。
-
@ user2357112那实际上不是参数,标准不是上帝,按定义,这就是问题所在
-
@harold:您是说无限,无副作用的循环不应该是未定义的行为吗?如果是这样,我真的没有话要说。有支持或反对的理由。我还没有形成意见。如果您说未定义的行为不应导致此类后果,则您要求C填补计算机语言设计空间中一个非常不同的领域。
-
@ user2357112实际上只是要求它真正填充它认为已填充的适当位置。它不是仅在高度专业化的语言或类似语言中才能看到的一些奇怪的功能。它只是实际执行您编写的事情应该做的基本原则,是的,我知道C对于总体而言太好了,它宁愿将您的代码作为建议,然后"随机进行",因为它可以。
-
@harold:编写C时,非常希望编译器生成可执行文件,该可执行文件将按照指定的语句执行顺序执行每个语句指定的操作;使用一种语言,其操作倾向于以可预测的方式映射到机器指令,这很有用。这样的用处没有消失。仍然需要一种语言,该语言可以按照C最初设计和使用的方式工作。不幸的是,名称" C"已被接受来代表完全不同的东西。
-
...(并且完全不适合C旨在实现的许多目的)。我不确定将" C"作为一种可用的系统编程语言需要采取什么措施,这种语言采用了程序员应提供其安全设备的理念,而不是采用超现代主义的思想,即编译器应消除程序员试图包括的所有安全措施。如果他们(编译器)确定它不满足编译器要求。
如果你深入组装
(从嵌入式系统的角度来看,或者如果您尝试对引导程序进行编程,这将更容易理解)
您将意识到while循环只是一个jmp指令...即
1 2 3 4 5 6
| (pseudo code: starting loop address)
add ax, bx
add ax, cx
cmp ax, dx
jz (pseudo code: another address location)
jmp (pseudo code: starting loop address) |
让我们解释一下它是如何工作的,无论如何,处理器将继续按顺序执行指令。因此,当它进入此循环的那一刻,它将在寄存器中添加寄存器bx并存储在ax中,在寄存器中添加寄存器cx并存储到ax,cmp,ax,dx中(这意味着从ax中减去dx),jz指令意味着跳转到(另一个地址如果设置了零标志(如果上述减法的结果为零,则标志寄存器中的一个位将被设置),然后jmp到起始循环地址(非常简单)并重做整个过程。
我打扰您所有这些程序集的原因是为了向您展示这将在C中转换为
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| int A,B,C,D;
// initialize to what ever;
while(true)
{
A = A + B;
A = A + C;
if((A-D)==0)
{break;}
}
// if((X-Y)==0){break;} is the
// cmp ax, dx
// jz (pseudo code: another address location) |
因此,请想象一下汇编中的senario,如果您有很长的指令列表而不是以jmp(while循环)结尾,可以重复某些部分或加载新程序或执行某些操作...
最终,处理器将到达最后一条指令,然后加载以下指令以查找所有内容(然后将冻结或三重故障等)。
这就是为什么当您希望程序在触发事件之前不执行任何操作时,必须使用while(1)循环,这样处理器才能继续跳转到原来的位置而不会到达该空指令地址。当事件被触发时,它跳转到事件处理程序指令地址,执行它,清除中断,然后返回到while(1)循环,仅跳转到其位置等待进一步的中断。顺便说一句,如果您想了解更多有关while(1)的信息,则称为超循环...对于在这一点上发狂地为之争辩并发表负面评论的人,这不是汇编教程,讲座或任何内容。只是简单的英文解释,就尽可能地简单,它忽略了很多底层的细节,例如指针和堆栈以及诸如此类的东西,并且在某些情况下还简化了事情以使观点更明确。没人在这里寻找文档准确性,我知道这个C代码不会像这样编译,但这仅适用于演示!
-
因此,用通俗易懂的英语来说,它可以防止程序退出直到准备就绪,即使您的程序没有做任何事情。是?
-
是的,说得很好,直到事件或中断被触发。
这被标记为C,但是我将从C ++的角度开始。在C ++ 11中,编译器可以自由地优化while(1);。
从C ++ 11草案标准n3092,第6.5节第5款(强调我的观点):
A loop that, outside of the for-init-statement in the case of a for statement,
— makes no calls to library I/O functions, and
— does not access or modify volatile objects, and
— performs no synchronization operations (1.10) or atomic operations (Clause 29)
may be assumed by the implementation to terminate. [Note: This is intended to allow compiler transformations, such as removal of empty loops, even when termination cannot be proven. — end note ]
C11标准有一个相似的条目,但有一个关键的区别。根据C11草案标准n1570(强调我的):
An iteration statement whose controlling expression is not a constant expression,156) that performs no input/output operations, does not access volatile objects, and performs no synchronization or atomic operations in its body, controlling expression, or (in the case of a for statement) its expression-3, may be assumed by the implementation to terminate.157)
156) An omitted controlling expression is replaced by a nonzero constant, which is a constant expression.
157) This is intended to allow compiler transformations such as removal of empty loops even when termination cannot be proven.
这意味着while(1);可以假定在C ++ 11中终止,但不能在C11中终止。即使这样,某些供应商仍将注释157(非绑定)解释为允许他们删除该空循环。 C ++ 11和C11中的while(1);之间的区别是已定义行为与未定义行为的区别。由于循环为空,因此可以在C ++ 11中将其删除。在C11中,while(1);被证明是不终止的,这是未定义的行为。由于程序员已调用UB,因此编译器可以自由执行任何操作,包括删除该有害循环。
关于优化删除while(1);的编译器,有许多关于堆栈溢出的讨论。例如,是否允许编译器消除无限循环?,是否可以优化掉用作睡眠的空for循环?,可以优化" while(1);"。在C ++ 0x中。请注意,前两个是C特定的。
-
实际上有编译器可以这样做吗?
-
同意while (1);是非终止的,但是为什么有人会称其为"未定义的行为"?对我来说,这似乎很好。这可能是理想的行为,也可能不是理想的行为,但是对于编译器而言,优化它的行为改变了程序的含义,因此很糟糕。
-
@Tibor:Intel,clang和其他公司正是这样做的。 C11标准中的该额外条款("其控制表达式不是常量表达式")似乎允许while(1);。但是,C11标准5.1.2.3 p 4表示:"在抽象机中,所有表达式都按照语义指定的方式求值。如果实际实现可以推断出未使用其值且不需要该值,则它不需要评估表达式的一部分。产生副作用……"因此,C11中仍然有一些可供编译器供应商优化的while(1);。
-
@DavidHammen"实际的实现可以推断出未使用其值并且不会产生所需的副作用,因此无需评估表达式的一部分" while (1);是语句,而不是表达式。
-
另外,我认为它具有非常可预测的副作用。对"需要"的判断有些模糊。
-
副作用包括执行I / O,更改变量的值。浪费CPU时间不算副作用。
-
具体而言,C11 5.1.2.3 P 2,"访问易失性对象,修改对象,修改文件或调用执行任何这些操作的函数都是副作用,它们都是执行环境状态的变化。"。浪费CPU时间不视为"副作用"。
-
@Tibor是的,我在一个相关问题上对此发表了一些评论。
嵌入式软件上的一种用法是使用看门狗实现软件重置:
或同等但更安全,因为它使意图更清晰:
1
| do { /* nothing, let's the dog bite */ } while (1); |
如果看门狗已启用并且在x毫秒后未得到确认,我们知道它将重置处理器,因此可以使用它来执行软件重置。
-
问题是关于while(1);的问题,即没有主体的while循环(请注意分号)。
-
@杰夫我把身体的那一部分去掉了
-
这是发生致命错误后如何复位PIC32处理器的方法。
-
并不是说while (1);的用法到目前为止可能是所有现有C代码中最重要的用法。
我假设while(1);与do循环不关联...
我看到的while(1);的唯一半有用的实现是等待中断的空循环。例如父进程正在等待SIGCHLD,表明子进程已终止。在所有子进程终止后,父级的SIGCHLD处理程序可以终止父线程。
它可以解决问题,但会浪费大量CPU时间。这样的用法也许应该执行某种睡眠以定期放弃处理器。
-
您可以使用while(1) pause();,而这恰恰是pause系统调用的唯一优点。在任何其他情况下,使用pause都可能是一个错误,可以通过使用带有适当参数的sigsuspend来解决。
我见过的while(1);的一个地方是嵌入式编程。
该体系结构使用主线程来监视事件,并使用工作线程来处理事件。有一个硬件看门狗定时器(在此说明),将在一段时间后对模块执行软复位。在主线程轮询循环中,它将重置此计时器。如果主线程检测到不可恢复的错误,则将使用while(1);捆绑主线程,从而触发看门狗复位。我相信断言失败也是通过while(1);实现的。
正如其他人所说,这只是一个无所事事的无限循环,完全类似于
1 2 3
| while (1) {
/* Do nothing */
} |
分号的循环确实有一个主体。当用作语句时,单个分号是空语句,并且循环主体由该空语句组成。
为了提高可读性,使读者清楚地知道null语句是循环的主体,我建议将其写在单独的一行上:
否则,很容易在" while"行的末尾漏掉它,因为通常没有分号,读者可能会将下一行误认为是循环的主体。
或者使用空的复合语句。
在AVR芯片组编程(使用C编程语言)中,此语句经常使用,它起着类似于事件循环的作用。
假设我要设计一个递增计数器,因此可以使用以下代码实现它:
1 2 3 4 5 6 7 8 9 10 11
| void interrupt0() {
/* check if key pressed, count up the counter */
}
void main() {
/* Common inits */
/* Enable interrupt capability and register its routine */
/* Event loop */
while(1);
} |
这可用于等待中断。基本上,您初始化所需的所有东西,并开始等待某些事情发生。之后,将调用并执行某些特定功能,然后返回到等待状态。
可以按下按钮,单击鼠标/移动鼠标,接收数据等。
我还要说的是,UI框架实际上经常使用类似的东西。在等待有关用户操作的信号时。
实际上非常有用。特别是当它是一个具有某种密码的程序,并且您想为用户禁用该程序时,因为例如,他输入了3次错误的密码。使用while(1);会停止程序的运行,直到重新启动程序后,什么都不会发生,主要是出于安全原因。
-
问题是关于while (1);,而不仅仅是while (1)。分号很重要;表示没有循环体。 :)
-
@jalf没注意到分号,我不好,已修复
-
尽管最受好评的答案也解决了安全性问题,但最受好评的答案还是阐明了它仅是有用的嵌入式系统。对于在任何多任务系统上运行的几乎所有程序而言,它对于安全性来说完全没有用。
我认为使用while(1);的原因是因为在代码的较早位置已在此线程上设置了EventHandler或中断。当您知道您的代码只会在很短的时间内"等待"时,使用标准的Thread Safe锁定代码可能会(在时间上)非常昂贵。
因此,您可以使用while(1);设置中断和"旋转",尽管这是一个忙等待(不让CPU空闲/服务其他线程),但设置周期很少。
总而言之,这是线程等待中断或事件时的"廉价"自旋锁。
由于条件始终为真,因此可以说我们正在使用数学中已知的逻辑重言式。
尽管循环证明总是正确的,但除非有代码强制或直到资源崩溃,否则循环不会停止循环。