我知道这一点:我是否投射了malloc的结果?
我读了它和其他问题,但我仍然没有满足我的担忧。我知道类型转换会隐式发生,但无论我读多少这些参数,错误都是我不明白的。
主要论点是int
和pointer
可能具有不同的大小。例如,让 int*
的大小为 8,int
的大小为 4。
int *x = (int*) malloc(sizeof(int));
首先,malloc()
分配的字节数为 4,尽管变量 x
将存储大小为 8 字节的指针。
我明确地将 malloc 回归 (int *)
,顺便说一下,它的尺寸为 8。怎么会有位丢失?
如果你没有#include
d stdlib.h
,malloc
的返回值很可能在返回到调用代码之前被截断。从理论上讲,它是未定义的行为。如果 malloc
的返回值被截断,则类似于使用:
int a = 10;
int* ap = &a;
int temp = (int)ap; // This is where you lose the pointer value due to truncation.
int* bp = (int*)temp;
你的房子有50英尺宽。您的住址是"40, Church st, Newtown"。假设有一家像沃尔玛这样的大商店或你家旁边的学校。那是450英尺宽。但地址是"50,教堂街,纽敦"。
您是否需要不同尺寸的纸张来写入这些地址,因为一个地址比另一个大?当然不是。指针是地址的同义词。这是位置。存储在那里的内容(在您的情况下为 4 个字节(不会更改地址长度。
因此,在最后,x 的大小为 8,并将指向 4 字节大小。
让我们看看会发生什么。假设以下代码:
#include <stdio.h>
int main(int argc, char ** argv) {
printf("sizeof(int) = %zunsizeof(int *) = %zun",
sizeof(int), sizeof(int *));
// output:
// sizeof(int) = 4
// sizeof(int *) = 8
int * foo = (int *) malloc(sizeof(int));
return 0;
}
在我的系统上使用clang ouch.c
编译它得到:
ouch.c:5:23: warning: implicitly declaring library function 'malloc' with type 'void *(unsigned long)'
int * foo = (int *) malloc(sizeof(int));
^
ouch.c:5:23: note: include the header <stdlib.h> or explicitly provide a declaration for 'malloc'
在这里,clang 足够聪明,注意到我正在调用 malloc
,这是一个已知的库函数,并假设(正确的(函数签名void *(unsigned long)
。所以一切都很好。但不是每个编译器都那么聪明,我也可以欺骗叮当声:
#include <stdio.h>
int main(int argc, char ** argv) {
printf("sizeof(int) = %zunsizeof(int *) = %zun",
sizeof(int), sizeof(int *));
int * foo = (int *) wrapper();
return 0;
}
在一个单独的文件中,我将链接到上面的主文件:
#include <stdlib.h>
void * wrapper(void) {
return malloc(sizeof(int));
}
跑步clang wrapper.c ouch2.c
给我:
ouch2.c:5:23: warning: implicit declaration of function 'wrapper' is invalid in C99 [-Wimplicit-function-declaration]
int * foo = (int *) wrapper();
^
ouch2.c:5:15: warning: cast to 'int *' from smaller integer type 'int' [-Wint-to-pointer-cast]
int * foo = (int *) wrapper();
^
2 warnings generated.
这很好,因为如果阅读这些警告,那么很容易理解问题的根源并修复它们。但是,如果我忽略它们并保持代码不变,就会发生以下情况:
编译ouch2.c
时,clang 看不到任何 wrapper
的声明。由于我从循环中删除了它的智能库函数检测,它别无选择,只能假设在某处将其声明为
int wrapper();
这是一个返回int
并接受任意数量的参数的函数。我们看到了证据,因为 clang(作为一个智能编译器(警告我关于从(返回的(int
到int *
的第二个警告的转换。
这种int
投射到int *
并不是这里的坏事。不好的是假设我们首先得到了一个int
。假设在 wrapper
函数中调用 malloc
返回了以下值:
0xAABBCCDD11223344
然后发生的情况取决于调用约定。让我们假设它将此值作为返回值放入某个 64 位寄存器中。
main
中的调用代码需要int
,因此它只从寄存器(可能是下半部分(读取 32 位并使用它。所以在main
,我从wrapper
得到这个:
0x11223344
然后将其转换为(64位(int *
,可能导致:
0x0000000011223344
然后用作内存地址。访问此地址可能会(如果您幸运的话(导致分段错误或(如果您不那么幸运(更改一些随机数据(如果它发生在堆栈上,例如更改返回地址,这尤其有趣(。
所以,最后但并非最不重要的一点是,如果我把演员阵容排除在外:
#include <stdio.h>
int main(int argc, char ** argv) {
printf("sizeof(int) = %zunsizeof(int *) = %zun",
sizeof(int), sizeof(int *));
int * foo = wrapper();
return 0;
}
并编译它clang wrapper.c ouch3.c
我得到:
ouch3.c:5:15: warning: implicit declaration of function 'wrapper' is invalid in C99 [-Wimplicit-function-declaration]
int * foo = wrapper();
^
ouch3.c:5:9: warning: incompatible integer to pointer conversion initializing 'int *' with an expression of type 'int' [-Wint-conversion]
int * foo = wrapper();
^ ~~~~~~~~~
2 warnings generated.
也是一个警告,但一个不同的警告。这种(某种(警告更有可能由编译器生成。
长话短说:clang 在警告潜在错误方面做得很好,并且不要强制转换返回值,因为这样如果你忘记包含 stdlib.h,即使不使用 clang 编译,也可以肯定会收到警告:)
请注意,在"int *x"中,x变量在定义后立即被分配。 它是一个指针,即它包含堆内的地址。然而,它的价值在分配之前是没有意义的;就像任何其他变量一样。malloc(n( 确保在内存中分配返回其值的地址处的 n 个字节。换句话说,x 的大小为 8,但 x[0] 的大小为 4,malloc(4( 分配了足够的内存来存储 x[0]。查看它们差异的一个好方法是比较 &x 和 &x[0]。后者将等于 x。