【问题标题】:How to store an integer in a location pointed to by a char*如何将整数存储在 char* 指向的位置
【发布时间】:2013-03-25 07:11:04
【问题描述】:

这可能是一个非常基本的问题,但我不太确定Casting an int pointer to a char ptr and vice versa 的答案是否适用于我的情况。 所以基本上我有以下几点:

void* head = sbrk(1024);  //allocate 1024 bytes in heap   

*((int*)(head+size)) = value;   //value and size are int with valoues between 1 and 1023

我想知道上面的任意大小值是否不起作用,那么大小值的限制是什么?它必须能被 4 整除吗?

【问题讨论】:

  • 我很震惊这甚至编译。指针算法需要一个typed 指针来确定调整地址时的大小差异。 GNU 有一个扩展允许它,所以我相信其他人也这样做,但我仍然被它收回。即使扩展允许它编译,如果大小 notsizeof(int) 的倍数,您也可能会在非 x86 平台上遇到对齐错误。我不能直接代表 GNU 扩展,但它很可能通过将指针视为 char* 来执行 void* 算术。
  • 混用 sbrk() 分配内存已被弃用很长时间,有利于 malloc() 和朋友。你为什么使用 sbrk?

标签: c unix pointers


【解决方案1】:

首先,你不能对 void 指针进行指针运算。该代码甚至不应该编译。

为了便于讨论,让我们假设您有一个 char 指针。然后正式地,这样的强制转换后跟访问是未定义的行为。然而,在现实世界中,如果您可以手动确保对齐,您的代码将始终有效。您必须确保写入的地址位于对齐的内存位置,否则无法保证代码可以正常工作。


EDIT 加上 ISO 9899:2011 标准中的相关引用,为什么 void 指针上的指针算术是未定义的行为:

6.3.2.2 无效

空表达式的(不存在的)值(具有 type void) 不得以任何方式使用,无论是隐式还是显式 转换(无效除外)不适用于此类 表达。

.

6.5.6 加法运算符

/--/

对于加法,两个操作数都应具有算术类型,或者一个 操作数应为指向完整对象类型的指针,而另一个 应为整数类型。 (递增相当于加1。)

.

4 一致性

如果“应”或“不应”要求出现在 违反约束或运行时约束,行为是 不明确的。未定义的行为在此另有说明 国际标准由词语“未定义的行为”或由 省略任何明确的行为定义。没有 这三者的侧重点不同;他们都描述了“行为 那是未定义的''。

违反标准中的规范文本的代码是否“应该编译”当然可以讨论,但我认为讨论对 OP 没有好处。永远不要编写依赖于未定义行为的代码。

【讨论】:

  • “该代码甚至不应该编译”是基于 C 标准中的规则,还是您对编译器“良好”行为的表达?
  • @Eric:C11 草案:“对于加法,两个操作数都应具有算术类型,或者一个操作数应是指向完整对象类型的指针,而另一个应具有整数类型。” (6.5.6/2) 定义了可以添加的指针类型,并且“void 类型包含一组空值;它是一个无法完成的不完整对象类型。” (6.2.5/19) 定义void,并在此过程中取消资格。
  • @CHao:这些语句定义了程序员必须做什么来创建符合标准的程序以及编译器必须接受什么。它们没有指定当不满足要求时编译器必须做什么。也就是说,不要求编译器必须拒绝编译包含带有指向 void 的指针的算术的程序。
  • @EricPostpischil @cHao 引用的第 6.5.6/2 章是相关的,这是我们可以阅读的规范文本:For addition, either both operands shall have arithmetic type, or one operand shall be a pointer to a complete object type and the other shall have integer type. (Incrementing is equivalent to adding 1.)。根据标准第 4 条,违反规范文本中的“应”是未定义的行为。像往常一样,讨论当我们得到 UB 时会发生什么并没有真正的建设性,尤其是当它完全超出 C 语言的范围时。只需让程序远离 UB。
  • 我提出这个问题的原因是您似乎在指定当行为未定义时会发生什么。你是说程序不应该编译。
【解决方案2】:

使用memcpy():

memcpy((char*)head + size, &value, sizeof(value));

【讨论】:

  • 这与已经存在的代码完全相同,除了必要的转换为char*。改用 memcpy 并没有明显的好处。
  • @Lundin 对齐怎么样?
  • 怎么样? memcpy 做它被告知的事情,它没有内置的智能。如果你给它一个未对齐的地址,它会盲目地接受它并尝试写入该地址。
  • @Lundin 但是memcpy() 将成功地从/到未对齐的位置进行复制,而*(int*)misaligned_pointer 在某些架构(例如 MIPS、ARM)上会失败。
  • 其他答案很好且内容丰富,但 memcpy 解决了我认为我在 i386 机器中遇到的对齐问题。
【解决方案3】:

在许多系统上,在这种情况下,要求size 是四的倍数(取决于下面详述的附加条件,包括您的系统上int 的大小为四个字节)。在不需要这个的系统上,它通常是首选。

首先,head 的类型是void *,C 标准没有定义当你用void * 进行指针运算时会发生什么。

一些编译器,特别是 GCC 及其继承者,会将此算术视为类型为 char *。我将在此基础上进行。

其次,我不知道sbrk 返回具有任何特定对齐方式的地址的保证。

让我们假设sbrk 确实返回了一个对齐良好的地址,并且您的 C 实现做了简单的事情来评估 * (int *) (head + size) = value,即发出存储指令来写入 value 的值(转换到int) 到地址head + size

那么你的问题就变成了:我的计算平台用int存储到这个地址做什么?

只要head + size 是适合您平台上int 的地址,商店就会按预期执行。在大多数平台上,四字节整数更喜欢四字节对齐,八字节整数更喜欢八字节对齐。只要head与此首选项的倍数对齐并且size是此首选项的倍数,那么store就会正常执行。

否则,会发生什么取决于您的平台。在某些平台上,硬件执行存储但可能比正常的存储指令执行得更慢,因为它将它分成两个单独的内存写入。 (这也意味着共享相同内存的其他进程可能能够在一部分值已存储但另一部分未存储时读取内存。同样,这取决于您的计算平台的特性。)

在某些平台上,硬件会发出异常信号,中断程序执行并将控制权转移给操作系统。一些操作系统通过分析失败的指令并执行执行预期存储的替代指令来修复未对齐的存储(或者操作系统将异常中继到程序中的特殊代码,可能在自动包含的库中,这些代码执行此修复工作) .在这些平台上,错位的店铺会很慢;它们会极大地降低程序的性能。

在某些平台上,硬件会发出异常信号,并且操作系统不会修复未对齐的存储。相反,操作系统要么终止您的进程,要么向它发送有关问题的信号,这通常会导致您的进程终止。 (其他可能性包括触发调试器或输入您在程序中包含的特殊代码来处理信号。)

【讨论】:

  • “一些编译器,特别是 GCC 及其继承者,会将这个算术视为类型是 char *” 而其他编译器可能会在屏幕上打印一个粉红色的大象。永远不要依赖它,因为它是未定义的行为。 OP 似乎对一般的 C 编程案例感兴趣,而不是特定的未定义行为如何在某个编译器上表现出来。
  • @Lundin:首先,我的回答并没有断言一个人应该依赖这个。其次,这不是未定义的行为。它不是由 C 标准定义的。但它由 GCC 和其他编译器定义。这是一个explicit part of the documentation。人们从哪里得到这样的概念,即如果 C 标准没有定义某些东西,它就是未定义的?废话。该问题被标记为 Unix 并使用sbrk,因此它显然不仅仅使用 C 标准。我清楚地陈述了前提。
  • 未定义行为 == 标准未定义。这是同一件事。这是未定义的行为,因为它违反了 6.5.6 的规范文本。所有违反规范性文本的行为都是未定义的行为。请参阅添加到我的答案中的标准引用。
  • @Lundin:如果您想使用“未定义的行为”来表示 C 标准未定义,那么您的评论与此答案无关。问题和我的答案都不是完全基于 C 标准。我不明白为什么人们试图将每个涉及 C 的问题都拖回 C 标准。我们在软件工程中使用很多很多文档:硬件手册、编译器文档、操作系统文档。当有人询问 C 和 Unix 的结合并显示语言扩展的迹象时,将答案仅限于 C 是没有意义的。
  • @EricPostpischil:明确确实语言限制为仅针对C的答案是有意义的,因为问题是关于C而不是GCC 的超集。 GCC 不是在 *nix 系统(尤其是严格的 Unix 系统)上使用的唯一编译器,并且“unix”标签并不自动意味着可以抛出所有 GCC 的非标准废话。库可以包含标准中没有的东西(尤其是 POSIX 的东西),但语言本身应该符合标准。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-10-12
  • 1970-01-01
  • 1970-01-01
  • 2016-11-11
相关资源
最近更新 更多