在 C 中,它曾经 O.K。增加一个未初始化的整数?
In C, is it ever O.K. to increment an uninitialized int?
根据 this SO answer,在 C 中递增未初始化的 int
会导致未定义的行为。但是,如果我根本不关心初始值怎么办?例如,如果我只想要一个递增的 id 怎么办?这是危险的还是不好的做法?
如果访问具有自动存储持续时间的对象的值,并且该对象未初始化,则行为未定义。这在 N1570 6.3.2.1 第 2 段(这是 ISO C 标准的最新 public 草案)中有明确和正式的说明。
"未定义的行为" 不仅仅意味着您可能会得到任意值。意思是 "behavior, upon use of a nonportable or erroneous program construct or of erroneous data, for which this International Standard imposes no requirements" (N1570 3.4.3)。语言标准几乎没有说明访问此类对象的值意味着什么。这实际上是纯粹的胡言乱语。 (标准的笑话是未定义的行为会导致恶魔从你的鼻子里飞出来。当然在现实生活中它不能——但如果是这样你就不能抱怨编译器不符合标准。)
但是让我们暂时忽略访问未初始化 int
的未定义行为。也许你今天感觉很幸运。也许您知道您的编译器将如何处理这种情况。
更准确地说,让我们对未定义的行为如何表现做出一些合理的假设。
假设我们有:
int i;
i ++;
假设 i
的(未定义)初始值恰好是 INT_MAX
。然后递增 i
会导致有符号整数溢出,这又是明确未定义的行为(N1570 6.5 第 5 段)。即使初始值小于 INT_MAX
,将其递增足够多次也会导致溢出——而且由于您不知道初始值是多少,因此您不知道可以 [=65] 多少次=] 增加它。
最可能的不良后果是优化编译器将根据假设其行为已定义的方式转换代码。这是一个不涉及未初始化变量的示例:
int i = INT_MAX;
int j = i + 1;
if (j > i) { /* ... */ }
如果加法遵循常见的环绕 2 的补语语义(不是 C 标准保证的,但通常是在硬件中实现的),那么j
将等于 INT_MIN
,而 (j > i)
将为假。从逻辑上讲,(j > i)
必须 为假,因为没有 int
值可以超过 INT_MAX
。但是由于 j
设置为 i + 1
,因此 (j > i)
必须 为真(对于任何具有定义行为的程序)。
无论您对此代码的行为有何期望,优化编译器都可能在法律上违反它们。如果幸运的话,它可能会在破坏您的代码之前警告您,但这不是必需的。
你在 i
的声明中添加 = 0
所花费的时间远远少于我们讨论如果你不这样做会发生什么情况所花费的时间。 (但即便如此,如果你增加 i
足够多的次数导致溢出,你可能 运行 会遇到问题。)
当大多数人读到“这导致未定义的行为”时,他们最初将其解释为“您感兴趣的变量的值是不确定的”。
这是错误的。
文本的字面意思就是它的意思:程序本身的行为未定义。
如果他们想告诉你一个值是不确定的,那么他们会说这个值是不确定的。但他们没有。这些是不同的句子,不同的意思。
当行为未定义时,意味着程序是无意义 -- 对于例如,在您对其值进行推断时,该变量甚至可能不再 存在 。
该程序甚至可能没有执行您认为的代码。它可能只是跳到别的地方,它可能突然删除你磁盘上的一个文件,它可以做任何事情。质疑变量的值完全没有抓住要点。
这 与您的示例一样糟糕,因为虽然您可能认为它等同于 i = 0
,但它 也 导致未定义的行为:
int i;
i -= i;
即,未定义的行为与任何特定变量的值无关。它是关于 behavior(动词),而不是 data(名词)。
虽然 "Undefined Behavior" 很可能是变量中的未定义值,但您绝不应该编写会做出该假设的代码。
其他一些发帖人通过举一个删除您的主目录的极端例子来强调未定义的行为。我们都知道这个例子很极端,但是他们提出的观点并不极端。
现实的未来 "Undefined Behavior" 可能是由增强的内存错误检查触发的分段错误。
由 "Purify" 等调试工具执行的内存故障检查可以检测到这些类型的错误。考虑到这些类型的工具可能会发展到具有足够低的开销,以至于它们将在生产代码中用于捕获这样的 "Undefined Behavior".
并不是完全牵强附会。
也就是说,对于某些未来的实现,"Undefined Behavior" 将是一个分段错误。
根据 this SO answer,在 C 中递增未初始化的 int
会导致未定义的行为。但是,如果我根本不关心初始值怎么办?例如,如果我只想要一个递增的 id 怎么办?这是危险的还是不好的做法?
如果访问具有自动存储持续时间的对象的值,并且该对象未初始化,则行为未定义。这在 N1570 6.3.2.1 第 2 段(这是 ISO C 标准的最新 public 草案)中有明确和正式的说明。
"未定义的行为" 不仅仅意味着您可能会得到任意值。意思是 "behavior, upon use of a nonportable or erroneous program construct or of erroneous data, for which this International Standard imposes no requirements" (N1570 3.4.3)。语言标准几乎没有说明访问此类对象的值意味着什么。这实际上是纯粹的胡言乱语。 (标准的笑话是未定义的行为会导致恶魔从你的鼻子里飞出来。当然在现实生活中它不能——但如果是这样你就不能抱怨编译器不符合标准。)
但是让我们暂时忽略访问未初始化 int
的未定义行为。也许你今天感觉很幸运。也许您知道您的编译器将如何处理这种情况。
更准确地说,让我们对未定义的行为如何表现做出一些合理的假设。
假设我们有:
int i;
i ++;
假设 i
的(未定义)初始值恰好是 INT_MAX
。然后递增 i
会导致有符号整数溢出,这又是明确未定义的行为(N1570 6.5 第 5 段)。即使初始值小于 INT_MAX
,将其递增足够多次也会导致溢出——而且由于您不知道初始值是多少,因此您不知道可以 [=65] 多少次=] 增加它。
最可能的不良后果是优化编译器将根据假设其行为已定义的方式转换代码。这是一个不涉及未初始化变量的示例:
int i = INT_MAX;
int j = i + 1;
if (j > i) { /* ... */ }
如果加法遵循常见的环绕 2 的补语语义(不是 C 标准保证的,但通常是在硬件中实现的),那么j
将等于 INT_MIN
,而 (j > i)
将为假。从逻辑上讲,(j > i)
必须 为假,因为没有 int
值可以超过 INT_MAX
。但是由于 j
设置为 i + 1
,因此 (j > i)
必须 为真(对于任何具有定义行为的程序)。
无论您对此代码的行为有何期望,优化编译器都可能在法律上违反它们。如果幸运的话,它可能会在破坏您的代码之前警告您,但这不是必需的。
你在 i
的声明中添加 = 0
所花费的时间远远少于我们讨论如果你不这样做会发生什么情况所花费的时间。 (但即便如此,如果你增加 i
足够多的次数导致溢出,你可能 运行 会遇到问题。)
当大多数人读到“这导致未定义的行为”时,他们最初将其解释为“您感兴趣的变量的值是不确定的”。
这是错误的。
文本的字面意思就是它的意思:程序本身的行为未定义。
如果他们想告诉你一个值是不确定的,那么他们会说这个值是不确定的。但他们没有。这些是不同的句子,不同的意思。
当行为未定义时,意味着程序是无意义 -- 对于例如,在您对其值进行推断时,该变量甚至可能不再 存在 。
该程序甚至可能没有执行您认为的代码。它可能只是跳到别的地方,它可能突然删除你磁盘上的一个文件,它可以做任何事情。质疑变量的值完全没有抓住要点。
这 与您的示例一样糟糕,因为虽然您可能认为它等同于 i = 0
,但它 也 导致未定义的行为:
int i;
i -= i;
即,未定义的行为与任何特定变量的值无关。它是关于 behavior(动词),而不是 data(名词)。
虽然 "Undefined Behavior" 很可能是变量中的未定义值,但您绝不应该编写会做出该假设的代码。
其他一些发帖人通过举一个删除您的主目录的极端例子来强调未定义的行为。我们都知道这个例子很极端,但是他们提出的观点并不极端。
现实的未来 "Undefined Behavior" 可能是由增强的内存错误检查触发的分段错误。
由 "Purify" 等调试工具执行的内存故障检查可以检测到这些类型的错误。考虑到这些类型的工具可能会发展到具有足够低的开销,以至于它们将在生产代码中用于捕获这样的 "Undefined Behavior".
并不是完全牵强附会。也就是说,对于某些未来的实现,"Undefined Behavior" 将是一个分段错误。