设置char *的缓冲区,中间转换为int *

时间:2014-09-23 11:38:08

标签: c++ c pointers memory

我无法完全理解我在此处阅读的内容的后果:Casting an int pointer to a char ptr and vice versa

简而言之,这会有用吗?

set4Bytes(unsigned char* buffer) {
  const uint32_t MASK = 0xffffffff;
  if ((uintmax_t)buffer % 4) {//misaligned
     for (int i = 0; i < 4; i++) {
       buffer[i] = 0xff;
     } 
  } else {//4-byte alignment
    *((uint32_t*) buffer) = MASK;
  }

}

修改
有一个长时间的讨论(在评论中,神秘地删除了)关于指针应该被铸造到什么类型以检查对齐。现在讨论的主题是here

6 个答案:

答案 0 :(得分:11)

如果您在所有 4个字节中填充相同的值,则此转换是安全的。如果byte order很重要,那么这种转换就不安全了。 因为当您使用整数一次填充 4字节时,它将填充4 Bytes,但顺序取决于endianness

答案 1 :(得分:1)

此代码可能对您有所帮助。它显示通过一次为其内容分配一个字节来构建32位数字,从而强制不对齐。它在我的机器上编译和工作。

#include<stdint.h>
#include<stdio.h>
#include<inttypes.h>
#include<stdlib.h>

int main () {
    uint32_t *data = (uint32_t*)malloc(sizeof(uint32_t)*2);
    char *buf = (char*)data;
    uintptr_t addr = (uintptr_t)buf;
    int i,j;
    i = !(addr%4) ? 1 : 0;
    uint32_t x = (1<<6)-1;
    for( j=0;j<4;j++ ) buf[i+j] = ((char*)&x)[j];

    printf("%" PRIu32 "\n",*((uint32_t*) (addr+i)) );
}

如@Learner所述,必须遵守字节顺序。上面的代码不可移植,并且会在大端机器上中断。

请注意,我的编译器会抛出错误&#34;从'char *'转换为'unsigned int'会丢失精度[-fpermissive]&#34;当尝试将char *转换为unsigned int时,就像在原始帖子中所做的那样。 This post解释说应该使用uintptr_t。

答案 2 :(得分:1)

不,它不适用于所有情况。除了可能存在或可能不存在问题的字节顺序之外,您还假设uint32_t的对齐为4.但此数量是实现定义的(C11草案N1570第6.2.8节)。您可以使用_Alignof运算符以便携方式进行对齐。

其次,buffer指向的位置的有效类型(同上。 Sec.6.5)可能与uint32_t不兼容(例如buffer }指向unsigned char数组)。在这种情况下,一旦尝试读取数组本身或通过不同类型的指针,就会破坏严格的别名规则。

假设指针实际指向unsigned char的数组,则以下代码将起作用

typedef union { unsigned char chr[sizeof(uint32_t)]; uint32_t u32; } conv_t;

void set4Bytes(unsigned char* buffer) {
  const uint32_t MASK = 0xffffffffU;
  if ((uintptr_t)buffer % _Alignof(uint32_t)) {// misaligned
    for (size_t i = 0; i < sizeof(uint32_t); i++) {
      buffer[i] = 0xffU;
    } 
  } else { // correct alignment
    conv_t *cnv = (conv_t *) buffer; 
    cnv->u32 = MASK;
  }
}

答案 3 :(得分:1)

除了 endian 问题之外,这里已经提到过:

CHAR_BIT - 每char的位数 - 也应该考虑。

在大多数平台上都是8,其中for (int i=0; i<4; i++)应该可以正常工作。

更安全的方式是for (int i=0; i<sizeof(uint32_t); i++)

或者,您可以加入<limits.h>并使用for (int i=0; i<32/CHAR_BIT; i++)

答案 4 :(得分:0)

如果您想确保基础数据不“改变形状”,请使用reinterpret_cast<>()

正如Learner所提到的,当您将数据存储在机器内存中时, endianess 成为一个因素。如果您知道数据如何在内存中正确存储(正确的字节顺序)并且您专门测试其布局作为替代表示,那么您可能希望使用reinterpret_cast<>()作为特定类型测试该内存,而无需修改原始存储。

下面,我修改了您的示例以使用reinterpret_cast<>()

void set4Bytes(unsigned char* buffer) {
  const uint32_t MASK = 0xffffffff;
  if (*reinterpret_cast<unsigned int *>(buffer) % 4) {//misaligned
     for (int i = 0; i < 4; i++) {
       buffer[i] = 0xff;
     } 
  } else {//4-byte alignment
    *reinterpret_cast<unsigned int *>(buffer) = MASK;
  }
}

还应该注意,您的函数似乎将缓冲区(32字节的连续内存)设置为0xFFFFFFFF,无论它采用哪个分支。

答案 5 :(得分:-1)

您的代码非常适合使用32位及以上的任何架构。字节排序没有问题,因为所有源字节都是0xFF

在x86或x64机器上,处理最终未对齐RAM访问所需的额外工作由CPU管理,对程序员是透明的(自Pentium II以来),每次访问都会有一些性能成本。所以,如果你只是设置缓冲区的前四个字节几次,那么你就可以简化你的功能了:

void set4Bytes(unsigned char* buffer) {
  const uint32_t MASK = 0xffffffff;
  *((uint32_t *)buffer) = MASK;
}

一些读物:

  1. 关于UNALIGNED MEMORY ACCESSES
  2. 的Linux内核文档
  3. Intel Architecture Optimization Manual, section 3.4
  4. Windows Data Alignment on IPF, x86, and x64
  5. Alexander Sandler的实用“Aligned vs. unaligned memory access