结构体

作者: Albert_Sun | 来源:发表于2016-03-22 16:33 被阅读112次
  • 结构体有名定义
  • 无名定义
  • 结构体嵌套定义
  • 结构体内存对齐
  • 结构体成员初始化
  • 结构体变量引用
  1. 结构体的有名定义:直白点就是在struct 之后跟着结构体的名称,如下:
struct str_test{
    int a;
    char b;
};
  1. 结构体的无名定义:注意与有名定义的区别。这种方式不提倡,但是系统支持
struct {
    int c;
    char e;
}var1,var2;  //结构体定义完毕直接定义变量。
  1. 结构体的嵌套定义:
struct str_test3{
    int length;
    int wide;
    int high;
    struct str_test4{
      int id;
      struct str_test3 var;
  };
};//这种也可以,但是不提倡使用。

struct str_test3{
    int length;
    int wide;
    int high;
};
struct str_test4{
    int id;
    struct str_test3 var;
};//是最常用方式,结构体互相独立;
  1. 结构体内存对齐
    1)、平台原因:不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
    2)、性能原因:数据结构应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。

这些问题既和软件相关又和硬件相关。

  1. 所谓软件相关主要是指和具体的编程语言的编译器的特性相关,编译器为了优化CPU访问内存的效率,在生成结构体成员的起始地址时遵循着某种特定的规则,这就是所谓的 结构体成员“对齐”;
  2. 所谓硬件相关主要是指CPU的“字节序”问题,也就是大于一个字节类型的数据如int类型、short类型等,在内存中的存放顺序,即单个字节与高低地址的对应关系。

字节序分为两类:Big-Endian和Little-Endian,有的文章上称之为“大端”和“小端”,他们是这样定义的:
Little-Endian就是低位字节排放在内存的低地址端,高位字节排放在内存的高地址端;
Big-Endian就是高位字节排放在内存的低地址端,低位字节排放在内存的高地址端。

最本质原因是基本数据类型的长度有大有小,在使用的时候不可能长短一样,但CPU为了提高效率需要大批量一次访问多个内存区,可能存在同一个数据由于没有字节对齐而出现跨字节访问,这样效率会下降,甚至有些CPU会异常,比如一个大小固定的盒子如果要放进去不整齐的筷子,就会很麻烦要么折断要么换盒子,最好就是将筷子对齐同时放入盒子。

其中筷子也有大头和小头之分,具体怎么放那是一个约定,只要整体系统都采用统一的大头或小头就行。

我们在做盒子放筷子的试验的时候,很多时候是我们人工可识别的,能很智能完成复杂操作,那么计算机在处理数据的过程中的字节是怎么对齐的呢?

首先我们的程序在编译阶段,编译器会稍微做些智能化的工作,将内存的分配过程中按照一定的规则对齐,GCC 默认的对齐字节是 4字节, vc 默认的对齐字节是 8字节。

在编译过程中有个开关可以控制对齐的字节大小: #pragma pack(n) 这个宏就是来控制编译过程中的字节对齐用的,其中的 n 就是你可定制的对齐字节大小,如果不设置就按默认值处理。

我们可以编写程序来验证字节对齐的神奇:

struct A
{
    char a;
    int c;
};

问 如上结构体的大小是多少?为什么? 这是一个企业面试题,类似的面试题多不胜数。

将这个结构体编写程序跑起来看看就知道了,在运行之前我们按照以前学习的基本知识计算出 结果应该是:char =1 int =4 所以结果应该是5
但是非常抱歉,程序的运行结果却是:8 ,到底是我们计算错误还是计算机出故障了?其实我们都没错,计算机也没错 ,就是字节对齐的起作用。
编译器在编译代码的过程中,根据如下原则对数据对齐

  1. 数据成员对齐规则:结构(struct或联合union)的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小的整数倍开始(比如int在32位机为4字节,则要从4的整数倍地址开始存储)。
  2. 结构体作为成员:如果一个结构里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储。(struct a里存有struct b,b里有char,int,double等元素,那b应该从8的整数倍开始存储。)
    原则
  3. 收尾工作:结构体的总大小,也就是sizeof的结果,必须是其内部最大成员的整数倍,不足的要补齐。
  4. 结构体中的字节对齐,如果成员的大小大于等于4字节,则以4字节对齐,然后再排列下一个成员。如果两个成员的大小加起来大于四字节,则这两个相邻成员仍分别按照四字节对齐。仅当两个相邻成员的总大小在四字节内的时候,才会一起按照四字节对齐。
    Demo:
typedef struct {
    char a;
    int b;
    short c;
}Demo1;
Demo1 tmp;
sizeof(Demo1)=12
("%p", &tmp.a) = 0
("%p", &tmp.b) = 4
("%p", &tmp.c) = 8
Demo1 tmp.png
typedef struct {
    char a;
    short b;
    int c;
}Demo2;
Demo2 tmp;
sizeof(Demo2)=8
("%p", &tmp.a) = 0
("%p", &tmp.b) = 2
("%p", &tmp.c) = 4
Demo2 tmp.png
typedef struct {
    char a;
    char b[4];
    short c; //int c
}Demo3;
Demo3 tmp;
sizeof(Demo3)=8 // 12
("%p", &tmp.a) = 0
("%p", &tmp.b) = 1
("%p", &tmp.c) = 6
Demo3 short.png Demo3 int.png

所以我们在使用结构体的过程中一定要注意它的这个特性,尤其是在需要用到结构体大小的地方,一定要考虑字节对齐产生的影响。

1)联合体是一个结构;
2)它的所有成员相对于基地址的偏移量都为0;
3)此结构空间要大到足够容纳最"宽"的成员;
4)其对齐方式要适合其中所有的成员;

  1. 结构体成员初始化
struct str_test
{
    int a;
    int b;
};
struct str_test var = {10,20};   ///结构体定义与变量定义分离,变量的初始化一次性完成
struct str_test var2 = {.a=30};  ///结构体变量的成员可以单独指定

等价

struct str_test var2 ;
    var2.a =30;
struct str_test2
{
    int aa;
    int bb;
    struct str_test cc;
};
struct str_test2 var3 = {10,20,{30,40}};   ///嵌套结构体初始化一次性完成 
struct str_test2 var5 = {.cc.a =10};       ///嵌套结构体成员变量的逐层赋值
struct str_test3
{
    int a;
    int b;
}var6,var7;  ///定义结构体的时候定义变量
struct str_test4
{
    int a;
    int b;
}var8 = {100,200};  ///定义结构体的时候定义变量并初始化
  1. 结构体变量的引用:
    1、变量名.成员 ;
    2、指针变量 ->成员;
struct str_test var;
   var.a =10;
   var.b =20;
struct str_test pvar = NULL;    ///结构体指针与基本数据类型的指针一样,必须指向一个实际的内存地址。    
pvar = (strcut str_test *) malloc(sizeof(struct str_test));

或者 定义变量同时定义指针:
struct str_test var, *pvar; pvar = &var;

相关文章

  • 结构体

    [toc] 结构体的定义方式 先定义结构体类型,再定义结构体变量 定义结构体类型的同时定义结构体变量 定义结构体类...

  • 【C语言笔记】<十九>结构体

    结构体的基本概念 结构体初始化 结构体的内存存储细节 结构体定义的方式 结构体类型的作用域 指向结构体的指针 结构...

  • C结构体和链表

    一,结构体变量定义及初始化 二,无名结构体 备注:无名结构体很少使用 三,宏定义结构体 四,结构体嵌套 五,结构体...

  • 结构体

    结构体定义* 结构体中的格式:* struch 结构体名* {* 结构体成员变量* }* 结构体中的特点* 1.结...

  • 结构体数组的定义

    结构体数组的定义 1、先定义结构体类型,再定义结构体数组 2、定义结构体类型的同时定义结构体数组 3、省略结构体类...

  • C#结构体,析构方法,跨程序访问

    结构体 结构体定义 结构体的语法格式: struct + 结构体名 { 结构体成员变量(相当于类中的字段) } 结...

  • 结构体

    结构体有名定义 无名定义 结构体嵌套定义 结构体内存对齐 结构体成员初始化 结构体变量引用 结构体的有名定义:直白...

  • 菜鸡学Swift3.0 13.结构体

    结构体 struct 是值类型 1.定义结构体 struct 结构体类型 { var 结构体属性:类型 ...} ...

  • 结构体

    结构体初识 结构体指针 结构体的匿名字段 结构体嵌套 Go语言中的OOP

  • C语言 第九章 结构体

    [TOC] 第九章结构体 结构体的定义 结构体定义2 指针表示结构体

网友评论

      本文标题:结构体

      本文链接:https://www.haomeiwen.com/subject/inkllttx.html