C中的变量重用

时间:2011-10-08 20:27:07

标签: 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");
    }
}

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

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

6 个答案:

答案 0 :(得分:5)

关于Java如何工作的概念可能会被误导 - Java每次都不会像这样循环“分配”新的int。像int这样的原始类型变量没有在Java堆上分配,编译器将为每个循环迭代重用相同的本地存储。

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

答案 1 :(得分:4)

请注意automaticdynamic内存分配之间的差异。在Java中只存在后者。

这是自动分配:

int numWords = 0;

这是动态分配:

int *pNumWords = malloc(sizeof(int));
*pNumWords = 0;

C中的动态分配仅显式发生(当您调用malloc或其衍生物时)。

在您的代码中,只有值设置为您的变量,不会分配新的值。

答案 2 :(得分:3)

从表现的角度来看,这并不重要。 (变量映射到寄存器或存储器位置,因此必须重复使用。)

从逻辑的角度来看,是的,它会被重用,因为你在循环之外声明了它。

从逻辑的角度来看:

  • numWords 不会外部循环中重复使用,因为它是在其中声明的。
  • numWords 将在内部循环中重复使用,因为它未在内部声明。

答案 3 :(得分:2)

这就是C中所谓的“阻止”,“自动”或“本地”范围。它是lexical scoping的一种形式,即名称是指其本地环境。在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...

有一些细微之处,例如:

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

 int x = y + 10;
 int y = 5;      // compiler error     

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;
}

有特质:

  1. 在K&amp; R C,ANSI C89和Visual Studio中,所有变量必须在函数或复合语句的开头声明(即在第一个语句之前)
  2. 在gcc中,变量可以在函数或复合语句中的任何位置声明,并且只能从该点开始显示。
  3. 在C99和C ++中,循环变量可以在 for 语句中声明,并且在循环体结束之前可见。
  4. 在循环块中,分配在ONCE执行,每次执行RH分配(如果有)。
  5. 在您发布的特定示例中,您询问了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);
        }
    }
    

    使用任何 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);
    }
    

    每次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;
    }
    

    总之,您在for循环中询问了int numWords = 0;在此示例中重用该变量。

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

答案 4 :(得分:0)

numWords变量的范围在for循环中。就像Java一样,你只能在循环中使用变量,所以理论上它的内存必须在退出时被释放 - 因为它在你的情况下也在堆栈中。

然而,任何好的编译器都会使用相同的内存,只需在每次迭代时将变量重新设置为0。

如果您使用的是class而不是int,则每次for循环时都会看到析构函数被调用。

即使考虑到这一点:

class A;

A* pA = new A;
delete pA;
pA = new A;

这里创建的两个对象可能位于同一个内存中。

答案 5 :(得分:-3)

每次循环都会分配它(编译器可以优化分配)

for (i = 0; i < 100; i++) {
    int n = 0;
    printf("%d : %p\n", i, (void*)&n);
}

不保证所有100行都具有相同的地址(尽管可能会有)。

编辑:{4.2} / 5中的C99 Standard说:"[the object] lifetime extends from entry into the block with which it is associated until execution of that block ends in any way.",并且在6.8.5 / 5中,它表示for的正文1}}声明实际上是一个块...所以第6.2.4 / 5段适用。