【问题标题】:Type casting in malloc [duplicate]malloc中的类型转换[重复]
【发布时间】:2016-05-22 14:07:09
【问题描述】:

我知道这件事:Do I cast the result of malloc?

我阅读了它和其他问题,但我仍然没有满足我的担忧。我知道类型转换会隐式发生,但是无论我读了多少这些论点,错误都是我不明白的。

主要论点是intpointer 可能有不同的大小。举个例子,让int* 的大小为 8,int 的大小为 4。

int *x = (int*) malloc(sizeof(int));

首先,malloc() 分配的字节数是 4,尽管变量 x 将存储一个大小为 8 字节的指针。

我明确地将 malloc 的返回值强制转换为 (int *),它 — 顺便说一下 — 大小为 8。这里怎么会丢失位?

【问题讨论】:

  • 什么错误?我在上面的代码中看不到任何会导致 C 中的警告或错误的内容。请注意这里没有错误或警告:ideone.com/jMccon
  • @Judismar Junior 我不明白你在问什么。
  • 你提到的问题的第一个答案非常明确。
  • 链接的答案说如果你忘记包含stdlib.h,这是一个致命错误,然后演员掩盖了这个错误。接下来发生的事情被归类为未定义的行为,可能表现为从返回的地址(或其他任何内容)中截断的位。 OTOH,如果您没有强制转换 malloc 的返回值的习惯,那么未能包含 stdlib.h 只会导致编译错误。
  • @JudismarJunior 如果链接的问题已经回答了您的问题,您可能希望通过单击问题顶部的按钮来接受此问题作为重复问题。

标签: c pointers casting malloc


【解决方案1】:

让我们看看会发生什么。假设这段代码:

#include <stdio.h>
int main(int argc, char ** argv) {
  printf("sizeof(int) = %zu\nsizeof(int *) = %zu\n",
    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)。所以一切都很好。但并不是每个编译器都那么聪明,我也可以欺骗 clang:

#include <stdio.h>
int main(int argc, char ** argv) {
  printf("sizeof(int) = %zu\nsizeof(int *) = %zu\n",
    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 的声明。由于我从循环中删除了它的智能库函数检测it has no choice but to assume that somewhere this is declared as

int wrapper();

这是一个返回 int 并接受任意数量参数的函数。我们看到了这方面的证据,因为 clang(作为一个智能编译器)警告我关于从(返回的)intint * 的转换及其第二次警告。

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) = %zu\nsizeof(int *) = %zu\n",
    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 编译,您也可以肯定会收到警告:)

【讨论】:

    【解决方案2】:

    你的房子大概有 50 英尺宽。 你的住址是'40, Church st, Newtown'。 假设您家旁边有一家大型商店,例如沃尔玛或学校。那是450英尺宽。 但地址是“50, Church St, Newtown”。

    您是否需要不同尺寸的纸张来写这些地址,因为一个比另一个大?当然不是。 指针是地址的同义词。这是位置。存储在那里的内容(在您的情况下为 4 个字节)不会更改地址长度。

    所以最后,x 的大小为 8,并将指向 4 字节大小。

    【讨论】:

    • 指针是地址的同义词。这是位置。存储在那里的内容(在您的情况下为 4 个字节的整数)不会更改地址长度。 不。请阅读 C 标准的 6.2.5 Types, paragraph 28:指向 void 的指针应具有与指向的指针相同的表示和对齐要求一种字符类型。同样,指向兼容类型的合格或不合格版本的指针应具有相同的表示和对齐要求。
    • (cont) 所有指向结构类型的指针都应具有彼此相同的表示和对齐要求。所有指向联合类型的指针都应具有彼此相同的表示和对齐要求。 指向其他类型的指针不必具有相同的表示或对齐要求。有关示例,请参阅stackoverflow.com/questions/1473935/…
    • 您的故事具有误导性。不同的指针类型可能有不同的大小和/或对齐要求。该标准不排除这一点。它也无法回答问题。
    • @n.m.你无法理解。我的故事没有误导性。如果您担心家庭地址支付的租金/城市税不适用于 C 地址/指针,是的,我的故事具有误导性。
    • @AndrewHenle,证明sizeof(一个指针)(不是数组)在不同类型的情况下是不同的。
    【解决方案3】:

    请注意,在“int *x”中,x 变量一经定义就被分配了。它是一个指针,即它包含堆内的地址。但是它的值在赋值之前是没有意义的;就像任何其他变量一样。 malloc(n) 确保将返回值的地址处的 n 个字节分配到内存中。换句话说,x 的大小为 8,但 x[0] 的大小为 4,并且 malloc(4) 分配了足够的内存来存储 x[0]。 比较 &x 和 &x[0] 是查看它们差异的一个好习惯。后者将等于 x。

    【讨论】:

      【解决方案4】:

      如果您没有#included 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;
      

      【讨论】:

      • “malloc 的返回值被截断”。结果只是未定义,这可能涉及截断返回值和/或炸毁核电站。
      • @JudismarJunior,类型转换不能解决问题。事实上,它将问题隐藏到运行时,而不是在编译时警告您。这就是为什么建议您不要对malloc 的返回值进行类型转换。
      猜你喜欢
      • 2013-02-13
      • 2014-01-30
      • 1970-01-01
      • 2014-08-17
      • 2012-10-28
      • 2013-11-22
      相关资源
      最近更新 更多