我有一个矩阵(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 :
添加了read_matrix
功能。
好像我在short
取代了int
,因为384 =(110000000)变成-128 =(bin)1000000
做了测试,发现了:
char c = 128; int i = 0; i | = c;
给出i = -128
。为什么????
答案 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}}。