c - 小端约定,并保存到二进制文件

标签 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]);
}

我做错了什么?

已编辑:

  1. 添加了read_matrix函数。

  2. 似乎我得到的是 short 而不是 int,因为 384 = (110000000) 变成 -128 = (bin) 1000000

  3. 做了一个测试,发现:

    字符 c = 128; 诠释我= 0; 我 |= c;

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

最佳答案

问题出在你的输入转换上:

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]) 的情况下, 左边的运算符已经是一个带符号的整数(因为 << 的类型是它的 promoted 左边参数的类型);右边的参数,一个隐式签名的 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];
}

关于c - 小端约定,并保存到二进制文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27643499/

相关文章:

c - 如果文件大小不变, fopen 更新模式 ("rb+") 是否会更改文件的磁盘位置?

c - 未定义的引用使用automake

c# - 在 Linux/Ubuntu 上部署 sql+winforms 应用程序,开发服务器仍然是 Windows

c++ - 通过 system() 调用启动另一个程序会阻塞套接字

c - ptrace(PTRACE_PEEKUSER) 和 ptrace(PTRACE_PEEKDATA) 之间的区别?

c - sscanf 求值表达式中的 "[^<]"是什么意思?

c++ - (LINUX)从Qt4移植到QT5 undefined symbol : _Zn9Qwidget11stylechangeER6QStyle

json - 遍历数组 : Cannot index array with string "<key>" 时出现 jq 错误

c - lseek EOVERFLOW 错误处理

c - 在 C 中的列表末尾插入项目,产生段错误