作者:朱金灿

来源:http://blog.csdn.net/clever101

同事使用标准C库读取文件,发现总是读取不对,让我帮忙看一下。

原来他定义了如下一个结构体:

// 定义块的结构
typedef struct {
unsigned short id;
long len;
} Chunk3DS;

然后这样读取文件:

       if (fread(&chunk,sizeof(Chunk3DS),1,m_fp)!=1)
{
return FALSE;
}

但是读取的内容总是不对。

我想到结构体内存对齐这档子事,于是我把读取代码改为:

Chunk3DS chunk;
if (fread(&chunk.id,sizeof(unsigned short),1,m_fp)!=1)
{
return FALSE;
}
if (fread(&chunk.len,sizeof(long),1,m_fp)!=1)
{
return FALSE;
}

接着我测试了一下:

int nShortSize = sizeof(unsigned short); // 等于2
int nLongSize = sizeof(long); // 等于4
int SstructSize = sizeof(Chunk3DS); // 等于8

整整差了两个字节,难怪读取的内容不对。现在我发现由于结构体内存对齐的缘故(结构体内存对齐的原理网上的相关文章很多,在这不进行详述),使用fread接口去读一个结构体的缓冲区常常不准确,其实也不科学,因为文件设计者在设计文件时文件的各个成员肯定都是紧挨着的,因此是使用简单类型逐个去读取。

如何解决这个问题呢?如前文所述,使用简单类型逐个去读取肯定是可以的,但是如果文件比较复杂的话,这意味着要写比较多的代码。有没有更好的办法呢?也有,可以将结构体做如下定义:

/* 对齐结构成员到1字节 */
#ifdef __GNUC__
#define GNUC_PACKED __attribute__((packed))
#else
#define GNUC_PACKED
#endif #ifdef __arm
#define ARM_PACKED __packed
#else
#define ARM_PACKED
#endif #if defined(WIN32) || defined(_WIN64)
#pragma pack(1)
#endif
ARM_PACKED
typedef struct {
unsigned short id;
long len;
}GNUC_PACKED Chunk3DS;
#if defined(WIN32) || defined(_WIN64)
#pragma pack()
#endif

参考文献:

1.  【原创&交流】使用标准C库读文件时需要注意的一个问题

使用标准C读取文件遇到的结构体对齐问题及其解决办法的更多相关文章

  1. c语言_文件操作_FILE结构体解释_涉及对操作系统文件FCB操作的解释_

    1. 文件和流的关系 C将每个文件简单地作为顺序字节流(如下图).每个文件用文件结束符结束,或者在特定字节数的地方结束,这个特定的字节数可以存储在系统维护的管理数据结构中.当打开文件时,就建立了和文件 ...

  2. C语言文件操作 FILE结构体

    内存中的数据都是暂时的,当程序结束时,它们都将丢失.为了永久性的保存大量的数据,C语言提供了对文件的操作. 1.文件和流 C将每个文件简单地作为顺序字节流(如下图).每个文件用文件结束符结束,或者在特 ...

  3. C++ 读取文本文件内容到结构体数组中并排序

    成绩排行:从Score.txt文件读取学生信息,对其进行排序,按回答题数从大到小排,若相等,按分数从小到大排 #include<iostream> #include<fstream& ...

  4. Golang Json文件解析为结构体工具-json2go

    代码地址如下:http://www.demodashi.com/demo/14946.html 概述 json2go是一个基于Golang开发的轻量json文件解析.转换命令行工具,目前支持转换输出到 ...

  5. 结构体:探析C#文件方式读写结构体

    最近直在研究Net Micro Framework字体文件(tinyfnt)由于tinyfnt文件头部有段描述数据所以很想 定义个结构体像VC样直接从文件中读出来省得用流个个解析很是麻烦 没有想到在中 ...

  6. 函数定义从零开始学C++之从C到C++(一):const与#define、结构体对齐、函数重载name mangling、new/delete 等

    今天一直在学习函数定义之类的问题,下午正好有机会和大家共享一下. 一.bool 类型 逻辑型也称布尔型,其取值为true(逻辑真)和false(逻辑假),存储字节数在不同编译系统中可能有所不同,VC+ ...

  7. const与#define、结构体对齐、函数重载name mangling、new/delete 等

    一.bool 类型 逻辑型也称布尔型,其取值为true(逻辑真)和false(逻辑假),存储字节数在不同编译系统中可能有所不同,VC++中为1个字节. 声明方式:bool result; result ...

  8. C语言中结构体对齐问题

    C语言中结构体对齐问题 收藏 关于C语言中的结构体对齐问题 1,比如: struct{short a1;short a2;short a3;}A;struct{long a1;short a2;}B; ...

  9. 《PHP7底层设计与源码实现》学习笔记2——结构体对齐

    书里给了一段代码,假如有个结构体如下: struct test {     char a;     int b;     long c;     void* d;     int e;     cha ...

随机推荐

  1. Ubuntu系统的安装(虚拟机) 并配置C/C++编译器

    一.系统的初始化配置 1.配置静态IP和DNS 配置静态IP 1.sudo vim /etc/network/interfaces,修改文件内容如下: auto eth0  #表示让网卡开机自动挂载e ...

  2. dubbo源码阅读之SPI

    dubbo SPI SPI,全程Service Provider interface, java中的一种借口扩展机制,将借口的实现类注明在配置文件中,程序在运行时通过扫描这些配置文件从而获取全部的实现 ...

  3. MySQL笔记(3)---文件

    1.前言 第二章简单记录了一下InnoDB存储引擎的一个基本内容,介绍了保证高效插入的Insert Buffer,change Buffer和确保数据安全的write ahead log以及doubl ...

  4. CentOS下使用crontab命令来定时执行任务

    原文地址:http://www.centoscn.com/CentOS/help/2015/0424/5261.html crontab命令 是用来让使用者在固定时间或固定间隔执行程序之用,换句话说, ...

  5. unsigned/signed int/char类型表示的数值范围

    一个32位的signed int类型整数表示的范围:-2^31~2^31-1 一个32位的unsigned int类型整数表示的范围: 0~2^32-1 一个8位的signed char类型整数表示的 ...

  6. JavaScript中十种一步拷贝数组的方法

    JavaScript中我们经常会遇到拷贝数组的场景,但是都有哪些方式能够来实现呢,我们不妨来梳理一下. 1.扩展运算符(浅拷贝) 自从ES6出现以来,这已经成为最流行的方法.它是一个很简单的语法,但是 ...

  7. CustomSqlSessionFactoryBean

    import java.io.File; import java.net.JarURLConnection; import java.net.URL; import java.util.ArrayLi ...

  8. Ansible工作流程详解

    1:Ansible的使用者 ------>Ansible的使用者来源于多种维度,(1):CMDB(Configuration Management Database,配置管理数据库),CMDB存 ...

  9. python学习之内存机制

    不可变对象(字符串.元组) 1. a = 1 首先在内存中创建对象1,并记录对象的引用计数为1次. id(a) 查看变量a引用的对象的内存地址 2. b = 1 内存中已存在对象1,变量b引用对象1, ...

  10. JavaScript学习总结(二)——延迟对象、跨域、模板引擎、弹出层、AJAX示例

    一.AJAX示例 AJAX全称为“Asynchronous JavaScript And XML”(异步JavaScript和XML) 是指一种创建交互式网页应用的开发技术.改善用户体验,实现无刷新效 ...