Little-endian约定,并保存到二进制文件

时间:2014-12-25 01:12:15

标签: c linux system-calls endianness

我有一个矩阵(2-D int指针int **mat),我试图用Little-endian惯例写入Linux中的文件。

这是我写入文件的函数:

#define BUFF_SIZE 4
void write_matrix(int **mat, int n, char *dest_file) {
    int i, j;
    char buff[BUFF_SIZE];
    int fd = open(dest_file, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR | S_IXUSR);

    if (fd < 0) {
        printf("Error: Could not open the file \"%s\".\n", dest_file);
    }

    buff[0] = (n & 0x000000ff);
    buff[1] = (n & 0x0000ff00) >> 8;
    buff[2] = (n & 0x00ff0000) >> 16;
    buff[3] = (n & 0xff000000) >> 24;

    write(fd, buff, BUFF_SIZE);

    for (i = 0; i < n; i++) {
        for (j = 0; j < n; j++) {
            buff[0] = (mat[i][j] & 0x000000ff);
            buff[1] = (mat[i][j] & 0x0000ff00) >> 8;
            buff[2] = (mat[i][j] & 0x00ff0000) >> 16;
            buff[3] = (mat[i][j] & 0xff000000) >> 24;

            if (write(fd, buff, BUFF_SIZE) != BUFF_SIZE) {
                close(fd);
                printf("Error: could not write to file.\n");
                return;
            }
        }
    }

    close(fd);
}

问题在于,当我写出一个足够大的mat[i][i] = i形式的矩阵(假设是512 X 512)时,我想我会得到一个溢出,因为我得到了奇怪的负数。

要转换回我使用:

void read_matrix(int fd, int **mat, int n, char buff[]) {
    int i, j;

    for (i = 0; i < n; i++) {
        for (j = 0; j < n; j++) {
            assert(read(fd, buff, BUFF_SIZE) == BUFF_SIZE);
            mat[i][j] = byteToInt(buff);
        }
    }
}

int byteToInt(char buff[]) {
    return (buff[3] << 24) | (buff[2] << 16) | (buff[1] << 8) | (buff[0]);
}

我做错了什么?

EDITED

  1. 添加了read_matrix功能。

  2. 好像我在short取代了int,因为384 =(110000000)变成-128 =(bin)1000000

  3. 做了测试,发现了:

    char c = 128; int i = 0; i | = c;

    给出i = -128。为什么????

2 个答案:

答案 0 :(得分:3)

问题在于您的输入转换:

int byteToInt(char buff[]) {
    return (buff[3] << 24) | (buff[2] << 16) | (buff[1] << 8) | (buff[0]);
}

您没有提到您所在的平台,但在大多数常见平台char上都已签名。这将导致问题。例如,假设buff[1]是0x80(0b1000000)。由于它是带符号的值,因此值为-128的代码。并且由于移位运算符首先对它们的两个参数进行整数提升,因此在执行移位操作之前将转换为整数-128;换句话说,它将具有值0xFFFFFF80,在移位后将变为0xFFFF8000。

按位逻辑运算符(例如|)在执行按位运算之前执行通常的算术转换;在(buff[1] << 8) | (buff[0])的情况下,左手操作符已经是signed int(因为<<的类型是提升的左手参数的类型);右边的参数,一个隐式签名的char,也将被提升为一个带符号的int,所以如果它是0x80,它最终会被符号扩展为0xFFFFFF80。

在任何一种情况下,按位或操作都会以不需要的高位1位结束。

buff[x]明确地投射到unsigned int无济于事,因为在重新解释为int之前,它首先会被标记扩展为unsigned int。相反,有必要将其转换为unsigned char

int byteToInt(char buff[]) {
    return   ((unsigned char)buff[3] << 24)
           | ((unsigned char)buff[2] << 16)
           | ((unsigned char)buff[1] << 8)
           | (unsigned char)buff[0];
}

由于int可能是16位,因此最好使用long,事实上最好使用unsigned long来避免其他转换问题。这意味着进行双重演员:

unsigned long byteToInt(char buff[]) {
    return   ((unsigned long)(unsigned char)buff[3] << 24)
           | ((unsigned long)(unsigned char)buff[2] << 16)
           | ((unsigned long)(unsigned char)buff[1] << 8)
           | (unsigned long)(unsigned char)buff[0];
}

答案 1 :(得分:1)

你所拥有的是一种经常被忽视的未定义行为。 有符号负值左移是未定义的See here了解详情。

当你这样做时

int byteToInt(char buff[]) {
    return (buff[3] << 24) | (buff[2] << 16) | (buff[1] << 8) | (buff[0]);
}

即使buff的一个元素具有负值(即,其中一个二进制数据的值设置了MSB),您也会遇到未定义的行为。由于您的数据是二进制的,因此将其读作unsigned最有意义。您可以使用标准类型来明确签名和长度,例如来自uint8_t的{​​{1}}。