c 中的 fwrite() 以不同的顺序写入字节

fwrite() in c writes bytes in a different order

#include <stdio.h>
#include <stdlib.h>


int main(void)
{
    int *int_pointer = (int *) malloc(sizeof(int));

    // open output file
    FILE *outptr = fopen("test_output", "w");
    if (outptr == NULL)
    {
        fprintf(stderr, "Could not create %s.\n", "test_output");
        return 1;
    }

    *int_pointer = 0xabcdef;

    fwrite(int_pointer, sizeof(int), 1, outptr);

    //clean up
    fclose(outptr);
    free(int_pointer);

    return 0;
}

这是我的代码,当我看到带有 xxd 的 test_output 文件时,它给出了以下输出。

$ xxd -c 12 -g 3 test_output 
0000000: efcdab 00                    ....

我希望它打印 abcdef 而不是 efcdab。

使用htonl()

它将 host-byte-order 是什么(您机器的字节顺序)转换为网络字节顺序。因此,无论您 运行 在什么机器上,都将获得相同的字节顺序。使用这些调用是为了无论您 运行 在哪个主机上,字节都以正确的顺序通过网络发送,但它也适用于您。

参见 htonl and byteorder 的手册页。有各种可用的转换函数,也适用于不同的整数大小,16 位、32 位、64 位 ...

#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>

int main(void) {
    int *int_pointer = (int *) malloc(sizeof(int));

    // open output file
    FILE *outptr = fopen("test_output", "w");
    if (outptr == NULL) {
        fprintf(stderr, "Could not create %s.\n", "test_output");
        return 1;
    }

    *int_pointer = htonl(0xabcdef);  // <====== This ensures correct byte order

    fwrite(int_pointer, sizeof(int), 1, outptr);

    //clean up
    fclose(outptr);
    free(int_pointer);

    return 0;
}

你在看哪本书?此代码中存在许多问题,例如将 malloc 的 return 值转换为......最重要的是,考虑使用整数类型的缺点,该整数类型的大小和表示形式可能因系统而异系统.

  • 保证 int 能够存储介于 -32767 和 32767 之间的值。 您的实现可能允许更多值,但要 便携并且对使用古老编译器的人友好,比如Turbo C(有很多),你不应该使用int来存储大于 32767 (0x7fff) 的值,例如 0xabcdef。当执行这样的out-of-range转换时,结果是implementation-defined;它可能涉及 saturation, wrapping, trap representations or raising a signal corresponding to computational error,例如,两者中的后者可能会在以后导致未定义的行为。
  • 您需要转换成agreed-upon字段格式。当通过写方式发送数据,或者将数据写入文件以传输到其他系统时,它是重要的是要商定通信协议。这包括对整数字段使用相同的大小和表示。输出和输入都应该跟一个翻译函数(分别是序列化和反序列化)。
  • 您的字段是二进制的,因此您的文件应该以二进制模式打开。 例如,使用 fopen(..., "wb") 而不是 "w"。在某些情况下,'\n' 个字符可能会被翻译成一对 \r\n 个字符,否则; Windows 系统因此而臭名昭著。你能想象这会造成什么样的破坏和混乱吗?我可以,因为 I've answered a question about this problem.

也许 uint32_t 可能是更好的选择,但我会选择 unsigned long,因为 uint32_t 不一定存在。关于这一点,对于没有 htonl 的系统(根据 POSIX,returns uint32_t),该功能可以像这样实现:

uint32_t htonl(uint32_t x) {
    return (x & 0x000000ff) << 24
         | (x & 0x0000ff00) << 8
         | (x & 0x00ff0000) >> 8
         | (x & 0xff000000) >> 24;
}

作为受上述 htonl 函数启发的示例,请考虑以下宏:

typedef unsigned long ulong;
#define serialised_long(x)   serialised_ulong((ulong) x)
#define serialised_ulong(x)    (x & 0xFF000000) / 0x1000000 \
                             , (x & 0xFF0000)   / 0x10000   \
                             , (x & 0xFF00)     / 0x100     \
                             , (x & 0xFF)

typedef unsigned char uchar;
#define deserialised_long(x) (x[3] <= 0x7f \
                                    ? deserialised_ulong(x) \
                                    : -(long)deserialised_ulong((uchar[]) { 0x100  - x[0] \
                                                                          , 0xFF   - x[1] \
                                                                          , 0xFF   - x[2] \
                                                                          , 0xFF   - x[3] })
#define deserialised_ulong(x) ( x[0] * 0x1000000UL \
                              + x[1] * 0x10000UL   \
                              + x[2] * 0x100UL     \
                              + x[3]               )

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    FILE *f = fopen("test_output", "wb+");
    if (f == NULL)
    {
        fprintf(stderr, "Could not create %s.\n", "test_output");
        return 1;
    }
    
    ulong value = 0xABCDEF;
    unsigned char datagram[] = { serialised_ulong(value) };
    fwrite(datagram, sizeof datagram, 1, f);
    printf("%08lX serialised to %02X%02X%02X%02X\n", value, datagram[0], datagram[1], datagram[2], datagram[3]);
    
    rewind(f);
    
    fread(datagram, sizeof datagram, 1, f);
    value = deserialised_ulong(datagram);
    printf("%02X%02X%02X%02X deserialised to %08lX\n", datagram[0], datagram[1], datagram[2], datagram[3], value);
    
    fclose(f);
    return 0;
}