我的目标是用四个字节保存一个long
,如下所示:
unsigned char bytes[4];
unsigned long n = 123;
bytes[0] = (n >> 24) & 0xFF;
bytes[1] = (n >> 16) & 0xFF;
bytes[2] = (n >> 8) & 0xFF;
bytes[3] = n & 0xFF;
但我希望代码是可移植的,所以我使用<limits.h>
:中的CHAR_BIT
unsigned char bytes[4];
unsigned long n = 123;
bytes[0] = (n >> (CHAR_BIT * 3)) & 0xFF;
bytes[1] = (n >> (CHAR_BIT * 2)) & 0xFF;
bytes[2] = (n >> CHAR_BIT) & 0xFF;
bytes[3] = n & 0xFF;
问题是比特掩码0xFF
只占八个比特,这不一定等同于一个字节。有没有一种方法可以使上层代码完全可移植到所有平台?
像这样的东西怎么样
unsigned long mask = 1;
mask<<=CHAR_BIT;
mask-=1;
然后用它作为掩模而不是CCD_ 5?
测试程序:
#include <stdio.h>
int main() {
#define MY_CHAR_BIT_8 8
#define MY_CHAR_BIT_9 9
#define MY_CHAR_BIT_10 10
#define MY_CHAR_BIT_11 11
#define MY_CHAR_BIT_12 12
{
unsigned long mask = 1;
mask<<=MY_CHAR_BIT_8;
mask-= 1;
printf("%lxn", mask);
}
{
unsigned long mask = 1;
mask<<=MY_CHAR_BIT_9;
mask-= 1;
printf("%lxn", mask);
}
{
unsigned long mask = 1;
mask<<=MY_CHAR_BIT_10;
mask-= 1;
printf("%lxn", mask);
}
{
unsigned long mask = 1;
mask<<=MY_CHAR_BIT_11;
mask-= 1;
printf("%lxn", mask);
}
{
unsigned long mask = 1;
mask<<=MY_CHAR_BIT_12;
mask-= 1;
printf("%lxn", mask);
}
}
输出:
ff
1ff
3ff
7ff
fff
我几乎只与嵌入式系统合作,在那里我经常不得不在各种不同的系统之间提供可移植的代码。就像写代码一样,它既可以在一些小的8位MCU上工作,也可以在x8_64上工作。
但即使对我来说,为移植到异国情调的过时DSP系统等而烦恼也是一种巨大的时间浪费。这些系统在现实世界中几乎不存在——为什么你需要它们的可移植性?除了";炫耀";大部分无用的语言律师的知识C?根据我的经验,99%的这种无用的可移植性问题都归结为程序员"炫耀";,而不是实际的需求规范。
即使你出于某种奇怪的原因确实需要这样的可移植性,这个任务一开始也没有任何意义,因为char
和long
都不是可移植的!如果char
不是8位,那么是什么让你认为long
是4字节?它可以是2个字节,可以是8个字节,也可以是其他的东西。
如果可移植性是一个实际问题,那么您必须使用stdint.h
。然后,如果你真的必须支持异国情调的系统,你必须决定哪一个。据我所知,现实世界中唯一使用不同字节大小的计算机是20世纪90年代各种过时的奇异TI DSP,它们使用16位字节/字符。让我们假设这是你的既定目标,你已经决定支持它很重要。
我们还假设存在一个标准的C编译器(ISO 9899(,用于那个奇异的目标,这是极不可能的。(更可能的情况是,你会得到一个一致性很差、大多已损坏的遗留C90……甚至更有可能是那些使用目标的人在汇编程序中编写所有内容。(对于标准C编译器,它不会实现uint8_t
,因为如果目标不支持它,它就不是强制类型。只有uint_least8_t
和uint_fast8_t
是强制类型。
然后你会这样做:
#include <stdint.h>
#include <limits.h>
#if CHAR_BIT == 8
static void uint32_to_uint8 (uint8_t dst[4], uint32_t u32)
{
dst[0] = (u32 >> 24) & 0xFF;
dst[1] = (u32 >> 16) & 0xFF;
dst[2] = (u32 >> 8) & 0xFF;
dst[3] = (u32 >> 0) & 0xFF;
}
#endif
// whatever other conversion functions you need:
static void uint32_to_uint16 (uint16_t dst[2], uint32_t u32){ ... }
static void uint64_to_uint16 (uint16_t dst[2], uint32_t u32){ ... }
然后,奇异DSP将使用uint32_to_uint16
功能。您可以使用相同的编译器#if CHAR_BIT
检查来执行#define byte_to_word uint32_to_uint16
等
然后还应该立即注意到endianes将是下一个主要的可移植性问题。我不知道过时的DSP经常使用什么endianes,但这是另一个问题。
关于:
unsigned long mask = (unsigned char)-1;
这将起作用,因为C标准在6.3.1.3p2 中规定
1当具有整数类型的值转换为另一个整数类型时除了_Bool之外,如果值可以由新类型表示,则不变。
2否则,如果新类型是无符号的,则值由重复地加或减一个以上的最大值可以用新类型表示,直到值在新类型。
并且unsigned long
可以表示unsigned char
的所有值。
#define CHARMASK ((1UL << CHAR_BIT) - 1)
int main(void)
{
printf("0x%xn", CHARMASK);
}
掩码将始终具有字符的宽度。计算的编译时间,不需要额外的变量。
或
#define CHARMASK ((unsigned char)(~0))
你也可以不戴口罩
void foo(unsigned int n, unsigned char *bytes)
{
bytes[0] = ((n << (CHAR_BIT * 0)) >> (CHAR_BIT * 3));
bytes[1] = ((n << (CHAR_BIT * 1)) >> (CHAR_BIT * 3));
bytes[2] = ((n << (CHAR_BIT * 2)) >> (CHAR_BIT * 3));
bytes[3] = ((n << (CHAR_BIT * 3)) >> (CHAR_BIT * 3));
}
int main(void)
{
unsigned int z = 0xaabbccdd;
unsigned char bytes[4];
foo(z, bytes);
printf("0x%02x 0x%02x 0x%02x 0x%02xn", bytes[0], bytes[1], bytes[2], bytes[3]);
}