C中的作用域只与编译时有关,因为我们知道我们可以在运行时访问任何内存吗?

我试图理解C中范围的确切含义。我能理解的是范围仅限于编译时间。 例如,如果您从其他某个函数访问局部变量。 这将导致编译时错误。 另一方面,以下程序工作正常。 这意味着C具有平坦的内存模型,并且可以在运行时访问任何内容。 C书将范围与生命周期和可变可见性联系起来,我发现它很混乱。 我认为所有这些术语只对编译时有意义。 有人可以点亮吗?

#include "stdio.h" int *ptr; int func(void) { /** abc is a local variable **/ int abc = 132; ptr = &abc; return 0; } int func1(void) { /** although scope of abc is over still I can change the value in the address of abc **/ *ptr = 200; printf("the value of abc=%d\r\n",*ptr); } int main(void) { func(); func1(); return 0; } 

结果: abc = 200的值

用简单的话来说,范围是什么意思? 它是在运行时还是编译时出现的? 我们可以看到,我们可以在运行时访问任何内容。 但是,如果我们不遵守规则,那么我们将得到编译错误。 例如,另一个函数中的局部变量引用。 编译器将抛出一个错误,说“变量未定义……”。

我可以对变量说以下内容吗?

 1) Scope attribute comes under compile time. 2) Lifetime attribute comes under run-time. 3) Visibility attribute comes under compile-time 

是的,C的内存模型允许您轻松访问任何内容,因此您可以实际执行上述操作并查看“有趣”的结果。

但是,您在此处执行的操作由C标准指定为未定义行为 (UB)。 这意味着任何事情都可能发生; 这可能是你所期望的,也可能不是。

请注意,您没有访问“局部变量”,因为当您访问func已经返回时,因此其局部变量的生命周期已过期。 你访问的是一个“刚刚发生”的内存区域,它有一个有趣的值。 如果从func内部调用func1 ,那么行为将被很好地定义。

还有一些说明:

范围绝对是一个仅编译时的概念; 名称的范围(变量,标识符等)是程序代码的子集,其中该名称由编译器识别。

这与变量的生命周期非常不同,变量的寿命与一般情况下的范围无关,并且将两者混为一谈是常见的错误。 局部变量的生命周期和范围确实是交织在一起的,但事实并非如此。

虽然理论上它只是“UB”,但在实践中却要求实际上失败。 abc的位置(在我知道的每个实现中)都在堆栈的某个位置。 由于您已离开初始块然后进入其他块,因此很可能其他内容将占用该内存位置。 你要覆盖哪个。

范围是什么意思?

变量的范围是可以引用变量的文本部分。 局部变量具有块范围 :从声明点到封闭函数体的末尾可以看到它。
它与变量的生命周期无关 。 它是存储持续时间,它告诉变量的生命周期。

它是在运行时还是编译时出现的?

它在编译时和链接时间进入图片。 当程序尝试访问其块之外的局部变量时,编译器将给出关于该未声明变量(其块的本地变量)的错误。
这个例子将更好地解释它:

 #include  void userlocal(void); int main() { int a= 2; printf("local a in outer scope of main is %d\n",a); userlocal(); printf("local a in scope of userlocal is %d\n",b); // This will give error at compile time return 0; } void userlocal(void) { int b = 20; printf("local a in scope of userlocal is %d\n",b); } 

输出:

 [Error] 'b' undeclared (first use in this function) 

我可以对变量说以下内容吗?

是的,你可以说。

就范围而言,最简单的想法是将C编译成一系列寄存器和内存操作,诸如块,for循环,if语句,结构等结构在编译之外没有任何意义,它们只是允许您作为程序员保持理智的抽象。

就这个例子和记忆而言,我试图解释它

正如每个人都在说你正在使用某些编译器特定的实现,通过标准的,未定义的动作。 要理解它是如何工作的,你可以把你正在编写的程序想象成有两个存储器,即堆和堆栈。 例如char *foo = malloc(50); 在堆上分配内存, char foo[] = "Foo"在堆栈上分配它。 堆栈是记住你正在做什么的内存,它包含一长串堆栈帧,每个函数调用都会添加一个帧,每个返回都弹出一个帧。 堆是另一种内存。

为了说明这一点,我们有这个程序:

 int *ptr; int func() { int abc = 123; ptr = &abc; return 0; } int func1() { int def; printf("func1() :: *abc=%i\n", *ptr); def = 200; return 0; } int main() { func(); printf("main() :: *ptr=%i\n", *ptr); func1(); printf("main() :: *ptr=%i\n", *ptr); } 

以下内容将在堆栈中发生( -未定义/未使用的内存, >左侧是程序当前正在执行的位置, X是数据):

 |-----| |-----| |-----| 

当我们进入main()它会将堆栈帧推送到堆栈:

  |-----| |-----| >|XXXXX| <- This is where all memory needed to execute main() is. 

然后调用func() ,它在需要执行的内存中包含整数123

  |-----| >|XX123| <- This is the stack frame for func() |XXXXX| <- Still the stack frame for main() 

func()将全局指针*ptr设置为堆栈上整数的地址。 这意味着当func()返回(并且内存未被清除,因为那将浪费CPU周期)时,该值仍然存在

  |-----| |--123| >|XXXXX| <- main() 

在调用下一个函数之前,仍然可以通过*ptr引用

  |-----| >|XXXXX| <- This is the stack frame for func1() |XXXXX| 

现在*ptr似乎有一些随机值...但您仍然可以访问内存位置并进行更改。 (如果func()func1()在它们的作用域中只定义了一个局部整数,很可能*ptr也指向func1()中的整数)

奖金

我没有测试过该程序,但我认为它会打印出这样的东西:

 main() :: *ptr=123 func1() :: *ptr= main() :: *ptr= 

你是对的,C有一个平坦的记忆模型。 并允许访问任何内存区域。 这里ptr是一个指向int的全局指针。 所以它指向一个可以存储和检索整数的地址。 这将导致在不同类型的场景中出现undefined behaviour

Scope是一个编译时属性,它处理引用变量有效或何时可见,这与对象的storage duration或生命周期不同,后者告诉它何时有效访问与变量关联的内存。

在这种情况下, abc具有自动存储持续时间,这意味着它的持续时间延长了它所在的块,在这种情况下是函数func并且尝试访问与该块之外的abc相关联的存储器是未定义的行为 ,它可能看起来像工作,但结果不能依赖。

从草案C99标准部分6.2.4 对象的存储持续时间段落谈到各种存储持续时间: staticautomatic 。 在解释static变量生命周期是程序的整个执行时,在第5段中它说:

对于没有可变长度数组类型的此类对象,其生命周期从entry进入与其关联的块,直到该块的执行以任何方式结束。 [..]

因此,自动变量生命是它所包含的块, 第2段说:

对象的生命周期是程序执行的一部分,在此期间保证为其保留存储。 存在一个对象,具有一个常量地址,25)并在其整个生命周期内保留其最后存储的值.26) 如果一个对象在其生命周期之外被引用,则该行为是未定义的。 当指针指向的对象到达其生命周期的末尾时,指针的值变得不确定。

因此,引用它之外的对象的保证寿命是未定义的行为。

这是未定义的,你不应该相信价值。 只需对代码进行微小更改(MSVC 2013)就会出现意外情况。

 int func1() { /** although scope of abc is over still I can change the value in the address of abc **/ printf("the value of abc=%d\r\n", *ptr); *ptr = 200; printf("the value of abc=%d\r\n", *ptr); return 0; } int main() { func(); printf("the value of abc=%d\r\n", *ptr); func1(); } 

输出在我的电脑上。

abc = 132的值
值abc = 1519668
abc = 200的值