C++存储对齐

结构体相关知识
目录:
1、c语言的变量存储时的对齐问题?
1.1、什么是对齐存放:变量在内存中的存放地址能够整除它的类型大小,如:int a = 100; &a%sizeof(a) == 0;
1.2、对齐分为两种
1.3、为什么要对齐存放? 加快cpu对内存的访问速度。
1.4、原子类型的自然对齐
1.5、结构体的适当对齐
1.6、手动对齐
2、柔性数组
2.1、定义形式
3、栈
3.1、先进后出的一个线性数据结构
3.2、例子

1、c语言的变量存储时对齐问题?
1.6、什么是对齐存放:变量在内存中的存放地址能够整除它的类型大小,如:int a = 100; &a%sizeof(a) == 0;
1.7、对齐分为两种
(1)自然对齐,原子数据类型都是自然对齐
(2)适当对齐,构造类型,典型的结构体就是适当对齐的
1.8、为什么要对齐存放? 加快cpu对内存的访问速度。
举例说明:int a = 100;
(1)假如,现在是对齐存放的,存放在24这个地址处,
(2)假如,现在是非对齐存放的。
1.9、原子类型的自然对齐
1.4.1、自然对齐:存储地址 % 类型空间大小 == 0, 如long long a = 3424;  &a % 8 == 0;换句话也就是原子变量的存放地址是它的类型大小(sizeof(类型))的整数倍,但是long double是个例外。
    例子: 
#include <stdio.h>
typedef struct kk
{
        int b;
        short a;
        char c;
}st;
char a;
short b;
int c;
long d
long long e;
float f;
double g;
long double h;
int i;
int main(void)
{
        printf("sizeof(char) = %d\n", sizeof(char));
        printf("sizeof(short) = %d\n", sizeof(short));
        printf("sizeof(int) = %d\n", sizeof(int));
        printf("sizeof(long) = %d\n", sizeof(long));
        printf("sizeof(long long) = %d\n", sizeof(long long));
        printf("sizeof(float) = %d\n", sizeof(float));
        printf("sizeof(double) = %d\n", sizeof(double));
        printf("sizeof(long double) = %d\n", sizeof(long double));
       i = (int)&a % sizeof(char);
        printf("i = %d\n", i);
        i = (int)&b % sizeof(short);
        printf("i = %d\n", i);
        i = (int)&c % sizeof(int);
        printf("i = %d\n", i);
          i = (int)&c % sizeof(long);
        printf("i = %d\n", i);
        i = (int)&d % sizeof(long long);
        printf("i = %d\n", i);
        i = (int)&e % sizeof(float);
        printf("i = %d\n", i);
        i = (int)&f % sizeof(double);
        printf("i = %d\n", i);
        i = (int)&g % sizeof(long double);
        printf("i = %d\n", i);
        return 0;
}
打印结果:
sizeof(char) = 1
sizeof(short) = 2
sizeof(int) = 4
sizeof(long) = 4
sizeof(long long) = 8
sizeof(float) = 4
sizeof(double) = 8
sizeof(long double) = 12
i = 0
i = 0
i = 0
I = 0
i = 0
i = 0
i = 0
i = 4
从结果可以看出,所有原子类型除long double外全部是绝对对齐存放的
分析:为什么?先我们gcc -o a.s -S a.c得到a.s,打开得到如下结果
  .file   "a.c"
  .globl a   //a默认就是1字节对齐
           .data
           .type   a, @object
           .size   a, 1 //空间大小
  a:
           .byte   1
   .globl b   //int b
           .align 2 //2字节对齐
          .type   b, @object
          .size   b, 2//空间大小
  b:
          .value  97
  .globl c  //int c
          .align 4 //4字节对齐
          .type   c, @object
          .size   c, 4 //空间大小
  c:
          .long   2
  .globl d      //long d
          .align 4  //4字节对齐
          .type   d, @object
          .size   d, 4 //空间大小
 d:
          .long   3
  .globl e  //long long e
          .align 8 //8字节对齐
          .type   e, @object
          .size   e, 8 //空间大小
          e:
          .long   4
          .long   0
  .globl f
          .align 4 //4字节对齐
          .type   f, @object
          .size   f, 4//空间大小
  f:
          .long   1069547520
  .globl g
          .align 8//8字节对齐
          .type   g, @object
          .size   g, 8//空间大小
  g:
          .long   0
          .long   1074528256
  .globl h
          .align 16 //16字节对齐
          .type   h, @object
          .size   h, 12//空间大小
  h:
          .long   0
          .long   -1610612736
          .long   16386
          .section        .rodata
从上面我们知道除了h以外,所有的变量其对齐的大小与其空间的大小是一致的,唯独h的对齐大小是16,但是空间大小确实12,导致我们(int)(&h) % sizeof(long double) != 0; 因为只有(int)(&h) % 16 == 0;
1.10、结构体的适当对齐
1.5.1、适当对齐:存储地址 % 适当类型大小M ==0,换句话说存储地址是M的整数倍
1.5.2、适当类型大小m
(1)如何得到m
类型空间大小 <= 机器字长(4),m = 类型空间大小
        类型空间大小 > 机器字长(4),m = 机器字长
(2)什么是机器字长?
内存中的个基本存储单元是一个字节,由8个bit组成
字节 == 8个bit
半字 == 两个字节 ==16个bit
字 == 四个字节== 两个半字 == 32bit
我们现在的2440是32bit的机器,它的一个字 == 4个字节(32bit)
思考:什么叫做32bit的机器?
        1.5.3、以结构体举例,找出结构体的适类型大小M,计算出每个成员的m,从所有的结构体成员中挑出最大的作为
              机构体的M
(1)获取结构体的M
                struct  kk
                {
                    int  a;       //4个字节 ==机器字长, 取int的类型长度或机器字长均可,取4
                    long long  b;  //8个字节 > 机器字长, 取机器字长,取4
                    short  c;     //2个字节 < 机器字长,取类型长度,取sizeof(short) == 2
                    char  d;      //1个字节 < 机器字长,取类型长度,取sizeof(char) == 1
} yy = {100, 350, 600, 800}
       上面结构体所有成员中最大的m值 == 4,所以结构体的M == 4
定义如下这样一个结构体
struct kk
{
char a; //m=1
Short b; //m=2
Int c; //m=4
Long long d; //m=4
}yy;

1、找出yyM值 == 4
2、结构体首地址按照M适当对齐,addr%M==0
3、成员也必须按照m适当对齐存放
4、结构体空间大小%M==0
对应左边的内存来存放yy结构体:
1、找到存放首地址 4%4 ==0
2、结构体每个成员对齐存放
3、算出结构体空间16%4==0

程序验证:空间大小==16

这里写图片描述

(2)结构体在内存中的适当对齐存放
(a)结构体适当对齐存放需要满足如下的条件
1、找出结构体的M值
    2、结构体存放的首地址必须按照结构体自己的M适当对齐存放,addr%M == 0;
    3、成员也必须按照成员自己的m值对齐存放。
    4、整个结构体最后占空间的大小必须整除结构体自己的M值,即sizeof(struct kk)%M == 0;
(3) 把刚才的结构体里面的成员对调,看一下结构体的存储空间有多大
定义如下这样一个结构体
struct kk
{
short b; //m=2
int c; //m=4
char a;//m=1
long long d; //m=4
}yy;

程序验证:空间大小==20

这里写图片描述

(4)结构体中含结构体的情况
struct zz
{
       char aa;//1
       short bb;//2
};

struct kk
{       
        char a;//1
        short b;//2
        int c;//4
        long long d;//8
        struct zz ss;
}yy;

虽然没yy里面又包含了一个SS的结构体变量,但是他们遵守同样的规则。

测试结果:20

这里写图片描述

(5)结构体中包含数组的情况
struct kk
{       
        char a;//1
        short b;//2
        int c;//4
        long long d;//8
        Long long e[3];
}yy;

把数组很多个相同类型的连续原子变量组成,所以可以把数组拆散看成一个一个的原子变量即可。

测试结果:40

这里写图片描述

    1.6、手动对齐 
        1.6.1、为什么手动对齐
        丛前面知道,数据的默认的对齐方式,如果是原子类型,那就是自然对齐,如果是结构体,那就是适当对 齐,但是我们看看结构体的适当对齐,往往内存中有很多空间并不能被充分的利用,那么我们就需要用到手动对齐,节约空间。
        1.6.2、如何手动对齐    
        1.6.2.1、命令和概念
        #pragma pack(n)//n必须为偶数,1除外
        结构体类型定义
        #pragma pack()//结束手动对齐

n:如果n<=成员类型大小,则所有的该成员存放地址按照n对齐存放(存放地址%n == 0),如果n>成员        类型大小,则该成员存放地址按照默认类型大小对齐存放(存放地址%成员类型大小 == 0),但是结构        体存放的首地址仍然按照结构体的M来对齐存放。
1.6.2.2、举例
(1)适当对齐
struct kk
{       
       char a;//1
       short b;//2
       int c;//4
       long long d;//8
};   
看看它的适当对齐  
结果:16 

这里写图片描述

(2)手动对齐1
#pragma pack(1)
struct kk
{       
       char a;//1
       short b;//2
       int c;//4
       long long d;//8
};   
#pragma pack()
1<=所有成员类型大小
全部按照1对齐存放
结果:15

这里写图片描述

(2)手动对齐2
#pragma pack(4)
struct kk
{       
       char a;//1
       short b;//2
       int c;//4
       long long d;//8
};   
#pragma pack()
1、结构体首地址a按照M对齐  存放
2、a:1<4按照a默认方式对齐存放
3、b:  2<=4,按照b默认方式对齐存放
4、c:4<=4,默认或按照n对齐存放均可
5、d:4(n)<8,按照n对齐存放
最后得到右边的一个存的结果,空间大小为:16

这里写图片描述

3、柔性数组
struct kk
{       
       int a;
       long b;
       char c[];
};
注意几点:
1、数组[]中不能有数字,否则就不是柔性数组
2、数组前至少有一个其它类型的成员
3、且数组必须只能在最后面
4、给值有两种方式   
a)初始化:struct kk yy = {100, 200, {‘1’, ‘2’, ‘3’, ‘4’, ‘\0’}};
b)赋  值:yy.c[0] = ‘a’;  yy.c[0] = ‘b’; 
5、Sizeof(yy.b);不可以,编译器为报错的
6、Strlen(yy.b);可以
2.1、定义形式
4、栈
3.1、先进后出的一个线性数据结构
3.2、例子
mystack.h

#ifndef __MYSTACK__
#define __MYSTACK__
#define SIZE 10
struct Mystack
 {       
        char array[SIZE];
        int top;
};
#if 1
extern int empty(struct Mystack *st);
extern void pop(struct Mystack *st);
extern void clear(struct Mystack *st);
extern int full(struct Mystack *st); 
extern void push(struct Mystack *st, char ch);
extern void print(struct Mystack *st);
#endif

main.c

#include <stdio.h>
#include "mystack.h"
int main(void)
{
         int flag = 0;
         char ch = 0;
         struct Mystack st;
         st.top = 0; 
         printf("input your characters!!!!\n");
         while(1)
         {
                ch = getchar();
                if(EOF == ch)
                        break;
                if('\n' == ch)
                        break;
                switch(ch)
                {
                        case '#':
                                if(0 == empty(&st))
                                        break;
                                else
                                {
                                        pop(&st);
                                        break;
                                }
                        case '@':
                                if(0 == empty(&st))
                                        break;
                                else
                                {
                                        clear(&st);   
                                        break;
                                }
                        default:
                                if(0 == full(&st))
                                {
                                        printf("stack\ 
                                             is full\n");
                                         flag = 1;
                                            break;
                 }
                 else
                 {                                        
                       push(&st,ch);                                                   
                       break;
                 }
             }
             if(1 == flag)
                  break;
      }
      print(&st);
      return 0;
}

child.c

#include <stdio.h>
#include "mystack.h"
int empty(struct Mystack *st)
{
         if(0 == st->top)
                return 0;
         else
                return 1;
}
void pop(struct Mystack *st)
{
         st->top--;
}
void clear(struct Mystack *st)
{
         st->top = 0;
} 
int full(struct Mystack *st)
{
        if(SIZE == st->top)
              return 0;
        else
               return -1;
}
void push(struct Mystack *st, char ch)
{
        st->array[st->top] = ch;
        st->top++;
} 
void print(struct Mystack *st)
{
        int i = 0;
        printf("\n");
        for(i=0; i<st->top; i++)
                printf("%c", st->array[i]);
        printf("\n");
}

Makefile                                
OBJS = main.o init.o student.o teacher.o menu.o account.o
CC = gcc
CFLAGS = -Wall -O -g
system : $(OBJS)
        $(CC) $(CLFAGS) $^ -o $@
%.o : %.c
        $(CC) $(CFLAGS) -c $< -o $@
clean : 
        rm *.o system
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值