【问题标题】:Which is the better way to declare dummy variables for nested loops?为嵌套循环声明虚拟变量的更好方法是什么?
【发布时间】:2017-02-18 04:51:17
【问题描述】:
  1. 方法 1 的优点是文件大小略小,因为源代码中的文本字符较少:

    int i, j;
    for (i = 0; i < numRows; i++)
        for (j = 0; j < numCols; j++)
        //<some code here>
    
  2. 方法2的优点是局部变量的范围更小。

    int i;
    for (i = 0; i < numRows; i++)
    {
        int j;
        for (j = 0; j < numCols; j++)
        //<some code here>
    }
    

即使在当今的现代计算机中优化方面的差异可以忽略不计,哪种方法被认为是“更好”的代码?


编辑以澄清这个问题不是重复的:

这个问题是基于当前的 C11 标准,它不允许这样的语法:

for (int i = 0; i < numRows; i++)

在 C++ 和 C99 中,这种语法完全可以接受,而 C11 不允许在 for 语句中声明变量。


编辑以纠正错误信息:

我以为我在使用 C11,因为我最近从 CodeBlocks 下载了编译器,所以我说 C11 不允许在 for 语句中声明变量。但事实证明我实际上使用的是 C90,这是我问题的根源。

【问题讨论】:

  • @tod:您建议的副本是一个不错的答案——除了标记为c++ 而不是c。这使得它作为 C 问题的副本的可取性低于 100%。这些信息大部分仍然有效——但令人惊讶的是,没有讨论为循环内定义的string 调用的构造函数和析构函数。
  • @FridaySky: waddya 是什么意思?那些自 C99 以来一直在 C 中!
  • @FridaySky:如果那是 GCC 版本 4.x,那么它默认为 -std=gnu90 模式,这意味着 GNU 对 C90 标准的扩展。如果您使用 5.x 或更高版本,则默认为 -std=gnu11,这意味着 GNU 对 C11 标准的扩展。我冒昧地猜测您正在使用可能会产生该消息的 GCC 4.x。添加-std=c11-std=gnu11(或-std=c99-std=gnu99),它们会编译得很好。 C90 标准已经 17 多年没有更新了。
  • 参数DummyVariables 正在命名一个不存在的文件。你之前错过了-o吗?您可以使用gcc --version 检查 GCC 版本。你可以通过运行gcc -v … 来了解它的真正作用。输出应包括版本信息 IIRC。

标签: c nested-loops


【解决方案1】:

为了纯粹的紧凑性和范围的限制,我会使用:

for (size_t i = 0; i < numRows; i++) {
    for (size_t j = 0; j < numCols; j++) {
    //<some code here>
    }
}

注意size_t 似乎是数组索引的使用。 size_t 类型是unsigned 整数类型,保证能够保存任何数组索引。只是风格问题,但我也建议在所有循环体周围使用大括号。这大大降低了您因不可避免的更新和更改而破坏代码的可能性。

通过养成使用这样的块作用域声明循环变量的习惯,你强迫自己选择在代码的其他地方使用存储在循环变量中的值。

【讨论】:

  • 嗨,大卫。感谢你的回复。但正如我在彼得回答的另一条评论中提到的那样,我担心 for 语句中的变量声明。虽然 C99 确实允许您的语法,但从我读过的内容来看,C11 不允许在 for 循环括号内声明变量。
  • @FridaySky-- C11 确实允许。你在哪里读过这个? From the C11 Draft Standard: "如果第 1 条是一个声明,它声明的任何标识符的范围是声明的其余部分和整个循环,包括其他两个表达式......"
  • @FridaySky-- 在 C 中使用循环范围的变量(在有意义的情况下)被许多甚至可能是大多数专业程序员认为是最佳实践。
  • 实际上,我没有从任何地方阅读它。我读到 C99 是 C 的较旧标准,并且我还读到 cmets 说循环范围的变量声明适用于 C99,所以我假设循环范围的变量声明不适用于C11,因为它对我不起作用。
【解决方案2】:

这似乎是一个品味问题,而不是有任何明确的答案,但我会给你我的意见:

考虑到当前的计算机,保存几个字符的源代码太微不足道了,甚至想都不敢想。事实上,我想即使在 1976 年我在 VAX 11/780 上学习 C 时,我也会这么说。

我倾向于第二个例子,因为当前的偏好是声明变量尽可能接近第一次使用。在 C++ 中,您甚至可以将循环变量的声明放在 for 语句中:

for (int i = 0; i < numRows; i++) {
   for (int j = 0; j < numCols; j++) {
      ...
   }
}

但这仍然只是个人喜好问题:相信如果变量的声明接近其用途,程序将更具可读性。

【讨论】:

  • C99 允许for (int i = ...。不需要 C++
  • 谢谢。我尝试寻找 ANSI C 规范,但我得到了 1988 版本。无论如何,该规范是为编译器编写者编写的,而不是为用户编写的。希望有一天能找到与 Harbison & Steele 书“C:参考手册”等效的 ANSI C。 (也适用于 C++ 版本。)
【解决方案3】:

这两种方法都不是首选。

两个常见的编码准则是 (1) 确保没有变量存在超过其需要的时间,以及 (2) 不要将变量用于多于一件事。遵循这些准则可以减少(通常但并非总是)以非预期的方式意外使用变量,因此有助于避免细微的编程错误。

在您的第一种情况下,ij 都将继续存在,直到封闭范围结束 - 这意味着它们在循环完成后存在。这最大限度地增加了后续代码(在该封闭范围内)意外重用 ij 用于其他目的(例如,当意图是使用另一个变量时)的机会。此类错误通常很难找到。

第二种情况也有同样的问题,除了i。不过,即使一个变量存在这样的问题也是坏消息。

我可能会使用类似的构造

// unintentionally using i or j here will cause a compilation error

for (int i = 0; i < numRows; i++)
{
    // unintentionally using j here will cause a compilation error

    for (int j = 0; j < numCols; j++)
    {
       //<some code here>
    }

    // unintentionally using j here will cause a compilation error
}

// unintentionally using i or j here will cause a compilation error

(我为了说明这一点而插入的 cmets 使这更难以理解,但在实践中通常不需要这样的 cmets。

这确保了ij 都不存在于外循环之外。这也意味着j 不能在外循环中意外使用。实际上,当打算使用j 时,很容易键入i(反之亦然)——例如,它们在QWERTY 键盘上靠得很近。 ij 在视觉上看起来也很相似,所以视觉代码检查经常会漏掉这样的错误。然而,使用这样的方法,编译器会检测到这样的错别字。如果有选择,最好让编译器找出错误,而不是让人类难以找到它们。

当然,这并不能防止内部循环中ij 的误用或互换——但这是指南经常鼓励使用比ij 更能提供信息的名称的原因之一——误用视觉上不同的名字更容易被凡人发现。

【讨论】:

  • 嗨,彼得。感谢你的回复。我对您的构造的担忧是某些 C 编译器不允许在 for 循环的括号内声明变量。但是,对于 Java 等其他语言,您的构造将非常理想。
  • 别担心我之前的评论。事实证明,我使用的编译器与 C90 一样古老,尽管我大约一年前才安装了该编译器。
【解决方案4】:

第二种方法是最好的方法

  1. 它可以使用低内存
  2. 如果在任何其他循环中需要,允许再次使用相同的变量

【讨论】:

  • 真的会少用内存吗?我的理解是大多数编译器会自动在循环内移动变量。
猜你喜欢
  • 2010-10-14
  • 2018-04-07
  • 2012-10-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-02-28
  • 2011-03-20
相关资源
最近更新 更多