C语言write系统调用写入位置异常原因咨询
问题:C程序写入字节位置不符合预期的原因?
我编写了一个简单的C程序,接收四个命令行参数:文件名、起始位置、长度和字节值;打开指定文件进行写入,并从指定位置开始,将文件中指定长度的段填充为指定字节值。程序以块为单位写入(当前为调试目的设置块大小为4字节)。
程序代码如下:
/* fill_file_w_byte_val.c */ #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/stat.h> #include <sys/types.h> #include <unistd.h> int block_size = 4; void error_handling(int res, const char *file_name) { if (res == -1) { perror(file_name); exit(1); } } void write_n_blocks_of_bytes_to_file( const char *file_name, int fd, void *mem, int block_size, int num_of_blocks, int byte_val ) { int i, res; mem = malloc(sizeof(char) * block_size); memset(mem, byte_val, block_size); for (i=num_of_blocks; i > 0; i--) { res = write(fd, mem, block_size); error_handling(res, file_name); } free(mem); } int main(int argc, char **argv) { char *file_name, *mem = NULL; int len, byte_val, num_of_blocks, fd, res, reminder; long start_pos; if (argc != 5) { printf("Wrong number of arguments: %d. It must be %d", argc-1, 4); return 1; } file_name = argv[1]; fd = open(file_name, O_WRONLY); error_handling(fd, file_name); start_pos = atoi(argv[2]); res = lseek(fd, start_pos, SEEK_SET); error_handling(res, file_name); len = atoi(argv[3]); byte_val = atoi(argv[4]); if ((byte_val < 0) || (byte_val > 255)) { printf("Wrong value of byte: %d. It must be from 0 to 255.", byte_val); return 1; } num_of_blocks = len / block_size; if (num_of_blocks > 0) write_n_blocks_of_bytes_to_file( file_name, fd, mem, block_size, num_of_blocks, byte_val ); reminder = len % block_size; if (reminder != 0) write_n_blocks_of_bytes_to_file( file_name, fd, mem, reminder, 1, byte_val ); res = close(fd); error_handling(res, file_name); return 0; }
运行会话:
fedor@fedor-Latitude-E7250:~/c$ hexdump test.bin 0000000 0000 0000 0000 0000 0000 0000 0000 0000 * 000001c fedor@fedor-Latitude-E7250:~/c$ ./fill_file_w_byte_val test.bin 1 2 255 fedor@fedor-Latitude-E7250:~/c$ hexdump test.bin 0000000 ff00 00ff 0000 0000 0000 0000 0000 0000 0000010 0000 0000 0000 0000 0000 0000 000001c
疑问:为何程序写入的是第0和第3字节,而非预期的第1和第2字节?
原因分析与解决方案
核心原因:hexdump的默认显示方式造成误解
hexdump默认以16位(2字节)为单位分组显示,并且会把每组内的两个字节按小端字节序解析成16位整数后输出。也就是说:
- 内存中连续的两个字节
[0x00, 0xff]会被解析为16位整数0xff00,并显示为ff00 - 内存中连续的两个字节
[0xff, 0x00]会被解析为16位整数0x00ff,并显示为00ff
回到你的运行结果:
执行命令后hexdump输出的ff00 00ff,对应的实际内存字节序列是:
字节0: 0x00, 字节1: 0xff, 字节2: 0xff, 字节3: 0x00
这完全符合你的预期——程序确实正确写入了第1和第2字节(值为0xff),只是hexdump的默认显示方式让你误判了位置。
验证方法
使用hexdump -C参数(按字节逐列显示)查看文件,就能直观看到每个字节的实际值:
hexdump -C test.bin
此时你会看到类似如下输出,清晰显示第1、2字节为ff:
00000000 00 ff ff 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 0000001c
程序的潜在优化点
- 起始位置的类型转换:
start_pos是long类型,但你用atoi转换,当起始位置超过int范围时会出错,建议改用atol或strtol:start_pos = atol(argv[2]); - 文件打开方式:当前用
O_WRONLY,如果文件不存在会直接报错,若需要自动创建文件,可添加O_CREAT参数,并指定权限:fd = open(file_name, O_WRONLY | O_CREAT, 0644);
内容的提问来源于stack exchange,提问作者Fyodor
相关产品推荐
相关产品推荐

