创建一个指向8位数组的32位指针(C语言)

4

我有一个缓冲区,其中每个条目的大小为8位:

uint8_t Buffer[10] = {0x12,0x34,0x56,0x78,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6};

我需要做的是创建指向该数组的指针,例如16位和32位指针。举个例子:
uint32_t *x;
x = Buffer;

uint32_t *y;
y = Buffer+4;

uint16_t *z;
z = Buffer+8;

然后每个变量将从数组中读取,例如:

x = 0x78563412
y = 0xf4f3f2f1
z = 0xf6f5

这段代码在功能上完全正常,但是我收到了关于不兼容指针类型的警告。所以我想知道是否有其他方法来解决这个问题,或者我只能接受这些警告?或者我完全错误地编写了这段代码?

请注意,此代码将在单一类型的平台上执行,其中字节顺序始终相同,并且数据类型的大小始终相同。


你可以通过显式转换来消除警告。此外,你的代码暗示int是32位的,你可能想要使用uint32_t来确保。你也可以先将其转换为int*,然后使用+1而不是+4,这样看起来更好(或者使用数组语法)。 - Karsten Koop
1
char 不一定是8位。如果您需要固定宽度,请使用 uint8_t 和其他固定宽度类型。这样,您就不必解释类型的长度。此外,您的代码可能会引发未定义的行为。 - too honest for this site
请使用适当的位移编组/序列化!已经有很多关于这个问题的提问了。 - too honest for this site
3个回答

7
你应该注意警告;你所做的是未定义行为。像这样的类型别名是未定义行为,特别是因为没有保证Buffer具有正确的对齐方式,以便可以将其作为int/short进行访问。如果Buffer具有正确的对齐方式,则可以直接进行显式转换,这样就可以了(并且警告会消失)。
你有两个选择:
一、将缓冲区对齐为两种类型中较大的那种。要小心指针算术不要扰乱对齐方式:
#include <stdalign.h>

alignas(int) unsigned char Buffer[10] = {0x12,0x34,0x56,0x78,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6};

unsigned int *x;
x = (unsigned int*)(Buffer);

unsigned int *y;
y = (unsigned int*)(Buffer+4);

unsigned short *z;
z = (unsigned short*)(Buffer+8);

第二种方法是创建一个unsigned int/unsigned short变量,然后将你感兴趣的字节memcpy到该变量中:

unsigned int x;
memcpy(&x, Buffer, 4);

unsigned int y;
memcpy(&y, Buffer + 4, 4);

unsigned short z;
memcpy(&z, Buffer + 8, 2);

问题在于我需要尽可能少地使用RAM空间。这就是为什么我试图将所有值存储在缓冲区中,该缓冲区定期写入ROM内存,然后只需将变量指向该缓冲区。我可能想错了。但基本上我想做的是,而不是像Buffer [0] << 8 | Buffer [1]这样做某事。我宁愿让short x指向Buffer [0]和Buffer [1]。 - gislibergur
@gislibergur:我增加了关于对齐的部分。 - Cornstalks
2
C语言中没有reinterpret_cast - alk
1
@alk:哎呀,我切换到了C++模式...现在已经更正了,谢谢。 - Cornstalks
1
通过malloc()(例如动态分配),分配缓冲区将保证内存对于任何类型的使用都是对齐的。 - alk
显示剩余2条评论

2
你的方法存在问题,它假设底层硬件有特定的字节序。不同的计算机会解释一系列十六进制字节。
01 23 45 67

作为eiter
01234567 or 67452301

你的程序可能在两个系统上都可以编译和运行,但由于结果是硬件特定的,编译器必须警告你可能会出现问题。

强制使用特定字节序的正确方法是使用整数数组,使用hton和ntoh函数进行转换,并使用指向unsigned char *的指针直接设置单个字节,或者使用memcpy函数。


1
你忘记了标准整数类型的对齐和比特宽度。 - too honest for this site

2

你可能想要使用联合(union)

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

typedef union{

    uint8_t Buffer[10];
    struct{
        uint32_t x;
        uint32_t y;
        uint16_t z;
    };

}MYSTRUCT;


int main(){

    MYSTRUCT b = {0x12,0x34,0x56,0x78,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6};

    printf("x=%#x y=%#x z=%#x\n",b.x,b.y,b.z);

 return 0;

}

没有保证一个 struct 不会包含填充字节。更不用说其他问题了。 - too honest for this site
1
通常我会使用类似于“attribute((packed))”这样的东西,具体取决于编译器。 - Djole
__attribute__#pragma packed等都不是标准的C语言,也不能被编译器保证。此外,在某些平台上,它们会产生大量的代码开销,因为每次访问较长的变量都会被分成多个字节读取以允许未对齐。最糟糕的情况是,它们会导致未对齐访问的崩溃/错误执行。更不用说其他问题了。总之:千万别用! - too honest for this site
2
不,这不是标准的C语言 - 这就是为什么我没有把它放在代码中的原因。在某些平台上,它会产生巨大的开销,但问题是在这些平台上,您可能不需要处理一个单一缓冲区中保存整数变量(提到了RAM节省而不是拥有更少的指令或更快)。我知道这不是最便携式的C代码,但类似的东西在使用8位PIC等设备时非常方便。 - Djole
PIC和其他8位处理器通常没有对齐要求。在这些平台上,使用位移进行适当的编组通常与非标准转换相比,不会增加任何开销,前提是使用良好的编译器。问题出现在例如ARM Cortex-M等处理器上,它们要么需要对齐访问,要么使用非最优访问(例如将非对齐字读取分成三个1/2/1字节读取)。无论哪种方式都是一个坏主意。通常,您只需进行一次序列化,然后使用更宽的类型进行操作,因此对于大多数访问而言,这是一个坏主意。此外,人们也不希望有... - too honest for this site
在数据处理的所有层面上都使用union,但仅在低级驱动程序中使用。您也可以为小型系统编写这样的代码。但是,您仍然会忽略其他问题。 - too honest for this site

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接