“int i=999; char c=i;”和“char c=999;”有什么不同?

4

我的朋友说他在stackoverflow的某个页面上读到它们是不同的,但是这两者怎么可能不同呢?

情况1

 int i=999;
 char c=i;

案例2
 char c=999;

在第一种情况下,我们将整数i初始化为999,然后用实际上是999i初始化c。在第二种情况下,我们直接用999初始化c。撇开截断和信息丢失不谈,这两种情况到底有什么不同呢? 编辑 以下是我所说的链接 将int转换为char时为什么没有溢出警告 其中一位成员评论说:“这不是同一件事。第一个是赋值,第二个是初始化。”
因此,这难道不仅仅是编译器优化的问题吗?

@JimBalter 我会记住的,Balter先生。但是我真的觉得这两个问题的性质不同。我想知道的不同于那个OP问的内容。 - Rüppell's Vulture
如果你“想知道除了那个帖子中问的问题之外的其他事情”,你应该让我们知道那个“其他事情”是什么。除非你能表达这只是一个重复,因为“有什么不同”这个问题的答案就是原来的问题。 - djechlin
我不相信这是一个重复的。 - Keith Thompson
@djechlin:看起来已经很清楚了。另一个问题特别询问为什么在一个情况下有警告而在另一个情况下没有。这是一个更一般的问题,询问这两个结构之间是否有任何区别。可以想象,在类似但不完全相同于C的语言中,它们之间可能存在实际的语义差异,超出了警告的存在或缺失。 - Keith Thompson
1
@KeithThompson 我投票关闭此问题,因为它已在“为什么一个会产生警告而另一个不会?”下得到了回答。 - djechlin
显示剩余11条评论
5个回答

9

它们具有相同的语义。

常量999的类型为int

int i=999;
char c=i;

i 被创建为 int 类型的对象,并用明显的语义初始化为 999

c 被创建为 char 类型的对象,并用 i 的值初始化,即 999。该值从 int 隐式转换为 char

普通 char 的有符号性是实现定义的。

如果普通 char 是无符号类型,则转换的结果是明确定义的。该值将对 CHAR_MAX+1 取模。对于具有 8 位字节(CHAR_BIT==8)的典型实现,CHAR_MAX+1 将为 256,存储的值将为 999 % 256,即 231

如果普通 char 是有符号类型,并且 999 超过了 CHAR_MAX,则转换会产生实现定义的结果(或者在 C99 开始后,引发实现定义的信号,但我不知道有哪些实现这样做)。对于具有 8 位字节(CHAR_BIT==8)和 2 的补码系统,通常结果为 -25

char c=999;

c作为char类型的对象被创建。它的初始值是将int999转换为char--由我上面描述的完全相同的规则。

如果CHAR_MAX >= 999(只有CHAR_BIT,字节中的位数,至少为10时才会发生),那么转换是微不足道的。有一些针对DSP(数字信号处理器)的C实现,其中CHAR_BIT设置为32,但这不是您在大多数系统上运行的情况。

在第二种情况下,您更有可能收到警告,因为它转换了一个常量表达式;在第一种情况下,编译器可能无法跟踪i的预期值。但是,一个足够聪明的编译器可以对两者都发出警告,而一个足够天真(但仍然完全符合要求)的编译器可能对两者都不发出警告。

正如我上面所说的,当源值不适合目标类型时,将值转换为带符号类型的结果是实现定义的。我想象中,一个实现可以为常量和非常量表达式定义不同的规则。虽然这是一种歪曲的选择,但我不确定甚至DS9K也不会这样做。

至于所引用的评论“第一个是赋值,第二个是初始化”,那是不正确的。两者都是初始化;在任何代码片段中都没有赋值。它们之间有一个区别,即一个是具有常量值的初始化,而另一个则没有。顺便说一下,这意味着第二个代码片段可以出现在文件范围内(在任何函数之外),而第一个则不行。


2
任何优化编译器都会使 int 类型的本地变量 i = 999 消失,并将截断的值直接赋给变量 c (假设您在其他任何地方都没有使用变量 i)。

你能再详细解释一下吗? - Rüppell's Vulture
只是稍微解释一下@Jack所说的,他的意思是编译器很可能会销毁你的i值。这意味着变量在编译后将不再存在,因此c=999将是唯一存在的东西,也就是case 2。但是,如果您在代码中的其他地方使用了i变量,则编译器将保留ic,就像您的情况1,并继续沿着这条路走。 - Nomad101

1

这取决于您的编译器和优化设置。查看实际的汇编清单以了解它们之间的差异。对于GCC和合理的优化,这两个代码块可能是等效的。


请再详细解释一下。OP 不是很聪明。 - Rüppell's Vulture
使用 gcc -S 运行 @Rüppell'sVulture 并比较清单。 - Anirudh Ramanathan
1
回答不好。问题不是关于生成的代码,而是关于语义,这些语义由语言标准定义。 - Jim Balter
在原始问题中,他真正询问的只是“这两种情况到底有什么不同?”所以你不应该那么确定,Jim Balter。 - David Grayson
"所以你不应该对此如此确定" -- 即使后续讨论非常明确表明我是正确的?从一开始就清楚,问题并不在于它们是否会生成不同的代码。 - Jim Balter

1
除了第一个还定义了类型为int的对象i之外,语义是相同的。

那个链接里的用户pmg说的是错的吗?由于他似乎是一个有声望的用户,我不得不确认一下。 - Rüppell's Vulture
@JimBalter: 给定 char c = 999;,编译器不必确定是否有溢出或警告。它可能会生成在执行时间将表达式 999 评估为类型 int 并将其转换为 char 的代码。这正是抽象机器语义中发生的情况。在编译时进行转换是一种可选的优化。 - Keith Thompson
“那么用户从链接中提取的信息是错误的?”-- 嗯,显然他的评论是错误的,因为它们显然都是初始化。这与它们是否不同是不同的问题;显然,它们有足够的不同之处,以至于编译器对其中一个发出了警告,而对另一个没有。 - Jim Balter

0
i 实际上是999。 不,i是一个变量。从语义上讲,在初始化c的时候它没有值...在运行时才能知道它的值(尽管我们可以清楚地看到它将是什么,优化编译器也可以)。但是在情况2中,你正在将999赋给一个char,这是不合适的,因此编译器会发出警告。

是的!!那个词就是溢出。Balter先生,您能否详细解释一下,以便易于理解。 - Rüppell's Vulture
@KeithThompson 非牛顿主义论点/稻草人逻辑。“只需要多一点聪明才智”——不,这需要一种与现有编译器不同且更难实现的编译器组织方式。 - Jim Balter
除此之外,@KeithThompson 提供了一份优秀而完整的答案,建议楼主接受。 - Jim Balter
我需要新的眼镜了。我的眼镜无法显示出行间内容 ;) - Daniel Fischer
@JimBalter:“显然它只是选择不这样做”是口语简写,意为“显然编译器的作者选择不编写它以便这样做”。(FYI,我曾经从事过编译器的工作。)警告一个问题和警告另一个问题之间的区别仅是程度上的区别。优化可以由编译器的任何阶段完成;即使未调用优化阶段,一些简单的优化也会自动完成。而且,增加优化通常可以启用其他警告。 - Keith Thompson
显示剩余10条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接