【问题标题】:Does freeing an int* which was assigned to a char* (allocated by `malloc`) invoke Undefined Behavior?释放分配给 char*(由“malloc”分配)的 int* 会调用未定义行为吗?
【发布时间】:2015-07-12 01:47:12
【问题描述】:

标题可能令人困惑。假设strmalloc 分配的指针。 ptr,类型为 int*,被分配给它并被释放,如下面的代码 sn-p 所示:

char* str = malloc(64);
int* ptr = str;

free(ptr);

我试图编译上面的代码。它只是给出一个警告:

source_file.c: In function ‘main’:
source_file.c:10:16: warning: initialization from incompatible pointer type
     int* ptr = str;
                ^

上面的代码会调用未定义的行为吗?
上面的代码sn -p是否释放了mallocstr分配的内存?

【问题讨论】:

  • 您使用的是哪个编译器?上面的代码使用 gcc 4.9.2 给出了error: cannot convert ‘char*’ to ‘int*’ in initialization
  • 在昨天这个问题之后,我也在考虑提出这个问题。感谢您跟进此... :-)
  • @farukdgn ,在 C++ 中使用 g++ 编译时出现该错误。 GCC 似乎可以编译它。我正在使用 GCC 4.8.1。

标签: c pointers memory free undefined-behavior


【解决方案1】:

上面的代码是否调用了未定义的行为?

视情况而定。

来自 C11 草案 6.3.2.3/7:

指向对象类型的指针可以转换为指向不同对象类型的指针。如果 结果指针未正确对齐)对于引用的类型,行为是 未定义。

由于char 的对齐方式可能与int 不同,这可能限制较少,将char * pc 分配给int * pi 可能会导致pi 未对齐。

但是对于 OP 给出的具体示例:

char * pc = malloc(64);
int * pi = pc;

行为将被定义为(参见Alter Manncommentmalloc() 保证返回正确对齐的内存块。

来自 C11 草案 7.22.3:

如果分配成功,则返回的指针 [由aligned_alloc、calloc、malloc 和 realloc] 适当对齐,以便可以将其分配给 指向具有基本对齐要求的任何类型对象的指针 ...

由于未对齐而导致未定义行为的示例是:

char * pc = malloc(64);
int * pi = pc + 1;

上面的代码sn-p是否释放了malloc为str分配的内存?

如果以前的分配会引入未定义的行为,这个问题是无关紧要的,因为已经调用了 UB 可能会发生任何事情。

如果先前的赋值不会调用 UB,则对 free() 的调用将完美地取消分配引用的内存块,因为将指针值从 int * 转换回 void *,如最初提供的那样malloc(),定义明确。

来自 C11 草案 6.3.2.3/7(续):

否则,当再次转换回来时,结果将等于 原始指针

来自 C11 草案 6.3.2.3/1:

指向 void 的指针可以转换为指向任何对象类型的指针或从指向任何对象类型的指针转​​换。指向的指针 任何对象类型都可以转换为指向 void 并再次返回的指针;结果应 比较等于原始指针

【讨论】:

  • 但另一方面:The block that malloc gives you is guaranteed to be aligned so that it can hold any type of data.
  • 保证对齐”是吗?
【解决方案2】:

没有。它不会调用未定义的行为。警告只是关于您可以强制转换的不兼容类型。

char* str = malloc(64);
int* ptr = (int*) str;
free(ptr);

free 确实需要一个 void 指针,上面没有问题。但是,由于 int 类型和 char 类型的对齐,使用这种值的结果可能调用未定义的行为。因此,char*int* 的转换本身不会导致未定义。

【讨论】:

  • 按照标准(见我的回答),如果没有给出目标类型的对齐,只是赋值已经调用了 UB。
  • 不确定您引用的哪个部分说将 char ptr 分配给 int ptr 会导致 UB。正如我在回答中所说,只有 using 才能导致 UB。
  • 我在回答中举了一个例子,说明什么时候分配会引起 UB。您的代码以及 OP 的代码不会引起 UB。
  • @BlueMoon:在一个平台上,int 存储在一个未对齐的地址不会陷阱,但会简单地忽略地址的低位(覆盖附近的数据),具有某种编译器-生成的对齐验证会很有帮助。让编译器捕获将未对齐的 char* 强制转换或强制转换为 int* 的尝试可能比捕获使用指针进行的所有访问便宜得多。
【解决方案3】:

上面的代码是否调用了未定义的行为?

没有。

上面的代码sn-p是否释放了malloc为str分配的内存?

是的。

只是为了澄清,关于 UB 关于动态分配的一些注释:

malloc 返回的内存被对齐以获取任何可能的值。这种内存没有声明类型,它的有效类型是通过存储设置的。

如果你这样做

*ptr = 42;

内存块的第一个 sizeof (int) 字节现在将是 int 类型,并且只能这样读取,即

float val = *(float *)ptr;

将是 UB。

然而,

*(float *)ptr = 42.0;

将是合法的,因为它重新设置了有效类型,现在反过来使读取*ptr 无效。

此外,通过charunsigned char 类型的指针访问任何对象总是合法的。

【讨论】:

  • 很抱歉,两天前我在摆弄手机时,不小心给你投了反对票。如果不进行编辑,我无法撤消它。
  • @BlueMoon:不用担心;从技术上讲,您的编辑应该是 charsigned charunsigned char - 这些都是不同的类型,尽管其中 2 个具有相同的表示;)
  • 对。我主要是想进行一些简单的编辑而不是纠正任何事情:)
【解决方案4】:

它可以在访问时根据字节序、对齐或此类 int 与 char 类型转换调用 UB。当您执行 malloc 时,它所做的只是返回一个void*,它可以是任何数据类型(在某些情况下可能需要类型转换)。如果您将 char* 中的指针放入 int* 并没有什么区别,但访问单元会有所不同,即 ints 一次 4 个字节与 char 一次 1 个字节。因此,您问题中的代码不会调用 UB,但内存访问可能会。

至于第二个问题,是的,在 ptr 上调用 free 将导致 str 指向的内存也被释放。现在,str 将是一个悬空指针。

【讨论】:

  • 可能需要类型转换”在 C 中它永远不会。
  • 在 C++ 中,您需要进行类型转换。
  • C 和 C++ 不是同一种语言。这个问题是关于 C 的。
猜你喜欢
  • 2012-12-10
  • 2019-08-20
  • 2018-02-03
  • 2021-12-17
  • 1970-01-01
  • 2011-04-05
  • 1970-01-01
  • 2020-05-12
  • 2014-11-28
相关资源
最近更新 更多