C代码中长int值的读写

C代码中长int值的读写,c,io,long-integer,C,Io,Long Integer,我正在研究一种文件格式,这种格式应该在几种不同的操作系统和计算机中写入和读取。这些计算机中的一些应该是x86机器,另一些应该是x86-64。可能存在其他一些处理器,但我还不关心它们 此文件格式应包含以下几个数字: struct LongAsChars{ char c1, c2, c3, c4; }; long readLong(FILE* file){ int b1 = fgetc(file); int b2 = fgetc(file); int b3 = fg

我正在研究一种文件格式,这种格式应该在几种不同的操作系统和计算机中写入和读取。这些计算机中的一些应该是x86机器,另一些应该是x86-64。可能存在其他一些处理器,但我还不关心它们

此文件格式应包含以下几个数字:

struct LongAsChars{
    char c1, c2, c3, c4;
};

long readLong(FILE* file){
    int b1 = fgetc(file);
    int b2 = fgetc(file);
    int b3 = fgetc(file);
    int b4 = fgetc(file);
    if(b1<0||b2<0||b3<0||b4<0){
        //throwError
    }

    LongAsChars lng;
    lng.c1 = (char) b1;
    lng.c2 = (char) b2;
    lng.c3 = (char) b3;
    lng.c4 = (char) b4;

    long* value = (long*) &lng;

    return *value;
}
虽然这似乎在我的计算机上起作用,但我担心它可能在其他计算机上不起作用,或者文件格式可能会在不同的计算机上有所不同(例如,32位计算机与64位计算机)。 我做错什么了吗?我应该如何实现我的代码以使用每个数字的固定字节数


我是否应该使用fread(这也可能使我的代码更快)?

使用
stdint.h
中的类型,以确保输入和输出的字节数相同

然后,您只需要处理endianness问题,而您编写的代码可能没有 真的很好

使用别名char*序列化long会使您在具有不同endianess的平台的写入文件中拥有不同的字节顺序

您应该像这样分解字节:

char c1 = (val >>  0) & 0xff;
char c2 = (val >>  8) & 0xff;
char c3 = (val >> 16) & 0xff;
char c4 = (val >> 24) & 0xff;
然后使用以下方法重新组合:

val = (c4 << 24) |
      (c3 << 16) |
      (c2 <<  8) |
      (c1 <<  0);

<代码> Val=(C4

),您也可能遇到一些问题。为什么不使用类似的或可以处理可能出现的任何可移植性问题的东西?

,而不是使用带有字符的结构,考虑更为数学的方法:

long l  = fgetc() << 24;
     l |= fgetc() << 16;
     l |= fgetc() <<  8;
     l |= fgetc() <<  0;

long l=fgetc()您不想使用long int。在不同的平台上,long int的大小可能不同,因此对于平台无关的格式来说,long l=fgetc()是不合适的。您必须决定文件中需要存储的值的范围。32位可能是最简单的

你说你还不担心其他平台。我认为这意味着你想保留支持它们的可能性,在这种情况下,你应该定义你的文件格式的字节顺序。x86是little-endian,所以你可能认为这是最好的。但big-endian是“标准”交换顺序,因为它用于网络

如果您选择big-endian(“网络字节顺序”):

事实上,您甚至不需要声明两个变量,只是在同一个变量中用其网络顺序等价物替换“value”有点混乱

它之所以有效,是因为“网络字节顺序”被定义为在内存中产生可互换(big-endian)顺序的任何位的排列。不需要弄乱联合,因为C中的任何存储对象都可以被视为字符序列。不需要特殊情况下的endianness,因为这就是ntohl/htonl的用途


如果速度太慢,您可以开始考虑使用SIMD或其他任何工具,对特定于平台的字节交换进行极度优化。或者使用little-endian,前提是您的大多数平台都是little-endian,因此它们之间的“平均”速度更快。在这种情况下,您需要编写或查找“主机到little-endian”和“little endian to host”函数,当然在x86上什么都不做。

我认为最跨体系结构的方法是使用uintXX类型,如stdint.h中所定义的。例如,在x86和x86-64上,int32将为您提供一个32位整数。 我现在在我的所有代码中默认使用这些,并且没有任何问题,因为它们在所有*NIX中都是相当标准的。

假设
sizeof(uint32_t)==4
,有
4!=24
可能的字节顺序,其中小端和大端是最突出的例子,但也使用了其他字节顺序(例如PDP端)

以下是从流中读取和写入32位无符号整数的函数,遵循由表示为字节序列的整数指定的任意字节顺序
0,1,2,3

标题定义了这些原型

_Bool read_uint32(uint32_t * value, FILE * file, uint32_t order);
_Bool write_uint32(uint32_t value, FILE * file, uint32_t order);
这些常数是多少

LITTLE_ENDIAN
BIG_ENDIAN
PDP_ENDIAN
HOST_ORDER

这将以big-endian格式读取文件。这可能是一件好事,但读取整个
然后
bswap
将其存储在内存中会更快。@ephemient:假设您需要bswap(如果您是big-endian呢?)。同时假设bswap工作正常(如果你的long是64位呢?或者你在某台被遗弃的中段机器上呢?)好吧,我在想“
bswap
,如果必要的话”,但这显然不是我写的,我尽量不去想中段机器(它们在过去二十年中存在过吗?)关于
s/bswap/ntohl/
?据我所知,如果给定64位的值,它的常见实现会降低高32位,这是正确的做法。@GMan-您对联合没有同样的问题吗(除非您根据平台的endianess有条件地编译不同的联合定义)?对stdint的引用非常有用,而且会有很大帮助!使用无符号字符或符号扩展会让你头疼。@George-你确定吗?不过,既然你提到了它,我认为如果sizeof(int)_Bool read_uint32(uint32_t * value, FILE * file, uint32_t order); _Bool write_uint32(uint32_t value, FILE * file, uint32_t order);
LITTLE_ENDIAN
BIG_ENDIAN
PDP_ENDIAN
HOST_ORDER