C while循环中的变量范围

C while循环中的变量范围,c,variables,scope,C,Variables,Scope,这也许是我遇到过的最奇怪的事情之一。我不会用C编写太多程序,但据我所知,加上在线检查不同的来源,变量macroName和macroBody仅在while循环的范围内定义。所以每次循环运行时,我都希望marcoName和macroBody获得新的地址,成为全新的变量。但事实并非如此 我发现,即使循环再次运行,两个变量共享相同的地址,这让我非常头疼,因为我需要检查元素的唯一性。我不知道这是为什么。每次while循环运行时,宏名和宏体不应该获得全新的地址吗 我知道这是个问题,因为我正在打印地址,它们是

这也许是我遇到过的最奇怪的事情之一。我不会用C编写太多程序,但据我所知,加上在线检查不同的来源,变量macroNamemacroBody仅在while循环的范围内定义。所以每次循环运行时,我都希望marcoNamemacroBody获得新的地址,成为全新的变量。但事实并非如此

我发现,即使循环再次运行,两个变量共享相同的地址,这让我非常头疼,因为我需要检查元素的唯一性。我不知道这是为什么。每次while循环运行时,宏名宏体不应该获得全新的地址吗

我知道这是个问题,因为我正在打印地址,它们是一样的

while(fgets(line, sizeof(line), fp) != NULL) // Get new line
{
    char macroName[MAXLINE];
    char macroBody[MAXLINE];

    // ... more code

    switch (command_type)
    {
        case hake_macro_definition:
            // ... more code

            printf("**********%p | %p\n", &macroName, &macroBody);
            break;

        // .... more cases
    }
}
作为链接列表代码一部分的代码

struct macro {
    struct macro *next;
    struct macro *previous;
    char *name;
    char *body;
};    
用于检查元素是否已存在于链表中的函数。但是由于*name有相同的地址,我总是在if条件中结束

static struct macro *macro_lookup(char *name)
{
    struct macro *temp = macro_list_head;

    while (temp != NULL)
    {
        if (are_strings_equal(name, temp->name))
        {
            break;
        }    

        temp = temp->next;
    }

    return temp;
}

这些阵列在堆栈上分配:

char macroName[MAXLINE];
char macroBody[MAXLINE];
编译器为您预先分配了函数开头的空间。换句话说,从计算机的角度来看,这些数组的位置将与您在函数体顶部的循环体外部定义它们的位置相同

C中的作用域仅指示标识符可见的位置。因此编译器(而不是计算机)强制执行
macroName
macroBody
不能在循环体之前或之后引用的语义。但从计算机的角度来看,一旦函数启动,这些数组的实际数据就存在,只有在函数结束时才会消失


如果您查看代码的程序集转储,您可能会看到计算机的帧指针减少了足够大的量,以便函数为所有局部变量留出空间,包括这些数组。

除了chrisaycock的回答之外,我还需要提到的是:在定义这些变量的函数之外,永远不要使用指向局部变量的指针。考虑这个例子:

int * f()
{
   int local_var = 0;
   return &local_var;
}
int g(int x)
{
   return (x > 0) ? x : 0;
}
int main()
{
   int * from_f = f(); //
   *from_f = 100; //Undefined behavior
   g(15); //some function call to change stack
   printf("%d", *from_f); //Will print some random value
   return 0;
}
实际上,这同样适用于块。从技术上讲,块局部变量可以在块结束后清除。因此,在循环的每次迭代中,旧地址都可能无效。这不会是真的,因为C编译器确实出于性能原因将这些变量放在同一个地址,但您不能依赖它


您需要了解的是内存是如何分配的。如果你想实现一个列表,它是一个不断增长的结构。记忆是从哪里来的?不能从堆栈中分配太多内存,而且一旦从函数返回,内存就会失效。因此,您需要从堆中分配它(使用
malloc
)。

如果您希望为这两个变量都获得一个唯一的地址,为什么不为循环的每次运行定义一个指针、分配和释放内存呢。另外,你在问题中的评论很有趣。您提到,您根据地址的唯一性做出决定,在本例中,地址是堆栈变量/内存指针。是的,我可能会这样做。非常感谢。所以我只在函数结束时得到“新”变量,对吗?@thepederrian类似的东西。您还可以通过动态内存分配获得“新”空间,即通过
malloc()
。顺便说一句,如果你有兴趣学习更多关于这些东西是如何工作的,这整个研究领域被称为计算机组织。我现在实际上参加了一个计算机组织课程。你解释事情的方式很有道理。我只是从来没见过这种情况。我假设使用了“新的”内存/寄存器,但这解释了为什么变量只存在一次。@ThePederstrian啊,太棒了!我年轻时在汇编中手动编码是我做过的最有教育意义的事情之一。喜欢。我自己也很喜欢集会。不幸的是,我们没有花太多时间。我们了解了基础,然后继续前进。但集会很有趣。真正让你欣赏更高级的语言。顺便说一句,非常感谢!谢谢你提到这件事。我遇到了另一个问题,它是由您描述的场景引起的。谢谢