当我们声明一个变量时,它被分配了一个内存位置。这个位置可能已经有一个值,如果我们不初始化变量,它将指向已经存在的值,这被称为垃圾值。所以即使我们自己没有给变量设置值,它仍然会指向一个值。但是这总是正确的吗?如果在内存位置没有值并且我们没有初始化变量会怎么样?当我试图用变量做一些事情时,例如算术,我会得到什么类型的错误?
siotufzp1#
C标准并不规定程序在物理计算机中的行为,它规定了程序在抽象模型中的行为。在抽象模型中,未初始化的对象没有固定的值。在这个模型中,一个未初始化的对象被称为具有一个 * 不确定的值 。(C 2018 6.7.9 10:“如果一个具有自动存储持续时间的对象没有显式初始化,它的值是不确定的...”)不确定的值是“未指定的值或陷阱表示”(3.19.2)。根据3.19.3, 未指定的值 * 是:相关类型的有效值,其中本文档未对在任何示例中选择的值提出要求也就是说,未初始化对象的“值”在使用它的每个示例中都是不指定的,如果连续打印三次,打印结果可能是“3”、“4”、“-11729301”。在一台普通的通用计算机的内存硬件中,在操作系统启动并为用户进程提供内存之后,任何内存位置都将保存一些特定的值。但是C程序的行为并没有被指定为将未初始化对象的行为与任何特定的内存位置联系起来。因此,尽管任何内存位置都可能有一些固定的值,C标准的抽象模型中的对象可能不会。我将在this answer、这个、这个和这个中进一步讨论这个。在www.example.com 2中还有一条额外的规则6.3.2.1:...如果左值指定了一个可以用寄存器存储类声明的自动存储持续时间的对象(从未获取其地址),并且该对象未初始化(未用初始化器声明,并且在使用之前没有对其进行赋值),则行为未定义。在这种情况下,程序的整个行为都是未定义的,而不仅仅是对象的值。
moiiocjp2#
任何RAM单元最接近于“无值”的情况就是从来没有被写入过。这在任何一台计算机上都是不可能的,它可以执行你创建的任何程序,但也不是不可能的,所以让我们假设它是这样的。然后再深入一点(仍然非常简化),最相关的RAM结构是动态内存和静态内存(不要与静态链接和动态内存分配混淆)。动态存储器或多或少是一种容量,其需要通过再次写入所存储的值来定期刷新,这是由存储器管理单元完成的(MMU)自动地。这些MMU在后台完成它们的工作。实际上这总是推翻了没有值被写入的假设。但是让我们假设一个MMU以某种方式只对从未有过有意写入的值的单元起作用(几乎保证永远不会被创建,因为它复杂,昂贵,没有任何“节省”价值)。静态RAM是一种小电路,具有两种自稳定状态的属性,通常是高电压或低电压。任何介于两者之间的值都会被快速驱动到定义值之一。但我们不要考虑“故意写入”。本质上,这使得它们难以预测。一些静态RAM类型具有1或0的趋势,但编译器从不假设这一点,也不应该依赖它们。因此,虽然“随机”是错误的表达(特别是如果你的目标是实际的随机性),但它是一个合适的模型。然而,这两种RAM都将在某个时刻被有意地读取,可能是由您的程序读取,因为这就是您询问的原因。阅读的物理行为最迟涉及到每个细胞在0和1之间的决定。阅读的目的是得到一个明确的1或0。结果可能是“随机的”,但它是一个明确的1或0。例如,为了形成一个字节,几个读取单元格的总数将有点不可预测,但它将是0和1的集合。所有可能的结果都在数据类型范围定义内。或者是一个特殊值(例如浮点数或整数的一些实现),它不被解释为值,但它不是可以使用的指定的“无值”。即使看三进制逻辑,与二进制相反,即可以具有三个状态的存储单元(我怀疑在任何现有的显着存储器大小的计算机中使用......),第三状态肯定不会被用来表示“不能使用”。简而言之,通过一些非常冒险的假设,变量的内存位置在物理上可以处于某种特殊的“从未写入”状态;但是在阅读时,结果不能从Map(避免这里的“写”字)到那里的数据类型的值(甚至包括特殊值)中辨别出来。如果你想跟踪内存位置或从未被写入的变量,你需要用定义的“未写入/写入”值来做到这一点,可能是引用它们的OTHER变量中的布尔值。为了使它们可靠,您仍然需要干净地初始化它们。Map到你的问题(我接受他们作为措辞一个单一的重点问题):
uubf1zoe3#
当一个具有自动存储持续时间的变量被声明并接受一个内存地址时,该内存地址中将有一些东西。
总是,也许,太确定了,但我会说是的,几乎,鉴于上述情况。
在初始化之前,阅读或修改该变量表示的对象中的值将相当于未定义的行为,如C国际标准中定义的:
1.* 在使用不可移植的或错误的程序结构或错误的数据时,未定义的行为,对此,本国际标准没有提出要求。*1.* 注:可能的未定义行为包括完全忽略情况并产生不可预测的结果,在翻译或程序执行过程中以环境特有的记录方式行为(有或没有发出诊断消息),终止翻译或执行(发出诊断消息)。1.* 示例未定义行为的一个示例是整数溢出的行为。*正如定义所述,对此类程序的行为没有要求,所以任何事情都可能发生,现代编译器,如果你尝试这样做,正确的标志,会警告你,甚至发出错误。
zxlwwiss4#
当我们声明一个变量时,它被分配了一个内存位置。这个位置可能已经有一个值,如果我们不初始化变量,它将指向已经存在的值,这被称为垃圾值。它是否有内存位置取决于编译器。它可能只把它保存在寄存器中,也可能完全优化它。只有当你使用对它的引用(或声明为volatile),它才会有内存位置。
volatile
当我试图用变量做一些事情时,例如算术,我会得到什么类型的错误?你不会得到任何错误。如果你使用的不是static storage duration对象,没有预先初始化或赋值,你的程序会调用undefined behavior(UB),它的行为将是 undefined。
int x; // static storage duration - zeroed static int x1; // static storage duration - zeroed void foo(void) { int y; //automatic storage duration - undefined static int z; // static storage duration - zeroed int *p1 = malloc(sizeof(*p1)); // p1 - automatic storage duration - initialized. Referenced object value undefined int *p2 = calloc(sizeof(*p2), 1); // p1 - automatic storage duration - initialized. Referenced object zeroed /* .... */
4条答案
按热度按时间siotufzp1#
C标准并不规定程序在物理计算机中的行为,它规定了程序在抽象模型中的行为。在抽象模型中,未初始化的对象没有固定的值。
在这个模型中,一个未初始化的对象被称为具有一个 * 不确定的值 。(C 2018 6.7.9 10:“如果一个具有自动存储持续时间的对象没有显式初始化,它的值是不确定的...”)不确定的值是“未指定的值或陷阱表示”(3.19.2)。根据3.19.3, 未指定的值 * 是:
相关类型的有效值,其中本文档未对在任何示例中选择的值提出要求
也就是说,未初始化对象的“值”在使用它的每个示例中都是不指定的,如果连续打印三次,打印结果可能是“3”、“4”、“-11729301”。
在一台普通的通用计算机的内存硬件中,在操作系统启动并为用户进程提供内存之后,任何内存位置都将保存一些特定的值。但是C程序的行为并没有被指定为将未初始化对象的行为与任何特定的内存位置联系起来。因此,尽管任何内存位置都可能有一些固定的值,C标准的抽象模型中的对象可能不会。
我将在this answer、这个、这个和这个中进一步讨论这个。
在www.example.com 2中还有一条额外的规则6.3.2.1:
...如果左值指定了一个可以用寄存器存储类声明的自动存储持续时间的对象(从未获取其地址),并且该对象未初始化(未用初始化器声明,并且在使用之前没有对其进行赋值),则行为未定义。
在这种情况下,程序的整个行为都是未定义的,而不仅仅是对象的值。
moiiocjp2#
任何RAM单元最接近于“无值”的情况就是从来没有被写入过。这在任何一台计算机上都是不可能的,它可以执行你创建的任何程序,但也不是不可能的,所以让我们假设它是这样的。
然后再深入一点(仍然非常简化),最相关的RAM结构是动态内存和静态内存(不要与静态链接和动态内存分配混淆)。
动态存储器或多或少是一种容量,其需要通过再次写入所存储的值来定期刷新,这是由存储器管理单元完成的(MMU)自动地。这些MMU在后台完成它们的工作。实际上这总是推翻了没有值被写入的假设。但是让我们假设一个MMU以某种方式只对从未有过有意写入的值的单元起作用(几乎保证永远不会被创建,因为它复杂,昂贵,没有任何“节省”价值)。
静态RAM是一种小电路,具有两种自稳定状态的属性,通常是高电压或低电压。任何介于两者之间的值都会被快速驱动到定义值之一。但我们不要考虑“故意写入”。本质上,这使得它们难以预测。一些静态RAM类型具有1或0的趋势,但编译器从不假设这一点,也不应该依赖它们。因此,虽然“随机”是错误的表达(特别是如果你的目标是实际的随机性),但它是一个合适的模型。
然而,这两种RAM都将在某个时刻被有意地读取,可能是由您的程序读取,因为这就是您询问的原因。
阅读的物理行为最迟涉及到每个细胞在0和1之间的决定。阅读的目的是得到一个明确的1或0。结果可能是“随机的”,但它是一个明确的1或0。
例如,为了形成一个字节,几个读取单元格的总数将有点不可预测,但它将是0和1的集合。所有可能的结果都在数据类型范围定义内。或者是一个特殊值(例如浮点数或整数的一些实现),它不被解释为值,但它不是可以使用的指定的“无值”。
即使看三进制逻辑,与二进制相反,即可以具有三个状态的存储单元(我怀疑在任何现有的显着存储器大小的计算机中使用......),第三状态肯定不会被用来表示“不能使用”。
简而言之,通过一些非常冒险的假设,变量的内存位置在物理上可以处于某种特殊的“从未写入”状态;但是在阅读时,结果不能从Map(避免这里的“写”字)到那里的数据类型的值(甚至包括特殊值)中辨别出来。
如果你想跟踪内存位置或从未被写入的变量,你需要用定义的“未写入/写入”值来做到这一点,可能是引用它们的OTHER变量中的布尔值。
为了使它们可靠,您仍然需要干净地初始化它们。
Map到你的问题(我接受他们作为措辞一个单一的重点问题):
uubf1zoe3#
当一个具有自动存储持续时间的变量被声明并接受一个内存地址时,该内存地址中将有一些东西。
总是,也许,太确定了,但我会说是的,几乎,鉴于上述情况。
在初始化之前,阅读或修改该变量表示的对象中的值将相当于未定义的行为,如C国际标准中定义的:
§3.4.3(C11 N1570)
1.* 在使用不可移植的或错误的程序结构或错误的数据时,未定义的行为,对此,本国际标准没有提出要求。*
1.* 注:可能的未定义行为包括完全忽略情况并产生不可预测的结果,在翻译或程序执行过程中以环境特有的记录方式行为(有或没有发出诊断消息),终止翻译或执行(发出诊断消息)。
1.* 示例未定义行为的一个示例是整数溢出的行为。*
正如定义所述,对此类程序的行为没有要求,所以任何事情都可能发生,现代编译器,如果你尝试这样做,正确的标志,会警告你,甚至发出错误。
zxlwwiss4#
当我们声明一个变量时,它被分配了一个内存位置。这个位置可能已经有一个值,如果我们不初始化变量,它将指向已经存在的值,这被称为垃圾值。
它是否有内存位置取决于编译器。它可能只把它保存在寄存器中,也可能完全优化它。只有当你使用对它的引用(或声明为
volatile
),它才会有内存位置。当我试图用变量做一些事情时,例如算术,我会得到什么类型的错误?
你不会得到任何错误。如果你使用的不是static storage duration对象,没有预先初始化或赋值,你的程序会调用undefined behavior(UB),它的行为将是 undefined。