编译器优化对 malloc 的调用以返回更多弱对齐内存是否合法?

2024-05-07

假设我们有以下代码

#include <stdlib.h>
#include <limits.h>
#include <stdio.h>

int main(void) {
    char *w = malloc(sizeof(long));
    long *n;

    *w = 'x';
    printf("some character: %c\n", *w);
    n = (long *)w;
    *n = LONG_MAX;
    printf("LONG_MAX: %ld", *n);

    return 0;
}

在一个系统上long比更严格地对齐char(这将是大多数系统)。

如果我们不打印long object *n, 的结果malloc只会用于char(其中有对齐1)。也就是说,智能编译器似乎不必受到以下约束:malloc与任何对象类型兼容(参见这个问题:malloc如何理解对齐? https://stackoverflow.com/q/8752546/2057969);因此它可以有malloc返回任意对齐的内存片段。

额外的代码行处理n,这样的编译器将无法再这样做,因为结果malloc现在还必须适合long。据我了解,上面的代码没有违反对齐要求或严格的别名规则。

我的理解正确吗?

  • 编译器可以优化对以下内容的调用吗malloc如果它知道内存的用途,则返回更多弱对齐的内存?
  • 同样,只要不存在对齐违规,是否允许编译的代码在幕后重新排列内存以实现较弱的对齐?也就是说:在上面的情况下,如果我们省略所有处理的行n,机器改变是否合法w紧接该行之后的任意对齐地址char *w = malloc(sizeof(long));?

相反的行为是,一旦内存被分配malloc,它必须保持其最大兼容对齐。


据我了解,上面的代码没有违反对齐要求或严格的别名规则。

我的理解正确吗?

是的,动态分配的内存保证有足够的对齐long,并且标准允许灵活使用动态分配的内存;使用新的非字符对象类型存储到动态分配的内存会将其有效类型更改为用于存储的类型。

我们可以分三步来分析标准的要求。

A. 标准规定了什么行为

C 标准指定了抽象机中程序的行为。在这个抽象机器中:

  • char *w = malloc(sizeof(long));分配适当对齐的内存long从 C 1999 到未来的 C 2023 的所有 C 标准(在标准的某些版本中可能更加严格地对齐)。出于讨论的目的,我们假设malloc成功了。
  • *w = 'x';将字符“x”写入内存。
  • printf("some character: %c\n", *w);打印带有字符“x”的消息。
  • n = (long *)w;将地址转换为long *。这是由 C 2018 6.3.2.3 7 定义的,它支持在指针到对象类型之间进行转换,前提是地址适当对齐(我们知道它是这样的)。
  • *n = LONG_MAX; writes LONG_MAX进入记忆。这不会将内存别名为不同类型,因为 C 6.5 6 表示动态分配内存的有效类型将成为用于存储它的类型,因此内存被视为具有类型long对于此访问。
  • printf("LONG_MAX: %ld", *n);使用类型访问内存long,这是内存的当前有效类型,因此这会打印带有LONG_MAX value.

B. 可观察的行为

C 2018 5.1.2.3 表示,仅需要符合标准的 C 实现来产生可观察的行为,而可观察的行为是:

  • 对易失性对象的访问严格按照抽象机的规则进行评估。
  • 在程序终止时,写入文件的所有数据应与根据抽象语义执行程序所产生的结果相同。
  • 交互设备的输入和输出动态应按照 7.21.3 的规定进行。这些要求的目的是尽快出现无缓冲或行缓冲的输出,以确保提示消息实际上在程序等待输入之前出现。

所以代码的可观察行为是:

  • 它打印“某个字符:x”,后跟一个换行符。
  • 它打印“LONG_MAX:”,后跟值LONG_MAX十进制和换行符。

C.应用以上内容回答问题

编译器可以优化对以下内容的调用吗malloc如果它知道内存的用途,则返回更多弱对齐的内存?

如果编译器生成一个程序:

  • 打印“某些字符:x”,后跟换行符。
  • 打印“LONG_MAX:”,后跟值LONG_MAX十进制和换行符。

则满足C标准的要求。因此,如果编译器生成的程序使用比 C 2018 中抽象要求更多的弱对齐内存,并且该程序打印这些消息,那么它就符合 C 标准。例如,如果基本对齐要求是 16 字节,那么malloc在抽象机中需要产生一个与 16 字节的倍数对齐的地址。但是,这不是可观察的行为,因此编译器生成的实际程序不需要这样做。只要它打印出所需的消息,就符合要求。

同样,只要不存在对齐违规,是否允许编译的代码在幕后重新排列内存以实现较弱的对齐?也就是说:在上面的情况下,如果我们省略所有处理的行n,机器改变是否合法w紧接该行之后的任意对齐地址char *w = malloc(sizeof(long));?

If the n行被删除,程序的可观察行为将是:

  • 打印“某些字符:x”,后跟换行符。

只要编译器生成了打印该消息的程序,用于执行该操作的任何内存的对齐方式就与它是否符合 C 标准无关。 C 标准所要求的只是打印该消息。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

编译器优化对 malloc 的调用以返回更多弱对齐内存是否合法? 的相关文章

  • C++ 维护子类对象的混合集合

    如果我在这里错过了一个相当基本的概念 我很抱歉 但我正在尝试弄清楚如何维护多个类类型的集合 所有类类型都派生自同一个父类 并且在检索它们时仍然可以访问它们的特定于子类的方法从集合中 作为上下文 我有一个基类 BaseClass 和许多类 例
  • 静态只读字符串数组

    我在我的 Web 应用程序中使用静态只读字符串数组 基本上数组有错误代码 我将所有类似的错误代码保存在一个数组中并检查该数组 而不是检查不同常量字符串中的每个错误代码 like public static readonly string m
  • 如何从 C# 中的 dataTable.Select( ) 查询中删除单引号?

    所以我有一个经销商名称列表 我正在我的数据表中搜索它们 问题是 一些傻瓜必须被命名为 Young s 这会导致错误 drs dtDealers Select DealerName dealerName 所以我尝试替换字符串 尽管它对我不起作
  • 以编程方式读取 SQL Server 查询计划建议的 SQL 特定执行的索引?

    如果我在 SSMS 中运行此命令 set showplan xml on GO exec some procedure arg1 arg2 arg3 GO set showplan xml off GO 我获得查询执行中涉及的完整调用堆栈的
  • IdentityServer 4 对它的工作原理感到困惑

    我阅读和观看了很多有关 Identity Server 4 的内容 但我仍然对它有点困惑 因为似乎有很多移动部件 我现在明白这是一个单独的项目 它处理用户身份验证 我仍然不明白的是用户如何注册它 谁存储用户名 密码 我打算进行此设置 Rea
  • 查找进程的完整路径

    我已经编写了 C 控制台应用程序 当我启动应用程序时 不使用cmd 我可以看到它列在任务管理器的进程列表中 现在我需要编写另一个应用程序 在其中我需要查找以前的应用程序是否正在运行 我知道应用程序名称和路径 所以我已将管理对象搜索器查询写入
  • 如何判断计算机是否已重新启动?

    我曾经使用过一个命令行 SMTP 邮件程序 作为试用版的限制 它允许您在每个 Windows 会话中最多接收 10 封电子邮件 如果您重新启动计算机 您可能还会收到 10 个以上 我认为这种共享软件破坏非常巧妙 我想在我的应用程序中复制它
  • 如何使用 Castle Windsor 将对象注入到 WCF IErrorHandler 实现中?

    我正在使用 WCF 开发一组服务 该应用程序正在使用 Castle Windsor 进行依赖注入 我添加了一个IErrorHandler通过属性添加到服务的实现 到目前为止一切正常 这IErrorHandler对象 一个名为FaultHan
  • 为什么在 WebApi 上下文中在 using 块中使用 HttpClient 是错误的?

    那么 问题是为什么在 using 块中使用 HttpClient 是错误的 但在 WebApi 上下文中呢 我一直在读这篇文章不要阻止异步代码 https blog stephencleary com 2012 07 dont block
  • 为什么可以通过ref参数修改readonly字段?

    考虑 class Foo private readonly string value public Foo Bar ref value private void Bar ref string value value hello world
  • 类型约束

    我有以下类层次结构 class Header IEnumerable
  • 启动时的 Excel 加载项

    我正在使用 Visual C 创建 Microsoft Excel 的加载项 当我第一次创建解决方案时 它包含一个名为 ThisAddIn Startup 的函数 我在这个函数中添加了以下代码 private void ThisAddIn
  • 为什么这个二维指针表示法有效,而另一个则无效[重复]

    这个问题在这里已经有答案了 这里我编写了一段代码来打印 3x3 矩阵的对角线值之和 这里我必须将矩阵传递给函数 矩阵被传递给指针数组 代码可以工作 但问题是我必须编写参数的方式如下 int mat 3 以下导致程序崩溃 int mat 3
  • C++ int 前面加 0 会改变整个值

    我有一个非常奇怪的问题 如果我像这样声明一个 int int time 0110 然后将其显示到控制台返回的值为72 但是当我删除前面的 0 时int time 110 然后控制台显示110正如预期的那样 我想知道两件事 首先 为什么它在
  • 保护 APK 中的字符串

    我正在使用 Xamarin 的 Mono for Android 开发一个 Android 应用程序 我目前正在努力使用 Google Play API 添加应用内购买功能 为此 我需要从我的应用程序内向 Google 发送公共许可证密钥
  • 检测到严重错误 c0000374 - C++ dll 将已分配内存的指针返回到 C#

    我有一个 c dll 它为我的主 c 应用程序提供一些功能 在这里 我尝试读取一个文件 将其加载到内存 然后返回一些信息 例如加载数据的指针和内存块的计数到 c Dll 成功将文件读取到内存 但在返回主应用程序时 程序由于堆损坏而崩溃 检测
  • 打印大型 WPF 用户控件

    我有一个巨大的数据 我想使用 WPF 打印 我发现WPF提供了一个PrintDialog PrintVisual用于打印派生的任何 WPF 控件的方法Visual class PrintVisual只会打印一页 因此我需要缩放控件以适合页面
  • String.Empty 与 "" [重复]

    这个问题在这里已经有答案了 可能的重复 String Empty 和 有什么区别 https stackoverflow com questions 151472 what is the difference between string
  • 将数组作为参数传递

    如果我们修改作为方法内参数传递的数组的内容 则修改是在参数的副本而不是原始参数上完成的 因此结果不可见 当我们调用具有引用类型参数的方法时 会发生什么过程 这是我想问的代码示例 using System namespace Value Re
  • 我可以在“字节数”设置为零的情况下调用 memcpy() 和 memmove() 吗?

    当我实际上没有什么可以移动 复制的时候 我是否需要处理这些情况memmove memcpy 作为边缘情况 int numberOfBytes if numberOfBytes 0 memmove dest source numberOfBy

随机推荐