为什么这段代码没有报错?

3
void main() {
    int i, j=6;
    for(; i=j ; j-=2 )
        printf("%d",j);
}

按照常规格式,第一个分号后面应该是一个“条件”,但这里却是“初始化”,因此应该出现错误提示。这个格式怎么可能是有效的呢?
但实际输出结果却是“642”。

2
它应该是一个表达式,而赋值也是一种表达式。 - Some programmer dude
1
没有初始化,只有赋值。顺便说一下,void main 是非标准的写法。 - n. m.
1
这基本上是 (i=j) != 0 的简写,因为 0false - 4castle
1
如果你使用-Wall选项编译clang,你会得到两个警告。第一个是因为你弄错了main的声明。第二个是因为你弄错了for语句(将赋值用作条件表达式)。如果你没有使用-Wall选项进行编译和/或忽略了警告,那么我无法充分表达我的意见(由于“友好”政策)。 - user3386109
1
@Nitish Prajapati:C语言没有专门的“条件”这种东西。每个“条件”基本上都是一个任意(非void)表达式,将隐式测试其是否等于零。i=j是一个非void表达式,这意味着它完全可以作为“条件”。 - AnT stands with Russia
显示剩余9条评论
4个回答

3
首先,让我更正一下术语,“i=j”是一种“赋值”,而不是“初始化”。
话虽如此,让我们先分析一下“for”循环的语法。
所以,“expression-2”应该是一个“表达式”。
现在,来看一下具有赋值运算符的语句的语法。
因此,正如规范C11在第§6.5.16章中提到的那样,赋值操作也是一个“表达式”,非常适合“for”循环语法中的“expression-2”部分。
关于“结果”,
因此,“i=j”将基本上将“j”的值赋给“i”,然后使用“i”的值进行条件检查(即作为TRUE或FALSE的非零或零)。
简而言之,从语法上讲,代码没有问题,因此编译器不会生成错误。
此外,在托管环境中,“void main()”应该是“int main(void)”,以符合标准。

明白了..谢谢 :) - Nitish Prajapati
相反地,有时候 main() 包含参数 [不是 void]。那这些参数是用来做什么的?请详细解释。 - Nitish Prajapati
@NitishPrajapati 这是针对 void main() 的。 它不处理命令行参数,对吗? - Sourav Ghosh
我不太明白你想说什么。我听过“命令行参数”的术语,但只是听说过而已。实际上,我不知道它们的目的以及它们所做的事情,也不知道为什么只能是main(int argc, char **argv)。argc和argv有什么作用呢? - Nitish Prajapati
我也看到过这个问题与 int main() 有关,而不仅仅是 void main()。 - Nitish Prajapati
@NitishPrajapati 好的,给我一些时间。 - Sourav Ghosh

3

i=j也是一个表达式,其值是赋值后i的值。因此它可以作为条件。

通常会这样巧妙地使用:

if ((ptr = some_complex_function()) != NULL)
{
  /* Use ptr */
}

有些程序员喜欢将赋值和检查合并到一行代码中。这种写法的可读性好坏取决于个人观点。


哦,好的好的!明白了。谢谢。 - Nitish Prajapati
顺便问一下,ptr是指针还是变量?我猜它只是一个变量。 - Nitish Prajapati
1
@NitishPrajapati,我不确定你所说的“或”是什么意思。指针只是一个可以存储特定类型地址的变量。 - StoryTeller - Unslander Monica
我是指它是一个指针变量还是非指针变量。 - Nitish Prajapati
@NitishPrajapati,我想NULL的比较已经说明了它是一个指针 :)。 - StoryTeller - Unslander Monica
请问您能解释一下这句话的意思吗?"与NULL进行比较就能知道指针的情况"。 - Nitish Prajapati

1
你的代码没有语法错误,因此编译器接受它并生成代码以产生 642
条件 i=j 被解释为 (i = j) != 0
为了防止这种错误模式和许多类似的错误,启用更多的编译器警告并使它们致命,方法如下:
gcc -Wall -W -Werror

如果您使用 clang,请使用 clang -Weverything -Werror。保留html,不做解释。

明白了。谢谢。 :) - Nitish Prajapati

1

这是一个非常好的问题。

为了真正理解它,你最好知道C代码在计算机中是如何执行的: 首先,编译器将C代码编译成汇编代码,然后汇编代码将被翻译成可以直接在主存储器中运行的机器码。

至于你的代码:

void main() {
    int i, j=6;
    for(; i=j ; j-=2 )
        printf("%d",j);
}

为了弄清楚结果为642的原因,我们需要查看它的汇编代码。
使用VS调试模式,我们可以看到:

enter image description here

Especially look at this:

010217D0  mov         eax,dword ptr [j]  
010217D3  mov         dword ptr [i],eax  
010217D6  cmp         dword ptr [i],0  
010217DA  je          main+4Fh (010217EFh)  

这四行汇编代码对应于C代码中的“i=j”,它的意思是,首先将j的值移动到寄存器eax中,然后将寄存器eax的值移动到i中(因为计算机不能直接将j的值移动到i中,它只能使用寄存器eax作为桥梁),然后比较i的值和0是否相等,如果相等,则跳转到010217EFh,循环结束;否则,循环继续。
因此,实际上首先进行赋值,然后进行比较以决定循环是否结束;当6减少到0时,循环最终停止,希望这可以帮助您理解为什么结果是642:D

谢谢。是的,很有帮助。我明白了它背后的概念。 - Nitish Prajapati

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接