C - 两个指针之间的转换行为

2024-03-24

2020年12月11日更新:感谢@“一些程序员老兄”在评论中的建议。 我的根本问题是我们的团队正在实现动态类型存储引擎。我们分配多个字符数组[PAGE_SIZE]缓冲区16 对齐存储动态类型的数据(没有固定的结构)。出于效率原因,我们无法执行字节编码或分配额外的空间来使用memcpy.

既然对齐方式已经确定了(即16),剩下的就是使用指针的强制转换来访问指定类型的对象,例如:

int main() {
    // simulate our 16-aligned malloc
    _Alignas(16) char buf[4096];

    // store some dynamic data:
    *((unsigned long *) buf) = 0xff07;
    *(((double *) buf) + 2) = 1.618;
}

但我们团队对这个操作是否是未定义行为存在争议。


我读过很多类似的问题,例如

  • 为什么 -Wcast-align 不会对 x86 上从 char* 到 int* 的转换发出警告? https://stackoverflow.com/questions/25762173/why-does-wcast-align-not-warn-about-cast-from-char-to-int-on-x86
  • 如何在非对齐位置将 char 数组转换为 int? https://stackoverflow.com/questions/26995151/how-to-cast-char-array-to-int-at-non-aligned-position
  • C 未定义的行为。严格的别名规则,还是不正确的对齐? https://stackoverflow.com/questions/46790550/c-undefined-behavior-strict-aliasing-rule-or-incorrect-alignment/46790815#46790815
  • SEI CERT C.S EXP36-C https://wiki.sei.cmu.edu/confluence/display/c/EXP36-C.+Do+not+cast+pointers+into+more+strictly+aligned+pointer+types

但这些与我对C标准的解释不同,我想知道是否是我的误解。

主要的困惑在于该部分6.3.2.3 #7 http://port70.net/%7Ensz/c/c11/n1570.html#6.3.2.3p7 of C11:

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

68) 一般来说,“正确对齐”的概念是可传递的:如果指向类型 A 的指针与指向类型 B 的指针正确对齐,而类型 B 又与指向类型 C 的指针正确对齐,则指向类型的指针A 与指向 C 类型的指针正确对齐。

是否结果指针这里参考指针对象 or 指针值?

在我看来,我认为答案是指针对象,但更多的答案似乎表明指针值.


解释A:指针对象

我的想法如下:指针本身就是一个对象。根据6.2.5 #28 http://port70.net/%7Ensz/c/c11/n1570.html#6.2.5p28,不同的指针可能有不同的表示和对齐要求。因此,根据6.3.2.3 #7 http://port70.net/%7Ensz/c/c11/n1570.html#6.3.2.3p7,只要两个指针具有相同的对齐方式,就可以安全地转换它们,而不会出现未定义的行为,但不能保证它们可以被取消引用。 用程序表达这个想法:

#include <stdio.h>

int main() {
    char buf[4096];

    char *pc = buf;
    if (_Alignof(char *) == _Alignof(int *)) {
        // cast safely, because they have the same alignment requirement?
        int *pi = (int *) pc; 
        printf("pi: %p\n", pi);
    } else {
        printf("char * and int * don't have the same alignment.\n");
    }
}

解释B:指针值

然而,如果C11标准谈论的是指针值对于引用类型而不是指针对象。上面代码的对齐检查是没有意义的。 用程序表达这个想法:

#include <stdio.h>

int main() {
    char buf[4096];

    char *pc = buf;
    
    /*
     * undefined behavior, because:
     * align of char is 1
     * align of int is 4
     * 
     * and we don't know whether the `value` of pc is 4-aligned.
     */
    int *pi = (int *) pc;
    printf("pi: %p\n", pi);
}

哪种解释是正确的?


解释B是正确的。该标准讨论的是指向对象的指针,而不是对象本身。 “结果指针”指的是强制转换的结果,并且强制转换不会产生左值,因此它指的是强制转换后的指针值。

以您示例中的代码为例,假设int必须在 4 字节边界上对齐,即它的地址必须是 4 的倍数。如果地址buf is 0x1001然后将该地址转换为int *无效,因为指针值未正确对齐。如果地址为buf is 0x1000然后将其转换为int *已验证。

Update:

您添加的代码解决了对齐问题,因此在这方面没有问题。然而它有一个不同的问题:它违反了严格的别名。

您定义的数组包含类型的对象char。通过将地址转换为不同的类型并随后取消引用转换后的类型,您可以将一种类型的对象作为另一种类型的对象进行访问。这是 C 标准不允许的。

尽管标准中未使用术语“严格别名”,但该概念在第 6.5 节第 6 和第 7 段中进行了描述:

6 The effective type of an object for an access to its stored value is the declared type of the object, if any.87) If a value is stored into an object having no declared type through an lvalue having a type that is not a character type, then the type of the lvalue becomes the effective type of the object for that access and for subsequent accesses that do not modify the stored value. If a value is copied into an object having no declared type using memcpy or memmove, or is copied as an array of character type, then the effective type of the modified object for that access and for subsequent accesses that do not modify the value is the effective type of the object from which the value is copied, if it has one. For all other accesses to an object having no declared type, the effective type of the object is simply the type of the lvalue used for the access.

7 An object shall have its stored value accessed only by an lvalue expression that has one of the following types:88)

  • 与对象的有效类型兼容的类型,
  • 与对象的有效类型兼容的类型的限定版本,
  • 与对象的有效类型相对应的有符号或无符号类型,
  • 与对象有效类型的限定版本相对应的有符号或无符号类型的类型,
  • 聚合或联合类型,其中包括其成员中的上述类型之一(递归地包括 子聚合或包含联合),或
  • 一种字符类型。

...

87 ) 分配的对象没有声明类型。

88)此列表的目的是指定在哪些情况下 对象可能有别名,也可能没有。

在你的例子中,你正在写一个unsigned long and a double在之上char对象。这些类型都不满足第 7 款的条件。

除此之外,这里的指针算术无效:

 *(((double *) buf) + 2) = 1.618;

当你正在治疗时buf作为一个数组double当它不是时。至少,您需要执行必要的算术运算buf直接将结果投射到最后。

那么为什么这对一个char数组而不是返回的缓冲区malloc?因为内存返回自malloc has no有效类型,直到您在其中存储某些内容,这就是第 6 段和脚注 87 所描述的内容。

因此,从严格的标准角度来看,您所做的是未定义的行为。但根据您的编译器,您也许可以禁用严格别名,这样就可以了。如果您使用 gcc,您需要传递-fno-strict-aliasing flag

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

C - 两个指针之间的转换行为 的相关文章

  • 自定义可视化 Web 部件属性 sharepoint

    我在 Visual Studio 2012 中创建可视 Web 部件属性时遇到问题 我被提及http msdn microsoft com en us library ee231551 aspx http msdn microsoft co
  • 将数据集导出到 EXCEL

    我使用以下代码将数据库表中的字段导出到 Excel 中 我想要做的是能够编写一条 SQL 语句从多个表中检索字段并将其导出到 Excel 中 这段代码只允许我导出一张表 另外 如何显示保存提示对话框 示例代码将不胜感激 非常感谢 prote
  • WPF MVVM将DataTable绑定到DataGrid不显示数据

    我有一个简单的控件 其中包含一个 DataGrid 其中 ItemsSource 绑定到 DataTable 当我填充 DataTable 时 我可以看到 DataGrid 中添加了行 但没有显示任何数据 我没有为此 DataGrid 使用
  • Reflection.Emit 中的短格式操作码错误

    我正在制作一种与以下非常相似的小语言hlsl但仅支持像素着色器 该语言使用reflection emit构建实现相同功能的 NET 程序集 我目前正在测试分支指令的实现if在我的一个单元测试中 一个大的if与内if elses 失败并显示以
  • ApiController 操作无法从查询字符串解析数组

    使用 Visual Studio 2012 2 MVC4 Web 应用程序 我有请求发送到我的 ApiController 如下所示 http localhost api keys ids 1 ids 2 ids 3 我的印象是以下方法应该
  • C# - 如何将 IntPtr 缓冲区数据保存到文件(最快的方法)?

    我使用此代码将非托管代码中的 IntPtr 缓冲区中的字节保存到文件中 这是一个简单的回调函数 private void callback IntPtr buffer int length byte bytes new byte lengt
  • 无法更新 .mdf 数据库,因为该数据库是只读的(Windows 应用程序)

    我使用 C 创建了一个数据库 Windows 应用程序 我的应用程序在 Windows XP 上成功运行 但在 Vista 或 Windows 7 系统上无法正确执行 我的应用程序显示类似以下内容的消息 无法更新 mdf 数据库 因为该数据
  • WIX 自动生成 GUID *?

    假设我生成产品 ID 为 的 WIX XML 文件 另外 对于每个组件 GUID 我都使用
  • 使用 Thread.Sleep() 时,异步编程如何与线程一起工作?

    假设 前言 在之前的问题中 我们注意到Thread Sleep阻塞线程参见 什么时候使用Task Delay 什么时候使用Thread Sleep https stackoverflow com questions 20082221 whe
  • 从空白启动时 VSTO 功能区不显示解决方案

    如果我从 文件 新建项目 菜单创建一个新的 Excel 2013 和 2016 VSTO 加载项 项目 然后单击 项目 添加新项目 gt 功能区 可视化设计器 则一切正常 我启动了应用程序 我的功能区显示在 Excel 中 但是 如果我首先
  • popen2()在c中如何工作?

    我尝试使用管道 叉子和 dup 在我的程序中执行 md5sume 命令 我发现总和代码运行成功 但我无法理解某些代码行 这是我的代码 int infp outfp char buf 128 if popen2 md5sum infp out
  • 让 WIX 在项目中包含引用

    我对 WiX 和设置自定义安装程序完全陌生 所以我对问题的主题表示歉意 我有一个内部业务应用程序 日记 它构建并运行良好 因此我按照教程 官方文档添加 WiX 项目并引用日记的 csproj 然后构建并运行这个最基本版本的 WiX 安装程序
  • Parallel.For 和 Break() 误解?

    我正在研究 For 循环中的并行性中断 看完之后this http tipsandtricks runicsoft com CSharp ParallelClass html and this http reedcopsey com 201
  • 在 C# 中加密并在 Flex 中解密

    我需要解密 Flex 中的一些数据 这些数据是用 C 加密并写入文件的 为了简单起见 我选择使用 as3crypto As3 库和 Bruce Schneier C 库 AS3 as3加密链接 http code google com p
  • 如何在 Xamarin.Mac 中执行终端命令并读入其输出

    我们正在编写一个 Xamarin Mac 应用程序 我们需要执行像 uptime 这样的命令 并将其输出读取到应用程序中进行解析 这可以做到吗 在 Swift 和 Objective C 中都有 NTask 但我似乎无法在 C 中找到任何示
  • 无论表单上的焦点控件如何,如何捕获 Keys.F1?

    我使用了 KeyDown 事件和一些简单的代码 例如if e KeyCode Keys F1 捕获在表单上按下 F1 但如果表单上有一些文本框 或者表单上有一些带有 Dock Fill 的电子表格 则上面的代码将毫无用处并且不执行任何操作
  • 什么是多重重继承?

    我将以下称为 多重重新继承 直接继承一个类一次 并通过继承其一个或多个后代来间接继承一次或多次 通过继承一个类的两个或多个后代来间接继承一个类两次或多次 我想知道它是否存在以及如何明确访问嵌入的子对象 1 Professional C 2n
  • 父窗体中的居中消息框[重复]

    这个问题在这里已经有答案了 有没有一种简单的方法可以在 net 2 0中将MessageBox居中于父窗体中 我在 C 中确实需要这个并发现中心消息框 C http bytes com topic c sharp answers 26712
  • 为什么C#不支持多重继承? [复制]

    这个问题在这里已经有答案了 可能的重复 C 应该包含多重继承吗 https stackoverflow com questions 191691 should c include multiple inheritance 为什么C 不支持多
  • 在 WPF 树视图中获取 FullPath?

    如果我以编程方式创建 WPF TreeView 例如 TreeView treeView lt added in the designer TreeViewItem rootNode new TreeViewItem rootNode He

随机推荐

  • 解析 2 位数年份:使用未知日期模式设置基准日期

    用户将以不同模式输入日期到我的应用程序 对于两位数年份 他还必须确定枢轴日期 Example 图案 yy MM dd基准日期 70 我以编程方式添加当前千年和上个世纪以获得更多动态 gt 1970 69 04 22变成2069 04 22
  • 在没有“应用程序许可”的情况下获取访问者的 Facebook 信息——Yelp 是如何做到的?

    好的 我花了几个小时仔细研究有关 xFBML Open Graph 等的分散的 FB 文档 其中大部分似乎已被弃用 无论如何 当访问者访问我的网站 假设他们已经登录 FB 时 我试图获取他们的 Facebook 用户 ID 或姓名 图片或其
  • 连接 JSONArray

    我在用JSON数组 http www json org javadoc org json JSONArray html在下面org json包裹 我的第一个 JSONArray 是这样的 249404 VPR249404 249403 VP
  • C++ 中的作用域和返回值

    我重新开始使用 c 并正在考虑变量的范围 如果我在函数内有一个变量 然后我返回该变量 那么该变量在返回时不会因为它所在的作用域已结束而 死亡 吗 我已经尝试过使用返回字符串的函数 它确实有效 谁能解释一下吗 或者至少给我指出一些可以向我解释
  • 使用自适应布局的 UILabel 的不同字体大小

    在 Xcode 6 中 我们有一个称为自适应布局的新功能 通过使用它 我创建了一个新项目 在其中我需要根据布局具有不同的字体大小 在 iPhone 3 5 英寸屏幕中 我必须将字体大小设置为 17pt 而 iPhone 中的其他屏幕大小必须
  • 让 Grails 监听外部 IP 地址

    我启动了一个 Grails 应用程序来侦听默认端口 8080 并以 root 身份运行 grails 以侦听端口 80 grails Dserver port 80 run app 这在本地主机上运行得很好 但是当我从外部 IP 地址访问它
  • 动态获取 docker-compose 创建的正在运行的容器名称

    当我运行 docker compose 时 它 会创建一个 Web 容器和 postgres 容器 我想通过类似的方式手动触发我的 Django 测试运行 docker compose run web python manage py te
  • 对于系统文本的多语言支持 x 种语言来说,这是一个很好的设计吗? [关闭]

    Closed 这个问题是基于意见的 help closed questions 目前不接受答案 这个实现的扩展性是否良好 要求是 系统必须支持x种语言 其中 x will 企业可以翻译的尽可能多的语言 所有系统维护的值 页面内容 电子邮件内
  • 使用 AVPlayer 传输音频

    当在表视图中选择一行但它不播放时 我尝试使用 AVPlayer 播放 URL 中的音频 我究竟做错了什么 这是代码 void tableView UITableView tableView didSelectRowAtIndexPath N
  • 如何在 Xcode 中使用 boost::multi precision::float128

    我正在尝试在 xCode 项目中使用 boost multi precision float128 我的编译器版本是 Apple clang 版本 11 0 0 clang 1100 0 20 17 目标 x86 64 apple darw
  • 检查模型是否存在,如果在 Laravel 中找不到则继续路由

    我有两个模型 我不想在其 URL 前面添加前缀 例如 用户和帖子 如果我有一个网址https example com title of the post https example com title of the post and htt
  • 正确确定日期字符串是否是该格式的有效日期

    我从 API 收到一个日期字符串 其格式为yyyy mm dd 我目前正在使用正则表达式来验证字符串格式 该格式工作正常 但我可以看到在某些情况下 根据字符串它可能是正确的格式 但实际上是无效的日期 IE 2013 13 01 例如 PHP
  • R包已安装但无法加载?

    我在 R 中安装了 digest 包 gt install packages digest Installing package into C Users user Documents R win library 3 3 as lib is
  • 为什么 Replace() 在我的 Python 函数中不起作用? [复制]

    这个问题在这里已经有答案了 这是实际代码 def replace exception chars string exception chars dict Old New old new exception chars keys list e
  • 如何更改时间选择器对话框片段的默认配色方案?

    我正在尝试更改时间选择器对话框片段的默认颜色 现在我不知道我应该做什么 这就是我设法将我的主题设置为 没有actionBar but this is the dialog i get 这是我的 xml 样式文件
  • 睡眠或暂停 NSThread

    我正在创建一个新线程 它每隔一定时间就运行我的方法之一 现在我正在做的事情如下 NSThread thread NSThread alloc initWithTarget self selector selector setUpTimerT
  • 如何向 clang-cl 添加 std=c++11 标志?

    我尝试在Windows下使用clang cl编译C 11程序 Clang cl 是 clang 的包装器 以便与 cl Visual Studio 编译器 更加兼容 我尝试添加 std c 11 and std c 11并且都被拒绝了 这并
  • Ruby on Rails:我的实际目标是什么?

    我一直在 Ruby on Rails 3 中构建应用程序 并且开始担心性能优化 现在我希望我的问题对于这个网站来说不太主观 但我对事实感兴趣 而不是讨论 所以这里是 虽然我试图让视图渲染得更快 但有一件事我根本不知道 我的目标是什么 给定一
  • 在同一活动中将数据片段传递给片段

    我有一些问题想询问有关在活动中将数据从一个片段传递到另一个片段的问题 我不知道该怎么做 有人可以帮忙吗 这是我的代码 ShareFragment java package com example user8 myapplication im
  • C - 两个指针之间的转换行为

    2020年12月11日更新 感谢 一些程序员老兄 在评论中的建议 我的根本问题是我们的团队正在实现动态类型存储引擎 我们分配多个字符数组 PAGE SIZE 缓冲区16 对齐存储动态类型的数据 没有固定的结构 出于效率原因 我们无法执行字节