printf 在这里做了啥?
Posted
技术标签:
【中文标题】printf 在这里做了啥?【英文标题】:what did printf do here?printf 在这里做了什么? 【发布时间】:2020-03-24 10:31:12 【问题描述】:我只是在测试我对指针的理解,同时我这样做了,
好吧,这就是我的想法。
所有指向任何类型的指针都保存地址,对吧?
说我声明char *a, b = 'f';
a = &b ;
因此,当我尝试通过指向 char 类型的指针访问 b 的内容时,这没关系。
但是,如果我将 int 数据类型的地址存储在 char 类型的指针中会怎样(尽管我收到警告:来自不兼容指针类型 [-Wincompatible-pointer-types] 的赋值,但我没有做错,对吧?我仍在存储一些内存的地址。)
int main()
// int type of one byte
int a, i ;
char *b;
for(i = -256 ;i < 257; ++i)
a = i;
b = &a;
printf("for i = %d, value stored in first byte of a = %d\n",i ,*b);
return 0;
现在我认为它只允许读取分配给 a 的 4 个字节中的第一个字节,因为我正在通过指向 char 的指针进行访问类型,我希望存储高达 255 的值,超出这将是一种溢出。 但发生了一些不同的事情,我只能存储 127 到 -128 哇!!。你看到它是一个字节大小的 int 类型吗。你可以运行代码来查看。
现在假设 int 类型的大小为一字节
当 a = 256 时,读取的值为 0,这是预期的,因为 0001 0000 0000 它将只读取第一个字节。 输出是可预测的,直到我们达到 a = 127,它存储为 0111 1111(从 int 类型的角度来看是正值) 当 a = 128 时,输出为 -128 ,存储 1000 0000 (实际上是 -128) 同样可以解释其他输出 所以输出可以通过假设 int 类型的大小为一个字节来解释,为什么 printf 这样做而不是抛出错误。
那么 printf 到底做了什么?谢谢各位大人 ;)
【问题讨论】:
请注意,您的代码也依赖于字节序a = &b;
(或后面程序中的b = &a;
)是违反约束的——这是不允许的。您可以使用-Werror
编译器标志来获取错误消息。
如果您的第一段使用与程序相同的变量名,则不会那么混乱
@M.M:允许违反约束。 C 标准需要 C 实现来诊断它们。它不需要 C 实现来禁止它们,并且确实允许 C 实现成功地转换包含违反约束的程序。
@EricPostpischil 标准不要求实现来翻译违反约束的程序,因此我将其描述为“不允许”。在您的术语中,几乎所有内容都是“允许的”(因为它是正确的,或者因为标准对包含违反约束或其他未定义行为的程序的行为没有要求),因此这样的术语是无用的
【参考方案1】:
您的系统使用带符号的字符,这是完全正常的。
您似乎假设/期望 char
是 unsigned
,这并不总是正确的。它要么是有符号的,要么是无符号的,但这取决于实现。
【讨论】:
@EricPostpischil 确实,我有点过于简单化了。我放弃了代码降价可能会有所帮助。谢谢。【参考方案2】:但是,如果我将 int 数据类型的地址存储在 char 类型的指针中会怎样(尽管我收到警告:来自不兼容指针类型 [-Wincompatible-pointer-types] 的赋值,我没有做错,对吧?我仍在存储一些内存的地址。)
是的,你做错了什么,这就是你收到警告的原因。
指针类型很重要有两个原因:
指针运算是根据对象而不是字节来完成的。如果p
是char *
并且它的值是char
对象的地址(比如0x1000
),那么p + 1
的计算结果是下一个char
对象的地址(0x1001
)。如果p
是int *
并且它的值是4 字节int
对象的地址(0x2000
),则p + 1
计算为下一个4 字节int
对象的地址(@ 987654334@)。
与其他类型一样,不同的指针类型可能具有不同的大小和表示形式。 int *
可能与char *
具有不同的表示形式。在大多数现代系统(如 x86 和 x86_64)上,它们是相同的,但在一些较旧或古怪的架构中,情况并非如此。
所以输出可以通过假设 int 类型大小为 1 字节来解释,为什么 printf 这样做而不是抛出错误。
问题不在于printf
,问题在于当您尝试将128
分配给*b
时遇到有符号整数溢出,因为在您的系统上纯@ 987654340@ 占据了[-128..127]
的范围。不幸的是,有符号整数溢出的行为是 undefined - 不要求您的实现以任何特定方式处理它。我知道的大多数实现根本不会尝试处理它——你只会得到一个意想不到的值。鉴于有多个有符号整数表示,并且鉴于许多实现依赖于未定义的行为来执行某些优化,您可以获得几乎 any 的结果。
【讨论】:
以上是关于printf 在这里做了啥?的主要内容,如果未能解决你的问题,请参考以下文章
编译器在这里做了啥:int a = b * (c * d * + e)? [复制]