我一直在使用C / C ++,但今天我在某本书中看到了一些我以前从未见过的东西:
QueryManager
上面打印的值正常,这是struct item {
int code;
float prize;
};
void main() {
struct item a,*b;
a.code = 123;
a.prize = 150.75;
printf ("Code: %d, Prize %d", a.code, a.prize);
b->code = 124;
b->prize = 200.75;
printf ("Code: %d, Prize %d", a->code, a->prize);
}
部分的一个惊喜。由于b是一个指针,为它分配的内存应该在大小为*b
(例如64位)的堆栈上,而它的数据只能通过在堆上单独分配来获得:
size_t
显然,没有必要。这怎么可能?
答案 0 :(得分:5)
在您的情况下,b->code
和b->prize
正在访问未初始化的内存,结果为undefined behavior。 不要那样做 。
详细说明,没有发生 魔法 自动内存分配,使这些指针指向任何有效的内存。仅仅因为C 允许你编写一个访问未初始化指针的代码并不强制它是有效的代码。 必须使指针指向某个有效的内存位置,然后才能访问(读取或写入)它们。
任何体面的编译器都可能(虽然不需要)对这样的代码产生警告。对于gcc
,启用-Wuninitialized
(通过添加-Wall
启用)应显示类似
警告:'b'在此函数中未初始化使用[-Wuninitialized]
那就是说,你有另一个主要问题。您正在传递一个float
作为%d
格式说明符参数,该参数再次调用它自己的UB。
printf ("Code: %d, Prize %d", a.code, a.prize);
^^^ ^^^^^^^^
相关,引用C11
,章节§7.21.6.1
[...]如果有任何论据 不是相应转换规范的正确类型,行为是 未定义。
您应该使用%f
来打印float
。
最后,对于托管环境,main()
的签名至少应为int main(void)
,以符合标准。
答案 1 :(得分:1)
正确诊断后,b
是未初始化的指针。访问成员进行读取和写入会调用未定义的行为,会自动分配给未指定的指针指向的内存。未初始化的指针只有一个不应使用的不确定值。
在你的情况下,程序似乎正常 ,但这只是一个巧合,在现代系统中不太可能:如果b
恰好有一个指向的值一些可访问的内存,因此不会发生分段错误,但是谁知道修改了哪个对象以及可能产生的进一步后果......
请勿执行此操作,并在启用更多警告的情况下进行编译,因为当前编译器能够检测到此类错误:gcc -Wall -Wextra
或clang -Weverything
。
您的代码中还有其他一些问题:
<stdio.h>
; float
格式printf
; %d
main
定义为返回void
,它应该返回int
; stdout
。第二项也会调用未定义的行为。其他3通常没有可怕的后果,但仍应予以纠正。
以下是更正后的版本:
#include <stdio.h>
#include <stdlib.h>
struct item {
int code;
float prize;
};
int main(void) {
struct item a, *b;
a.code = 123;
a.prize = 150.75;
printf("Code: %d, Prize %f\n", a.code, a.prize);
b = malloc(sizeof(*b));
if (b != NULL) {
b->code = 124;
b->prize = 200.75;
printf ("Code: %d, Prize %f\n", b->code, b->prize);
}
return 0;
}