【问题标题】:Why is it okay to declare a STRUCT inside a nested loop?为什么可以在嵌套循环中声明 STRUCT?
【发布时间】:2019-09-14 19:20:02
【问题描述】:

这是一些提供的 (CS50) 代码的 sn-p,它在嵌套循环中一遍又一遍地声明相同的 STRUCT“三元组”。为什么这样可以?在我看来,在嵌套循环的范围上方声明 STRUCT“三元组”并在每次迭代时更改值会更有效。

typedef struct
{
    BYTE rgbtBlue;
    BYTE rgbtGreen;
    BYTE rgbtRed;
} __attribute__((__packed__))
RGBTRIPLE;

.

 for (int i = 0, biHeight = abs(bi.biHeight); i < biHeight; i++)
{
    // iterate over pixels in scanline
    for (int j = 0; j < bi.biWidth; j++)
    {
        // temporary storage
        RGBTRIPLE triple;

        // read RGB triple from infile
        fread(&triple, sizeof(RGBTRIPLE), 1, inptr);

        // write RGB triple to outfile
        fwrite(&triple, sizeof(RGBTRIPLE), 1, outptr);
    }

【问题讨论】:

  • 我认为在这种情况下“分配”应该被认为是“声明”——我不希望一个公认的初学者知道所有的术语:)。
  • 是的,我在分配命名法上的错误,它已声明 - 我会解决这个问题。尽管如此,同一个声明的数千次迭代是否比第一个循环之前的单个声明更好?这是否适用于所有变量,可以在它们将使用的范围内一遍又一遍地声明它们吗?
  • 感谢所有花时间帮助新手的人。从无知到理解还有很长的路要走,而这个论坛是长途陌生旅行的好工具……-Bill
  • 比尔,很高兴能提供帮助!请您点击最适合您的答案旁边的复选标记以将该答案标记为已接受?这将对未来的读者有所帮助,并将提高您和回答者的声誉。谢谢!
  • 啊,我没有意识到我需要选择获胜者......如果在杂货店选择一条线是一个指标,我的选择不会很有帮助。事实上,正确理解的大部分是 cmets 中的交互。再次感谢!

标签: c cs50


【解决方案1】:

在大多数情况下,此级别的效率是编译器关心的问题。编译器可能会为每个 RGBTRIPLE 重用相同的堆栈空间! (虽然不是必须的。)

将 RGBTRIPLE 放在需要它的最小大括号对(范围)内可防止您在变量的内容可能无效时意外、错误地访问该范围之外的该变量。

【讨论】:

  • 范围安全胜过效率绝对是这段代码 sn-p 更重要的收获,尤其是对于初学者。 +1
  • 是的,我会比@PhilipWrage 更进一步地建议范围安全是这里的唯一要点。对初学者有用的教训是,尽管表面上看,但这个声明对效率没有影响,因为它本身实际上并没有生成任何可执行的材料。
【解决方案2】:

仍然,同一个声明的数千次迭代是否比第一个循环之前的单个声明更好?

当然没问题。无论哪种方式,一个好的编译器都不会产生任何性能差异的代码。

对于适度的线性性能变化,可能会减少调用fread()的频率。

for (int i = 0, biHeight = abs(bi.biHeight); i < biHeight; i++) {
    RGBTRIPLE triple[bi.biWidth];
    fread(triple, sizeof triple, 1, inptr);
    fwrite(triple, sizeof triple, 1, outptr);
}

甚至

RGBTRIPLE triple[biHeight][bi.biWidth];
fread(triple, sizeof triple, 1, inptr);
fwrite(triple, sizeof triple, 1, outptr);

很多因素都需要考虑编码。避免专注于诸如此类的微优化。

【讨论】:

  • 谢谢大家的帮助;我的收获是保持最小的数据范围必要胜过通过变量声明循环。这不是关于优化本身,而是我需要知道事情是如何运作的神经症。最后一个问题:声明和 fread 行之间的断点;将为三元组的元素提供一个有效的占位符,但不是有效的数据(无论迭代如何)。先前迭代中的“三元组”数据在重新声明时消失了。真的? RGBTRIPLE triple[bi.biWidth]; BREAK POINT fread(triple, sizeof triple, 1, inptr);
  • @2Tall " 上一次迭代中的‘三元组’数据在重新声明时消失了” --> 不完全。对象生命周期持续到 {} 块结束。
  • 因此,声明只会在第一次遇到时对 {} 内的执行代码产生影响。你只需要在使用前声明它,当你完成范围时它就消失了。这样可以轻松保持紧张,感谢您的澄清 - 这不是我设想的过程。
【解决方案3】:

这里要理解的重要一点是,RGBTRIPLE triple; 声明声明了变量,但并不直接对应于“创建存储”,或者根本不翻译为任何机器语言输出。

您只是在向编译器声明此变量的范围和用法(并且在最局部的块内声明是一种很好的做法,即您只希望它在该区域内有效)。您可以将此行放在循环之外,或者放在其函数的顶部,而不更改可执行输出。

编译器的工作是在堆栈上有效地创建空间以供运行时使用局部变量。在实践中,它会简单地重用每次循环迭代一遍又一遍使用的相同空间。 (这里的人们会正确地告诉你,这不是保证,这在技术上是正确的,但实际上它总是会重用相同的空间,就像你在循环上方声明它一样。 )

【讨论】:

    猜你喜欢
    • 2020-02-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-09
    • 1970-01-01
    • 2015-12-23
    相关资源
    最近更新 更多