当前位置: 首页 > 知识库问答 >
问题:

什么是数据对齐?在C中键入指针时,为什么以及何时应该担心?[副本]

申屠亦
2023-03-14

我找不到一个像样的文档来解释对齐系统是如何工作的,以及为什么某些类型比其他类型更严格地对齐。

共有3个答案

哈和惬
2023-03-14

一些系统可以访问部分内存,例如32位字(4字节)。这是一个硬件限制。这意味着进入内存控制器的实际地址应该可以被四整除(因为它仍然在寻址字节)。所以,一旦你试图让一个单词位于不能被四整除的地址,就有两个选择——要么编译器会尝试生成一些奇特的代码,用两次内存访问来组成这个单词,但情况并非总是这样。有时它只会生成一个代码来访问给定地址中的4个字节。然后处理器将因数据对齐错误而失败。

这导致了语言的局限性。

考虑代码(坏代码):

uint8_t a[] = {1,2,3,4,5,6};
uint32_t b = *(uint32_t*)&a[1];

并假设a与可被四个边界整除的边界对齐。然后第二行尝试从第二个元素的地址中读取一个单词,即不能被四整除的地址。这将导致对齐错误。但在C中,严格的别名规则只是禁止使用它。

梁勇
2023-03-14

这是“实现定义”,即对齐要求不是语言规范的一部分。

不同的CPU对对齐有不同的要求。有些不能在不均匀的地址上寻址16位值,有些可以。有些不能寻址浮点值,除非与可由其大小整除的地址对齐,有些可以。依此类推。有些人访问未对齐的数据对象的速度会比正确对齐的数据对象慢,有些人会被未对齐的访问绊倒。

这就是为什么语言标准没有详细说明哪种类型需要以哪种方式对齐(因为它不能),而是将其留给“实现”——在本例中是编译器后端。

如果键入指针,则可能会强制代码在无法寻址的地址寻址给定对象。您需要确保“旧”类型的对齐要求至少与“新”类型的对齐要求一样严格。

在C(C 11以上)中,让alignof操作符告诉您给定类型的对齐要求。您还可以使用alignas操作符在给定类型或对象上强制执行更严格的对齐。

在C(C11以上)中,您可以得到\u Alignof和\u Alignas操作符,这

甄伟兆
2023-03-14

我会试着简短地解释。

计算机的体系结构由处理器和内存组成。内存按单元格组织,因此:

 0x00 |   data  |  
 0x01 |   ...   |
 0x02 |   ...   |

每个存储单元都有指定的大小和可以存储的位数。这取决于架构。

当您在C/C程序中定义变量时,您的程序会占用一个或多个不同的单元格。

例如

int variable = 12;

假设每个单元格包含32位,并且int类型大小为32位,那么在内存中的某个位置:

variable: | 0 0 0 c |  // c is hexadecimal of 12.

当您的CPU必须对该变量进行操作时,它需要将其放入寄存器中。CPU可以从内存中接收“1时钟”的少量位,这种大小通常称为WORD。这个维度也取决于架构。

现在假设您有一个变量,由于某些偏移量,它存储在两个单元格中。

例如,我有两个不同的数据块要存储(我将使用“字符串表示法来更加清楚”):

data1: "ab"
data2: "cdef"

因此,内存将以这种方式组成(2个不同的单元):

|a b c d|     |e f 0 0|

也就是说,data1只占用单元格的一半,因此data2占用剩余部分和第二个单元格的一部分。

现在假设您的CPU想要读取data2。CPU需要2个时钟来访问数据,因为在一个时钟内它读取第一个单元格,在另一个时钟内它读取第二个单元格中的剩余部分。

如果我们根据这个内存示例对齐data2,我们可以在第二个单元格中引入一种填充和移位data2

|a b 0 0|     |c d e f|
     ---
   padding

这样,CPU将只丢失“1个时钟”才能访问data2

align系统只是引入了填充,以便根据体系结构将数据与系统内存对齐。

我不会深入回答这个问题。然而,从广义上讲,记忆对齐来自上下文的要求。

在上面的示例中,使用填充(因此数据与内存对齐)可以节省CPU周期以检索数据。由于内存访问次数较少,这可能会对程序的执行性能产生影响。

然而,除了上面的示例(仅为说明而做)之外,还有许多其他情况下内存对齐是有用的,甚至是需要的。

例如,一些体系结构可能对如何访问内存有严格的要求。在这种情况下,填充有助于分配满足平台约束的内存。

 类似资料:
  • 问题内容: 为什么以及何时应该在php中使用该函数?使用后是否应该始终使用它?我读到我必须使用它来防止会话固定,这是唯一原因吗? 问题答案: 什么啊 就像函数名称所说的那样,它是一个函数,它将用新的ID替换当前的会话ID,并保留当前的会话信息。 它有什么作用? 它主要有助于防止会话固定攻击。会话固定攻击是恶意用户试图利用系统中的漏洞固定(设置)另一个用户的会话ID(SID)的地方。这样,他们将拥有

  • 我知道可以用Scala表示如下: 我明白为什么它是有用的。例如,给定两个函数: 我可以很容易地编写函数<code>getPhoneByUserId(userId:Int),因为<code>选项是一个单子: ... 现在我看到Scala中的< code >应用函子: 我想知道我什么时候应该使用它而不是monad。我想Option和List都是。您能否举一个简单的例子来使用与Option和List一起

  • 当我们尝试实现链表时,我无法理解我们创建节点指针而不是节点结构的原因,如下所示: 和 在这里,为什么我们要将等节点声明为结构指针而不是直接结构

  • 问题内容: 我习惯于进行Java编程,在编程时,您无需真正考虑指针。但是,此刻我正在用C ++编写程序。在创建具有其他类成员的类时,何时应该使用指针,何时不应该使用指针?例如,什么时候我想这样做: 与此相反: 问题答案: 首先避免指针。 在以下情况下使用它们: 您想使用Pimpl习惯用法或抽象工厂。 该实例实际上是由程序的其他部分管理的,而该类仅需要能够访问它。 您想推迟对象的构建(即,您想 在

  • 关于什么时候使用Docker而不是VM的,有什么指导方针吗?(反之亦然) 在我看来,像NGINX、Apache或Redis这样的服务应该是docker,但我不确定是否应该在HPC环境中使用ElasticSearch docker。 Docker总是比VM好吗?

  • 本文向大家介绍什么是数据结构中的指针? 相关面试题,主要包含被问及什么是数据结构中的指针? 时的应答技巧和注意事项,需要的朋友参考一下 其实就是指向一块内存的地址,通过指针传递,可实现复杂的内存访问. 函数指针? 指向一块函数的入口地址. 指针作为函数的参数? 比如指向一个复杂数据结构的指针作为函数变量 这种方法避免整个复杂数据类型内存的压栈出栈操作,提高效率. 注意:指针本身不可变,但指针指向的