【问题标题】:fwrite() in c writes bytes in a different orderc 中的 fwrite() 以不同的顺序写入字节
【发布时间】:2017-06-15 05:27:45
【问题描述】:
#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。

【问题讨论】:

标签: c file memory malloc endianness


【解决方案1】:

你在看哪本书?这段代码中存在许多问题,例如转换 malloc 的返回值......最重要的是,考虑使用整数类型的缺点,它的大小和表示可能因系统而异。

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

也许uint32_t 可能是更好的选择,但我会选择unsigned long,因为uint32_t 不能保证存在。需要注意的是,对于没有 htonl(根据 POSIX 返回 uint32_t)的系统,该函数可以这样实现:

uint32_t htonl(uint32_t x) {
    return (x & 0xFF)       * 0x1000000
         + (x & 0xFF00)     * 0x100
         + (x & 0xFF0000)   / 0x100
         + (x & 0xFF000000) / 0x1000000;
}

作为受上述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;
}

【讨论】:

    【解决方案2】:

    使用htonl()

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

    参见htonlbyteorder 的手册页。有多种转换函数可用,也适用于不同的整数大小,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;
    }
    

    【讨论】:

    • htonl() 和 htons() 函数应该返回从主机到网络字节顺序转换的参数值。 你明白这对于使用 的系统意味着什么吗?内部网络字节顺序?另外,我强烈推荐POSIX manpages 而不是 Linux 联机帮助页; Linux 对 C 是什么或不是什么没有发言权。
    • @Seb - 我错过了什么? stackoverflow.com/questions/32205546/…
    • htonl 契约是参数是主机字节顺序,返回值是网络字节顺序。这与内部表示无关。以函数strto* 为例,它们的参数是一个字符串,返回值是一个由该字符串(或0)表示的整数。再一次,这与内部表示无关。 hton*ntoh* 的 POSIX 手册页中没有合同义务,即从 内部表示 到主机或网络字节顺序的状态转换......
    • 您的错误是混淆了主机字节顺序(绝对的)和内部表示(相对的,因系统而异)。 . 手册页根本没有提到内部表示!
    • 他的@Seb,我不明白所有合同的含义,如果有情况它不起作用,但我的理解是 htonl() 做正确的事。 tutorialspoint.com/unix_sockets/network_byte_orders.htm
    猜你喜欢
    • 2017-04-24
    • 2020-08-23
    • 1970-01-01
    • 1970-01-01
    • 2021-03-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-01-10
    相关资源
    最近更新 更多