2012-09-23 61 views
6

我一直觉得我不应该在循环中定义一个变量,因为它是不必要的或浪费的。这使我想知道如果下面的recv()函数需要一个新的缓冲区循环的每次迭代:为什么在循环内部定义这个缓冲区?

while (totalBytesRecvd < echoStrLen) 
{ 
    char buffer[BUFSIZE]; 
    numBytes = recv(sock, buffer, BUFSIZE - 1, 0); 
    ... 
    totalBytesRecvd += numBytes; 
    buffer[numBytes] = '\0'; 
    fputs(buffer, stdout); 
} 

为的recv()的文档没有提及它如何使用缓冲区指针什么。为了更好的理解,我试着在循环之前定义缓冲区,并且recv()似乎覆盖了缓冲区,而不是重新定义缓冲区。这是有道理的,因为recv()传递了一个指向缓冲区开始的指针。

是否有一个特定的原因来定义一个缓冲区一遍又一遍的循环内?或者,我对此的基本理解是正确的?

回答

8

recv,作为read和其他类似的功能,不关心缓冲区的以前的内容,它只是用它来写结果。

不是说它会有所作为:因为您没有初始化缓冲区,即使您将该变量声明为循环的本地变量,其内容也将是“未定义的”。

此外,在大多数C实现:

  • 没有初始化,这将采取一切正好是堆在那个位置,这反过来又意味着将采取相同的位置上可变机构,它在前面的迭代中,有效地给你与循环外的变量完全相同的结果。
  • 堆栈分配很便宜 - 通常他们只需要调整一个寄存器;
  • 实际上,它们甚至更便宜:通常寄存器调整仅在函数开始时完成,占所有局部变量;局部变量的范围变成只是一个编译时构造,因为它在函数启动时被分配。

显然,相反,如果你初始化您的变量将是不同的 - 执行初始化代码将不得不在每次迭代运行;但是,如上所述,不需要初始化任何东西,recv只是不关心缓冲区的当前状态。

+0

+1,比我的回答好得多。 – Martin

+0

迄今为止所有非常有用(和类似)的答案。任何人都可以指出我的来源?或者我可以在我的编译器文档中找到所有这些吗?编译器优化在我读过的C编程书中被掩盖了。 – Nocturno

+2

我的源代码花费太多时间看编译器生成的汇编程序:)。一般来说,不要担心这种优化。为正确的工作使用正确的算法,并且只有考虑到这些类型的调整,才能演示一段代码*需要*优化。 – Martin

2

在循环中声明一个变量只是为它保留了堆栈空间;它不会清除内容或以其他方式触摸变量。因此,这种声明的风格并不比在循环之外声明更昂贵。

5

这不是浪费。它声明了这个变量的范围。编译器可能为了其他目的回收堆栈上的空间,而不是从堆栈中分配更多的空间。它在运行时不需要额外的开销 - 编译器在编译时计算必要的堆栈空间,并在函数的开头只调整一次堆栈指针。

2

定义在一个循环的变量只有当它的价格昂贵,构建坏,这是很少C.​​

随着连最基本的优化,它甚至没有去打扰修改堆栈指针的情况下,循环的每次迭代。许多编译器会在调试模式下初始化一个数组,但除非这个缓冲区很大,否则这不太可能是一个大问题。在C++中,你可能会考虑不用一个昂贵的构造函数来声明一个变量,如果你只能在循环之外构造它,但这不会成为一个问题。

1

我一直觉得我不应该在循环中定义一个 变量,因为它不必要或浪费。

你一直是一个不正确的印象,以及一个不仅是毫无根据的,但抽得一个非常不好的做法 - 过早的优化 - 对一个非常好的一个,声明变量接近它们作为可能。

1

我也曾经认为将声明移出循环会导致代码更快,特别是对于像数组这样的大型结构。我认为malloc'd(堆)数据通常是这样,因为通过在循环中调用malloc和free可以浪费很多开销。对于堆栈数据(比如你的)我不认为这是一个大问题。

但是,我最近遇到了相反的情况,在那里我把一个声明从一个内部循环中移出来,并且实际上以代替了较慢的代码。我想出了这几个可能的解释:

  1. 在报关的时候被转移到更广的范围,编译器无法作为有效的优化代码。
  2. 在循环迭代之间内存中存储了大量数据,导致缓存使用效率低下。

无论如何,我没有一个很好的参考,但将定义移入或移出循环可以使代码更快或更慢,具体取决于情况。您必须在更改代码前后测量性能,以查看是否有差异。

+0

'我也曾经认为将声明移出循环会导致代码更快,特别是对于像数组这样的大型结构。 - *为什么*你认为? '我认为malloc'd(堆)数据通常是这样的 - 分配不是声明!如果你有像'int foo = very_long_calculation(rand())'这样的语句,那么在循环中移动*计算*当然是很昂贵的。对malloc的调用有什么不同? –