C中的变量重用

avg*_*tvs 8 c

我正在看的代码是这样的:

for (i = 0; i < linesToFree; ++i ){
    printf("Parsing line[%d]\n", i);

    memset( &line, 0x00, 65 );
    strcpy( line, lines[i] );

    //get Number of words:
    int numWords = 0;

    tok = strtok(line , " \t");
    while (tok != NULL) {
        ++numWords;
        printf("Number of words is:  %d\n", numWords);
        println(tok);

        tok = strtok(NULL, " \t");
    }
}
Run Code Online (Sandbox Code Playgroud)

我的问题围绕着使用numWords.运行时系统是否重用此变量,或者int每次运行for循环时是否分配新变量?如果你想知道我为什么这么问,我是一个Java程序员,他想进入HPC,因此我想学习C.通常我知道你想避免像这样的代码,所以这个问题确实是探索性的.

我知道答案可能依赖于编译器......我正在寻找一个更深层次的解释.假设您选择的编译器.

Gre*_*ill 5

你在Java中如何工作的概念可能会被误导 - Java不会int每次都通过类似的循环"分配"一个新的.类似的原始类型变量int未在Java堆上分配,并且编译器将为每个循环迭代重用相同的本地存储.

另一方面,如果你new每次通过循环调用Java中的任何东西,那么是的,每次都会分配一个新对象.但是,在这种情况下,你不是那样做的.除非你调用malloc或类似(或在C++中),否则C也不会从堆中分配任何东西new.


the*_*olf 2

这就是 C 中所谓的“块”、“自动”或“本地”作用域。它是词法作用域的一种形式,即名称引用其本地环境。在 C 中,它是自上而下的,这意味着它在文件被解析和编译时发生,并且只有在程序中定义之后才可见。当变量超出范围时,词法名称不再有效(可见),并且可以重用内存。

该变量在局部作用域或由大括号定义的块中声明{ /* block */ }。这定义了一整组 C 和 C99 习惯用法,例如:

for(int i=0; i<10; ++i){    // C99 only. int i is local to the loop
    // do something with i 
}  // i goes out of scope here...
Run Code Online (Sandbox Code Playgroud)

其中有一些微妙之处,例如:

 int x = 5;
 int y = x + 10; // this works

 int x = y + 10;
 int y = 5;      // compiler error     
Run Code Online (Sandbox Code Playgroud)

和:

int g;        // static by default and init to 0
extern int x; // defined and allocated elsewhere - resolved by the linker 
int main (int argc, const char * argv[])
{
    int j=0;  // automatic by default  
    while (++j<=2) {
        int i=1,j=22,k=3;    // j from outer scope is lexically redefined
        for (int i=0; i<10; i++){
            int j=i+10,k=0;
            k++;             // k will always be 1 when printed below
            printf("INNER: i=%i, j=%i, k=%i\n",i,j,k);
        }
        printf("MIDDLE: i=%i, j=%i, k=%i\n",i,j,k);   // prints middle j
    }

    // printf("i=%i, j=%i, k=%i\n",i,j,k); compiler error
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

有一些特质:

  1. 在 K&R C、ANSI C89 和 Visual Studio 中,所有变量必须在函数或复合语句的开头声明(即在第一个语句之前)
  2. 在 gcc 中,变量可以在函数或复合语句中的任何位置声明,并且仅从该点开始可见。
  3. 在 C99 和 C++ 中,循环变量可以在for语句中声明,并且在循环体结束之前都是可见的。
  4. 在循环块中,分配执行一次,并且每次执行 RH 分配(如果有)。

在您发布的特定示例中,您询问int numWords = 0;是否int每次通过循环都分配一个新的。=不,循环块中只分配了一个 int,但每次都会执行其右侧。这可以这样证明:

#include <stdio.h>
#include <time.h>
#include <unistd.h>

volatile time_t ti(void){
    return time(NULL);
}

void t1(void){
    time_t t1;
    for(int i=0; i<=10; i++){
        time_t t2=ti();    // The allocation once, the assignment every time
        sleep(1);
        printf("t1=%ld:%p t2=%ld:%p\n",t1,(void *)&t1,t2,(void *)&t2);
    }
}
Run Code Online (Sandbox Code Playgroud)

使用任何与gcc(clang、eclipse 等)兼容的编译器进行编译,并关闭 ( -O0) 或打开优化。t2 的地址始终相同。

现在与递归函数进行比较:

int factorial(int n) {
    if(n <= 1)
        return 1;
    printf("n=%i:%p\n",n,(void *)&n);
    return n * factorial(n - 1);
}
Run Code Online (Sandbox Code Playgroud)

每次的地址n都会不同,因为每次递归调用都会分配一个新的自动n。

与强制使用循环块分配的阶乘迭代版本进行比较:

int fac2(int num) {
    int r=0;   // needed because 'result' goes out of scope
    for (unsigned int i=1; i<=num; i++) {
        int result=result*i;   // only RH is executed after the first time through
        r=result;
        printf("result=%i:%p\n",result,(void *)&result);  // address is always the same
    }
    return r;
}
Run Code Online (Sandbox Code Playgroud)

总之,您询问了int numWords = 0;for 循环内部。本例中重复使用了该变量。

编写代码的方式是,程序员依靠int numWords = 0;第一个执行后的 RH 并将变量重置为 0,以便在随后的 while 循环中使用。

  • 您的“fac2”示例是 C99,仅应注意 C1X。在直接 gcc 中,`result` 的值没有初始化为 0! (2认同)