我一直在打开和关闭C/C ,但是今天我在某本书中看到了我从未见过的东西:
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是指指针,因此分配的内存应在堆栈上,大小为size_t
(ex。64位),而其数据应仅通过在堆上分别分配来获得:
b = (item*)malloc(sizeof(struct item));
显然,不需要该。这怎么可能?
在您的情况下,b->code
和b->prize
正在访问非初始化的内存,结果是未定义的行为。不要这样做。
要详细说明,没有
任何不错的编译器都可能(不需要)发出针对这样的代码的警告。对于gcc
,启用-Wuninitialized
(添加-Wall
启用)应显示
警告:" b"在此功能[ - wuninitializatized]
中使用
也就是说,您有另一个主要问题。您将float
作为 %d
格式指定符参数,再次调用UB。
printf ("Code: %d, Prize %d", a.code, a.prize);
^^^ ^^^^^^^^
相关,引用 C11
,第§7.21.6.1
[...]如果有任何参数 不是相应转换规范的正确类型,行为是 未定义。
您应该使用%f
打印float
。
最后,对于托管环境,main()
的签名至少应为int main(void)
,以符合标准。
正如您正确诊断的那样,b
是一个非初始化的指针。访问成员以读取和写作调用不确定的行为,没有内存是自动分配的,以指向非直接的指针。非初始化的指针只有不应该使用的不确定值。
在您的情况下,该程序似乎表现通常通常,但这只是一个巧合,并且在现代系统上不太可能:如果b
恰好具有指向某些可访问内存的值,则因此,不会发生分割故障,但是谁知道修改了哪些对象以及这可能会产生什么进一步的后果...
不要执行此操作,并在当前编译器能够检测到此类错误时启用了更多警告:gcc -Wall -Wextra
或clang -Weverything
。
您的代码中还有其他几个问题:
- 您不包括标准标题
<stdio.h>
; - 您通过
printf
格式%d
;
通过 - 您将
main
定义为返回void
,它应该返回int
; - 您不会使用newline结束输出到
stdout
。
float
第二项还调用了未定义的行为。其他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 %fn", a.code, a.prize);
b = malloc(sizeof(*b));
if (b != NULL) {
b->code = 124;
b->prize = 200.75;
printf ("Code: %d, Prize %fn", b->code, b->prize);
}
return 0;
}