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() 传递了一个指向缓冲区开头的指针。

是否有特定的原因在循环内一遍又一遍地定义缓冲区?还是我对此的基本理解正确?

4

6 回答 6

8

recv, asread和其他类似的函数,不关心缓冲区之前的内容,它只是使用它来写入结果。

并不是说无论如何都会有所作为:由于您没有初始化缓冲区,因此即使您将变量声明为循环的本地变量,它的内容也将是“未定义的”。

此外,在大多数 C 实现中:

  • 不初始化该变量意味着它将采用该位置堆栈上发生的任何内容,这反过来意味着它将采用与前一次迭代时相同的位置,从而有效地为您提供与具有完全相同的结果循环外的变量。
  • 堆栈分配很便宜——通常它们只需要调整一个寄存器;
  • 实际上,它们甚至更便宜:通常寄存器调整只是在函数的开头完成,占所有局部变量;局部变量的作用域只是一个编译时构造,因为它是在函数启动时分配的。

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

于 2012-09-23T01:04:37.593 回答
5

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

于 2012-09-23T01:02:09.340 回答
2

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

于 2012-09-23T01:02:04.923 回答
2

只有在构造成本高昂的情况下,才在循环中定义变量是不好的,而在 C 中很少出现这种情况。

即使是最基本的优化,它甚至都不会在循环的每次迭代中修改堆栈指针。许多编译器会在调试模式下对数组进行零初始化,但除非这个缓冲区很大,否则这不太可能是一个大问题。

在 C++ 中,您可能会考虑不使用昂贵的构造函数声明变量,如果您可以只在循环外构造一次就可以逃脱,但这在这里不会成为问题。

于 2012-09-23T01:04:48.513 回答
1

我一直认为我不应该在循环内定义变量,因为它是不必要的或浪费的。

你总是有一个不正确的印象,而且这种印象不仅毫无根据,而且将一种非常糟糕的做法——过早优化——与一个非常好的做法相提并论,声明变量尽可能接近它们的用途。

于 2012-09-23T02:02:39.097 回答
1

我还曾经认为,将声明移出循环会导致代码更快,尤其是对于更大的结构,例如数组。我认为这对于 malloc 的(堆)数据通常是正确的,因为在循环中调用 malloc 和 free 会浪费很多开销。对于堆栈数据(如您的),我认为这不是什么大问题。

但是,我最近遇到了相反的情况,我将声明移出内部循环,实际上最终得到了较慢的代码。我对此提出了一些可能的解释:

  1. 当声明被移动到更广泛的范围时,编译器无法有效地优化代码。
  2. 在循环迭代之间,大量数据被保存在内存中,导致缓存使用效率低下。

Anyway I don't have a good reference for this, but moving definitions in or out of loops can make the code faster or slower depending on the situation. You have to measure the performance before and after changing the code to see if there is a difference.

于 2012-09-23T02:53:25.667 回答