将4元素字符数组复制为C中的整数
char是1个字节,整数是4个字节。 我想逐字节地从char [4]复制到整数。 我想到了不同的方法,但我得到了不同的答案。
char str[4]="abc"; unsigned int a = *(unsigned int*)str; unsigned int b = str[0]<<24 | str[1]<<16 | str[2]<<8 | str[3]; unsigned int c; memcpy(&c, str, 4); printf("%u %u %u\n", a, b, c);
输出为6513249 1633837824 6513249
哪一个是正确的? 出了什么问题?
这是一个字节序问题。 当您将char*
解释为int*
时,字符串的第一个字节成为整数的最低有效字节(因为您在x86上运行此代码是小端),而使用手动转换时,第一个字节变为最重要的。
把它放到图片中,这是源数组:
abc \0 +------+------+------+------+ | 0x61 | 0x62 | 0x63 | 0x00 | <---- bytes in memory +------+------+------+------+
当这些字节被解释为小端架构中的整数时,结果为0x00636261
,即十进制6513249.另一方面,手动放置每个字节会产生0x61626300
- 十进制1633837824。
当然将char*
视为int*
是未定义的行为,因此差异在实践中并不重要,因为您实际上并不允许使用第一次转换。 然而,有一种方法可以实现相同的结果,称为类型惩罚 :
union { char str[4]; unsigned int ui; } u; strcpy(u.str, "abc"); printf("%u\n", u.ui);
前两个都不正确。
第一个违反了别名规则,可能会失败,因为str
的地址未正确对齐unsigned int
。 要使用主机系统字节顺序将字符串的字节重新解释为unsigned int
,可以使用memcpy
复制它:
unsigned int a; memcpy(&a, &str, sizeof a);
(假设unsigned int
的大小和str
的大小相同。)
第二个可能因整数溢出而失败,因为str[0]
被提升为int
,因此str[0]<<24
具有int
类型,但shift所需的值可能大于int
可表示的值。 要解决此问题,请使用:
unsigned int b = (unsigned int) str[0] << 24 | …;
第二种方法以big-endian顺序解释str
中的字节,而不管主机系统中unsigned int
的字节顺序如何。
unsigned int a = *(unsigned int*)str;
此初始化不正确并调用未定义的行为。 它违反了C别名规则,可能会违反处理器对齐方式。
你说你想逐字节复制。
这意味着行unsigned int a = *(unsigned int*)str;
不被允许。 但是,您正在做的是将数组作为不同类型读取的相当常见的方式(例如,当您从磁盘读取流时)。
它只需要一些调整:
char * str ="abc"; int i; unsigned a; char * c = (char * )&a; for(i = 0; i < sizeof(unsigned); i++){ c[i] = str[i]; } printf("%d\n", a);
请记住,您正在阅读的数据可能与您正在阅读的机器不具有相同的字节顺序。 这可能有所帮助:
void changeEndian32(void * data) { uint8_t * cp = (uint8_t *) data; union { uint32_t word; uint8_t bytes[4]; }temp; temp.bytes[0] = cp[3]; temp.bytes[1] = cp[2]; temp.bytes[2] = cp[1]; temp.bytes[3] = cp[0]; *((uint32_t *)data) = temp.word; }
两者都是正确的:
-
您的第一个解决方案以本机字节顺序(即CPU使用的字节顺序)进行复制,因此可能会根据CPU的类型给出不同的结果。
-
无论CPU使用什么,您的第二个解决方案都会以大端字节顺序(即最低地址的最高有效字节)进行复制。 它将在所有类型的CPU上产生相同的值。
什么是正确的取决于如何解释原始数据(char数组)。
例如,Java代码(类文件)总是使用大端字节顺序(无论CPU使用什么)。 因此,如果要从Java类文件中读取int
,则必须使用第二种方法。 在其他情况下,您可能希望使用CPU依赖的方式(我认为Matlab以原生字节顺序将int
写入文件,参见此问题 )。
如果您使用CVI(National Instruments)编译器,您可以使用扫描function执行此操作:
unsigned int a;
对于大端:扫描(str,“%1i [b4uzi1o3210]>%i”,&a);
对于小端:扫描(str,“%1i [b4uzi1o0123]>%i”,&a);
o修饰符指定字节顺序。 我在方括号内指示在str数组中的起始位置。