是什么让C开发人员如此好奇“ i ++ == ++ i”?[关闭]


15

只是随机观察,似乎在StackOverflow.com上,是否存在有关“ ++ i == i ++”的问题。这个问题一直被问到,我想我在过去两个月中看到了大约6到7次问。

我只是想知道为什么C开发人员对它如此感兴趣?C#和Java开发人员也存在相同的概念/问题,但我想我只看到一个与C#相关的问题。

是因为有这么多示例使用++ i吗?是因为有一些受欢迎的书或教程吗?是因为C开发人员只是喜欢尽可能多地将它们塞进一行以提高“效率” /“性能”,并因此更频繁地使用++运算符遇到“怪异”的构造?


23
(对于我作为C#开发人员而言,i ++和++ i相等。我知道它们不相等,但是如果我编写依赖于差异的代码,我宁愿将其重构为更具可读性,并希望编译器和JITter会注意它,但是作为C#开发人员,我并不想节省一个或两个时钟周期,因为无论如何我都已经比较低效了)
Michael Stum

1
作为新兴的程序员,我们喜欢玩算术,逻辑,并对事物的工作方式感到好奇。我认为这是我们菜鸟喜欢这些问题的重要原因。它可能不是一项突破性的功能,但确实很有趣!我们最初成为程序员的原因不是吗?
加尼

2
你是从字面上指的是表达++i == i++,或者更一般约在意义之间的差异++ii++
基思·汤普森

Answers:


30

我怀疑其中至少有一部分更简单:即使到现在,我们仍然在学年开始时看到很多类似的问题,并且在全年中逐渐减少。

因此,我认为可以猜测,其中相当一部分仅仅是班级的结果,在该班级中,老师至少谈论了一点,但并不能很好地解释他的观点(通常不因为他本人并不真正了解他们)。尤其是根据似乎提出这些问题的人们,很少有基于实际编码的人。


14
这不是永恒九月的对立面吗?永恒的九月是AOL开始向其客户提供Usenet访问权的一年,使整年看起来像九月(新入学的学生传统上以新手炮轰董事会的月份)。
nlawalker 2011年

这是一个有趣的理论,但如果有人不理解材料,也许并不是老师的错:也许学生(1)上课时没有给予足够的重视,以及(2)懒得在之前找到关于stackoverflow的答案再次问同样的问题。
Giorgio 2014年

12

因为C程序员必须了解操作顺序。C#开发人员不必使用按位运算符(&,|,〜)或前缀运算符,因为我们通常更担心其他内容。但是,我们的C#开发人员没有意识到的是,我们应该知道我们每天使用的运算符做什么以及如何正确使用它们。我们大多数人都只是避开可能会出现问题的地方。

在您的头顶上,此代码段的输出是什么?

    double x = 5.5;
    Console.WriteLine(x++);
    x = 5.5;
    Console.WriteLine(++x);

我想说很多经验丰富的C#开发人员都不知道控制台的输出是什么。


3
+1。在这种情况下,了解固定前和固定后增量/减量运算符之间的区别是值得的。
Maulrus

4
我有点明白这一点,我也不知道输出(我相信它是5.5和6.5,但我不确定),对我来说这并不重要,因为我将其重构为x ++。Console.WriteLine(x); 立即。但是,我并不是完全一无所知,我只是认为这是一种具有零可用性的大代码味道。
Michael Stum

9
问题的答案与操作顺序或前缀和后缀增量之间的差异无关。这是定义和未定义行为的问题。
David Thornley,2010年

2
我今天遇到了这个。有点像if (myList[i++] == item)。聪明。我发现它正在寻找IndexOutOfRange异常的源……确实,非常“聪明”。
rmac

7
我认为它是5.5和6.5,但我以前从未见过++用在浮子上,我必须说它感觉不太合适。人们这样做吗?(我更喜欢+= 1
Bart van Heukelom

8

我认为这很明显。在C#中,对于int ii++ == ++i始终为false,而++i == i++始终始终为真,可靠,并且每个感兴趣的人都可以通过学习C#的规则(或者实际上只是通过运行C#的规则)轻松地发现这一点。

另一方面,在C和C ++中,它几乎是未定义的,因为它取决于编译器,执行环境,平台等,因此这是一个很难回答的问题,因此很多人提出了这个问题。


+1-但这并不是一个很难回答的问题。“未定义”是一个词。所有“取决于...”的东西都是大多数人不担心的东西。即使您只为一个特定的平台编写程序,也应该在整个语言中使用普通的习惯用语,并且大多数增量使用前增量和后增量运算符都是一些常见习语的一部分。
2011年

+1:这实际上回答了询问的问题(也就是说,您不是在两行之间阅读)。
Thomas Eding

7

这是一个受欢迎的问题,因为这是一个技巧问题。它是未定义的

上面的链接转到Bjarnes Stroustrup主页上的FAQ,它专门解决了此问题,并解释了为什么未定义此构造的原因。它说的是:

基本上,在C和C ++中,如果在表达式中读取了两次变量(在该变量中也写入了变量),则结果是不确定的。

评估顺序未定义可以产生更好的代码。


5

很可能是因为在C中,如果您编写 i++还是++i执行指针算术。在那里,i手术前后增加可能至关重要。我给你举个例子,但是我上一次写C的时间是10年前。

在C#中,我从来没有遇到过需要我考虑的情况,i++或者++i因为AFAIK对于for / while循环而言,这并不重要。


3
了解C#的差异仍然很重要-只是因为您似乎只在for / each循环中使用它们并不意味着这是找到它们的唯一地方
STW 2010年

完全正确。
Mladen Prajdic

3
这与问题无关。问题不在于i++和之间的区别++i,而在于将它们组合在同一表达式中。
David Thornley,2010年

@David问题是为什么C程序员对此感到好奇。答案(在C中很重要,在C#中不那么重要)是完全相关的。”
Florian F

2

几年前,我读到这些操作员被认为是危险的,因此我试图从中获取更多信息。如果那时stackoverflow上升了,我会在那里问。

现在是因为有些扭曲的人写了一些循环,例如

while( [something] )
{
  a[++j] = ++j;
}

即使是现在,我也不太确定会/应该发生什么,但是对我来说很明显,同一行上的多个++ [var]正在引起麻烦。


2
作为C中一项作业的一部分,增量具有未定义的行为
tzenes 2010年

7
像这样x = ++j;的分配在C中得到了很好的定义。上面的内容是未定义的,因为j修改了两次而没有中间的顺序点。
马修·弗拉申

2

有两个原因。

++ i的正确实现是递增i,然后返回它。i ++的正确实现是保存当前值,递增i,然后返回保存的值。知道这些没有被实现为同义词很重要。

然后问题变成了编译器何时在评估表达式(例如相等性)应用它们。

如果先进行相等性测试,然后进行递增和递减前运算符,那么您将不得不编写与首先评估左侧(lhs)和右侧(rhs)的逻辑不同,然后再编写逻辑平等。

这全都与操作顺序有关,而这反过来又会影响一个人编写的代码。而且,并不奇怪,并非所有语言都同意。

基本测试列出了这些信息,使开发人员可以进行测试以查看其假设是否正确以及实际实现是否与语言规范匹配。

使用指针,循环或返回值时,这可能会产生各种影响。


5
关于此的基本测试将产生误导,因为它是未定义的行为。如果它这次完全按照您的期望运行,则不能保证下次会运行。
David Thornley,2010年

您回答了错误的问题。您应该回答为什么C程序员对此感到好奇。
雨果

无论如何,第二个参数还是不正确的(至少在C ++ 11之前):的行为++i使用该值,i+1并且在某个时间点(可能在此之前或之后)i使用该值,但在下一个序列点之前,使用递增
MM

@MattMcNabb-您能指出我的规范吗,我很想知道实施中是否发生了变化。在C很好地映射到PDP程序集的年代,++ i注入了INC指令,而后缀版本必须将值复制到寄存器中,以便在增量后使用指令。是什么构成了您描述的更改的必要性?
Walt Stoneburner 2015年

2

我认为这是文化冲击。

正如已经指出的那样,由于未严格定义后递增等的执行顺序,因此无法定义C或C ++中表达式的行为。未定义行为在C语言中很常见,当然,很多行为已导入C ++中。

对于以另一种语言进行过编程的人-有人认为编程语言应该是精确的,而计算机应该按照他们被告知的去做……对,这真是令人震惊。发现C在某些方面是故意模糊的。


之所以使用C,是因为它被设计为支持比定义C#时甚至更多的平台,包括您能想到的最奇怪的嵌入式或大型机平台。另一方面,C#可以在x86和360上运行?也许ARM也一样?没有其他的。这就是C#可以定义更多内容的原因。
DeadMG

++另外,C并非首先基于PDP-11构建吗?哪里有自动递增指令?C希望“靠近机器”,以便可以有用地使用指令集。
Mike Dunlavey

@MikeDunlavey:不,C ++--运算符不是基于PDP-11,当以C的前代语言B引入这些运算符时就不存在。请参见cm.bell-labs.com/who/dmr/chist.html,以及搜索“自动增量”。
基思·汤普森

1

也许仅仅是C开发人员通常更频繁地使用++递增-看到C如何没有“ foreach”或类似的语句来遍历数组。哦,当然还有指针运算,如前所述!


1
好点,忘记了foreach是一个相对现代的概念。
Michael Stum

-1

这两部分之间的区别:post和preincrement的工作方式是这两段代码在每种语言中均应起作用。这不是语言依赖!!!

++i

这是预增量。在将值i发送到使用它的行之前,这段代码将首先增加的值。

i++

这是职位增加。这段代码i在增加的值之前将返回的值i的那一行。

在另一个小示例中:

int main(void)
{
  int i = 0; 
  int result; 
  result = 10 + ++i; 
  printf("%d \n", result); // = 11 

  int j = 0; 
  result = 10 + j++; 
  printf("%d \n", result);// = 10
  return 0;
}

这段代码会在键盘上编译结果。

这与操作符重载的内部实现有关。

++i 直接增加值(因此快一点)

i++首先创建一个副本,该副本将返回到需要的位置,然后i变量的原始值增加一。

我希望现在已经清楚了。


但这并没有解决实际的问题:为什么C开发人员比其他人提出更多的要求。OP了解操作员。
Martijn Pieters 2014年

同样,这个答案实际上是不正确的。 ++i不会更快,并且i++不会创建副本。此外,++iC语言中的行为与C ++中的行为略有不同,更不用说其他语言了。
MM
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.