如何在C语言中将一个由4个uint8_t元素组成的数组转换为uint32_t类型

9

我正在尝试将一个uint8_t数组转换为一个uint32_t数组,但似乎无法正常工作。
有人能帮我吗?我需要将uint8_t值转换为uint32_t。
我可以通过移位来实现这一点,但我认为还有更简单的方法。

uint32_t *v4full;
v4full=( uint32_t *)v4;
while (*v4full) {
    if (*v4full & 1)
        printf("1");
    else
        printf("0");

    *v4full >>= 1;
}
printf("\n");

你能展示一下源数组的构建吗? - bvj
抱歉,我更正了类型错误。源数组构造 in4_pton(token,-1,&v4,-1,&c); 这将把字符串转换为IP地址并将其放入v4数组中。 - user2714949
好的。不要将指针强制转换为32位字,以免程序误认为它正在查看32位单词。如果您需要使用int,请迭代原始的8位字节向量,然后将字节赋给简单的int变量;例如,for(int n=0; n < 4; n ++){ int i =(int)v4 [n]; ...}除非您希望将整个向量呈现为一个组合的32位字... - bvj
只是为了澄清:我们可以将v4看作是uint8_t v4 [4],您想将其作为单个32位数字获取吗? - Floris
6个回答

24

考虑到需要将uint8_t值转换为uint32_t,并且在in4_pton()函数中的规格...

尝试使用可能的字节顺序修正进行操作:

uint32_t i32 = v4[0] | (v4[1] << 8) | (v4[2] << 16) | (v4[3] << 24);

1
你的猜测和我的一样好……如果他需要字节顺序反转,你的答案是正确的(加1以防万一)。如果不需要,我的略微更紧凑。这可能取决于机器架构(大/小端…)。 - Floris

8

您的示例存在问题-实际上是您尝试做的事情有问题(因为您不想进行转换)。

您知道吗,其实这是一个鲜为人知的事实,但您不能以这种方式切换指针类型

具体来说,像这样的代码是非法的:

type1 *vec1=...;
type2 *vec2=(type2*)vec1;
// do stuff with *vec2

仅当type2为char(或unsigned char或const char等)时,此操作才合法。但如果type2是其他类型(如您示例中的uint32_t),则违反了标准,并且如果使用-O2或-O3优化编译,则可能引入代码bug。
这被称为“强类型别名规则”,它允许编译器假定不同类型的指针永远不会指向内存中的相关点 - 因此,如果更改一个指针的内存,编译器无需重新加载所有其他指针。
编译器很难找到违反此规则的实例,除非您明确告诉它。例如,如果将代码更改为执行以下操作:
uint32_t v4full=*((uint32_t*)v4);

如果使用-O3 -Wall进行编译(我使用的是gcc),你会收到以下警告信息:
warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]

所以你不能避免使用shift键。
注意:在较低的优化设置下,它将正常工作,并且如果您从未将信息指针更改为“v4”和“v4_full”,它也将在较高的设置中正常工作。它可以工作,但仍然是一个错误,仍然“违反规则”。

4
如果v4full是一个指针,那么这行代码如下:
uint32_t *v4full;
v4full=( uint32_t)&v4;

应该抛出一个错误或至少编译器警告。也许您的意思是?
uint32_t *v4full;
v4full=( uint32_t *) v4;

我假设v4本身就是一个指向uint8数组的指针。我意识到我是在从不完整的信息中进行推断... 编辑自上面似乎已经解决了一个错别字,让我们再试一次。
下面的代码片段可以按预期工作 - 也是我认为你希望你的代码工作的方式。请评论一下 - 这段代码有什么问题吗?
#include <stdio.h>
#include <inttypes.h>

int main(void) {
    uint8_t v4[4] = {1,2,3,4};
    uint32_t *allOfIt;
    allOfIt = (uint32_t*)v4;
    printf("the number is %08x\n", *allOfIt);
}

输出:

the number is 04030201

注意 - 打印数字时字节的顺序是反向的,您会得到04030201而不是您可能期望/想要的01020304。这是因为我的机器(x86体系结构)是小端字节序。如果您想确保字节的顺序符合您的要求(换句话说,元素[0]对应于最高有效字节),最好使用@bvj的解决方案-将每个四个字节移入32位整数的正确位置。同样,如果需要(告诉编译器使用CPU的内置指令),可以参见此前的回答中非常有效的方法。

您的印象是,OP希望将整个数组作为32位字的指针进行取消引用吗? - bvj
但是由于v4数组是IPv4地址的二进制表示,可能存在字节顺序问题。 - bvj
3
提醒一下,严格按照c99标准,对于allOfIt = (uint32_t*)v4这行代码是不合法的。它可能会在未来引入bug,甚至在目前较高优化的编译器上也可能存在问题。请注意修改。 - rabensky
@cluracan 我不知道 - 我的 C 语言比 '99 年要老一些... 我想你仍然可以将两种类型进行 union 合并吧?那需要再写几行代码... - Floris
@Floris Union目前仍然有效,但也违反了标准(因为您将写入一个成员并从另一个成员读取,这在技术上是不允许的)。不幸的是,C和C++都正在朝着禁止任何“智能”低级实现的方向发展(我讨厌的另一个例子:有符号溢出是未定义行为,不被允许:( )。我相信他们有他们的理由,但我不喜欢它:( - rabensky
显示剩余2条评论

4
另一个使这段代码不可移植的问题是,许多体系结构需要将 uint32_t 对齐在四字节边界上,但允许 uint8_t 具有任何地址。如果在未正确对齐的数组上调用此代码,则会导致未定义的行为,如使用 SIGBUS 引起程序崩溃。在这些机器上,将任意 uint8_t[] 转换为 uint32_t[] 的唯一方法是 memcpy() 内容。(如果您使用四字节块执行此操作,则编译器应优化为根据您的架构选择非对齐加载或两次加载和一次移位中的更有效的操作。)
如果您可以控制源数组的声明,则可以使用 #include <stdalign.h> 并声明 alignas(uint32_t) uint8_t bytes[]。经典解决方案是将字节数组和 32 位值都声明为 union 的成员,并在它们之间进行类型转换。使用从 malloc() 获取的指针也是安全的,因为这些指针保证具有适当的对齐方式。

0
这是一个解决方案:
/* convert character array to integer */
uint32_t buffChar_To_Int(char *array, size_t n){
    int number = 0;
    int mult = 1;

    n = (int)n < 0 ? -n : n;       /* quick absolute value check  */

    /* for each character in array */
    while (n--){
        /* if not digit or '-', check if number > 0, break or continue */
        if((array[n] < '0' || array[n] > '9') && array[n] != '-'){
            if(number)
               break;
            else
               continue;
        }

        if(array[n] == '-'){      /* if '-' if number, negate, break */
            if(number){
               number = -number;
               break;
            }
        }
        else{      /* convert digit to numeric value   */
            number += (array[n] - '0') * mult;
            mult *= 10;
        }
    }
    return number;
}

-1

另外一种解决方案:

u32 ip;

if (!in4_pton(str, -1, (u8 *)&ip, -1, NULL))
      return -EINVAL;

... use ip as it defined above - (as variable of type u32)

在这里,我们使用in4_pton函数的结果(ip),没有任何额外的变量和类型转换。


3
请解释你的代码做了什么以及为什么它会解决问题。只有代码的答案(即使它可以工作)通常不能帮助提问者理解他们的问题。 - SuperBiasedMan
@SuperBiasedMan:请看问题的第二条评论 - user2714949 根据使用 in4_pton 的结果提出了问题。我的答案是另一种方法,可以在不使用额外变量和转换的情况下使用此函数。 - dimcha

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