结构体相关知识
目录:
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、找出yy的M值 == 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