序列化整数到大端的存储

时间:2016-10-20 13:27:51

标签: c serialization endianness

我想遵循Rob Pikes的指导原则并将整数存储到磁盘而不必担心有关endianess的问题。所以,这是我的测试代码:

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

typedef uint8_t   byte;

uint32_t _wireto32(byte *data) {
  uint32_t i =
    ((uint32_t)data[3]<<0)  |
    ((uint32_t)data[2]<<8)  |
    ((uint32_t)data[1]<<16) |
    ((uint32_t)data[0]<<24);
  return i;
}

void _32towire(uint32_t i, byte *data) {
  data[0] = (i >> 24) & 0xFF;
  data[1] = (i >> 16) & 0xFF;
  data[2] = (i >>  8) & 0xFF;
  data[3] =  i        & 0xFF;
}

void _dump(char *n, byte *d, size_t s, uint64_t N) {
  int l = strlen(n) + 9;
  fprintf(stderr, "%s (len: %ld, Num: %ld): ", n, s, N);
  size_t i;
  int c;
  for (i=0; i<s; ++i) {
    fprintf(stderr, "%02x", d[i]);
    if(i % 36 == 35 && i > 0) {
      fprintf(stderr, "\n");
      for(c=0; c<l; ++c)
        fprintf(stderr, " ");
    }
  }
  fprintf(stderr, "\n");
}

int main(int argc, char **argv) {
  FILE *fd = NULL;
  uint32_t n_orig = 20160809;
  uint8_t b[4];
  uint32_t n_new;

  if(argc != 2) {
    fprintf(stderr, "Usage: util w|r\n");
    exit(1);
  }

  switch(argv[1][0]) {
    case 'w':
      if((fd = fopen("buffer.b", "wb+")) == NULL) {
        perror("unable to write buffer.b");
        return 1;
      }

      _32towire(n_orig, b);

      fwrite(b, 4, 1, fd);
      close(fd);

      _dump("out", b, 4, n_orig);

      break;

    case 'r':
      if((fd = fopen("buffer.b", "rb+")) == NULL) {
        perror("unable to open read buffer.b");
        return 1;
      }

      if((fread(b, 1, 4, fd)) <=0)  {
        perror("unable to read from buffer.b");
        return 1;
      }

      close(fd);

      n_new = _wireto32(b);

      _dump(" in", b, 4, n_new);

  }

  return 0;
}

当我在x86系统上运行时,一切看起来都很好:

% ./util w && ./util r
out (len: 4, Num: 20160809): 0133a129
 in (len: 4, Num: 20160809): 0133a129

现在如果我将输出文件传输到big-endian系统(在我的情况下是powerpc上的aix),我得到:

./util r
 in (len: 4, Num: 0): 0133a129

所以,我显然忽视了一些事情。有没有人有想法?

谢谢, 汤姆

3 个答案:

答案 0 :(得分:2)

如果您想知道为什么大端系统会打印Num: 0,那就是因为您的_dump函数取uint64_t N而不是32位。在大端机器上,4个最重要的字节是0。

答案 1 :(得分:2)

gcc -maix32说:

tom.c:27:3: warning: format '%ld' expects argument of type 'long int',
but argument 5 has type 'uint64_t' [-Wformat=]
fprintf(stderr, "%s (len: %ld, Num: %ld): ", n, s, N);

这样做:

fprintf(stderr, "%s (len: %ld, Num: %ld): ", n, s, (long)N);

编译时始终使用此标志:-W -Wextra -Werror -pedantic

答案 2 :(得分:0)

虽然var str = "SOME TEXT (BI1) SOME MORE TEXT (BI17) SOME FINAL TEXT (BI1234)"; var re = /\((BI\d+)\)/g; var res =str.match(re).map(function(s) {return s.substring(1, s.length-1);}) console.log(res); console.log(res.join(" "));fclose()之间的错误在:

close()

在使用GCC v4.8.3进行AIX编译时工作正常,即使出现此错误和警告。也许这个版本的gcc知道如何处理从$ gcc tst.c -otst tst.c: In function ‘main’: tst.c:61:7: warning: implicit declaration of function ‘close’ [-Wimplicit-function-declaration] close(fd); ^ uint32_t的投射。

uint64_t

只是为了知识,尝试一下明确的演员:

$ ./tst w
 out (len: 4, Num: 0): 0133a129
$ ./tst r
  in (len: 4, Num: 0): 0133a129

但是,您的_dump(" in", b, 4, n_new); _dump(" in", b, 4, (uint64_t)n_new); 功能可以修复以接收兼容类型:

_dump()

我在测试时显示powerpc(AIX):

显示结束:

void _dump(char *n, byte *d, size_t s, uint32_t N) {
...
}

运行:

#include <stdio.h>

int main() {
    int n = 0x01;

    if ( ((char*)(&n))[0] == 1 && ((char*)(&n))[sizeof(n) - 1] == 0) {
        printf("This is a litte-endian plataform\n");
    } else if ( ((char*)(&n))[0] == 0 && ((char*)(&n))[sizeof(n) - 1] == 1) {
        printf("This is a big-endian plataform\n");
    } else {
        printf("Couldn't determine the endianess of this plataform\n");
    }
}