文章目录
前言
C语言有几种基本类型(如整型、实型、字符型)、构造数据类型(数组)和指针类型。但是,在解决一些比较复杂的实际问题时,只有这些数据类型是不够的。于是C语言提供了一种名为结构体(简称结构)的数据类型。
以下代码均在vs环境下实现
一、结构是什么?
结构是将彼此相关的、类型1不同的数据组合在一起的构造数据类型,它是由若干成员(结构分量)组成的,每一个成员的数据类型可以是基本数据类型,也可以是构造类型。
二、结构的使用
1.基本用法
#include<stdio.h>
struct am
{
char aihao[10];
int year;
char hunying[20];
};
typedef struct name
{
char ch1[20];
char sex[10];
struct am a;
}name;
int main()
{
char arr[] = "hello bit";
name n = { "Jaky","性别男",{"爱好女",18,"未婚"} };
printf("%s\n",n.ch1 );
printf("%s%s\n", n.sex, n.a.aihao);
printf("%d %s", n.a.year, n.a.hunying);
return 0;
}
1.1.结构数组
typedef struct am
{
char aihao[10];
int year;
char hunying[20];
}am;
typedef struct name
{
char ch1[20];
char sex[10];
am a;
}name;
int main()
{
name n[10] = { { "Jaky","性别男",{"爱好女",18,"未婚"} },
{"Taly","male",{"no",19,"no"}}
};
printf("%s %d\n",n[0].ch1,n[0].a.year);
printf("%s %d", (n + 1)->ch1, (*(n + 1)).a.year);
return 0;
}
1.2.结构指针
typedef struct s
{
int a;
char b;
}S;
int main()
{
S s[2] = { {1,'a'},{2,'b'} };
int i;
S* p;
p = s;
for(i=0;i<2;i++)
printf("%d %c\n", (p+i)->a, (p+i)->b);
return 0;
}
一般结构指针会配合结构数组使用
3.位段
位段?
位段的声明和结构体是类似的,有两个不同
1.位段的成员必须是int ,unsigned int,signed int ;好像char也可以;所有成员都是相同类型的;
2.位段的成员后边有一个冒号和一个数字。
位段可以说是很省内存,相对于其他类型来说
来看看下面这段代码的结果是什么?
#include<stdio.h>
struct S
{
int a : 2;
int b : 5;
int c : 10;
int d : 30;
};
int main()
{
struct S s;
printf("%d", sizeof(s));
return 0;
}
答案是8
so why ?
其实成员后面的数字是定义这个数据占多少个bit位,并且它向内存申请空间是,从第一个数据开始,分配一个int型空间,用完或者不够用才会开辟下一个int型。
所以,在我的机器上,上面的代码是先分配一个int型大小的空间,a,b,c,这三个成员放下去了,还剩15个bit位,不够放d了就只能再开辟一个int 型,总共就用了两个int型,所以它占的空间就是8。
位段的内存分配
1.位段成员可以是int unsigned int,signed int 或者是char(属于整形家族)类型
2.位段的空间上是按照需要以四个字节(int)或者一个字节(char)的方式来开辟的
3.位段涉及很多不确定因素,位段是不跨平台的,注重可移植性的程序应该避免使用位段。
位段的跨平台问题
1.int 位段是当作有符号还是无符号数是不确定的
2.位段中最大位的数目不能确定。(16位机器最大16,32位机器最大32,写成27,在16位机器就会出现问题。
3.位段中的成员在内存中是从左向右分配,还是从右向左分配尚未定义
4.当一个结构包含两个位段,第二个成员比较大,无法容纳第一个成员时,是舍弃剩余的位还是利用,这是不确定的。
尽管位段有这么些问题,但是由于位段的这种内存分配方式,所以位段是应用在数据传输方面还是大有所用。
4.共用体
共用体也叫联合体,顾名思义,共用体就是从内存中拿一块空间,各种不同类型的数据都可以放在里面,不过在程序运行的某一个时刻只有一个成员有效,也即是说共用体的所有成员不能同时放在这块内存区域内。
#include<stdio.h>
typedef union S
{
char b;
int a;
}S;
int main()
{
S s;
printf("%d", sizeof(s));
return 0;
}
以上代码的结果是 4
so ?
因为联合体即共用体它的所有成员共用一块内存空间,所以它至少要能够存下最大成员
百度面试题
如何计算当前计算机的大小端储存?
so…大小端存储是什么?
//假如
int a=0x11223344;
//低地址-------------高地址//这是一片地址
......[11][22][33][44][][][].....大端字节序存储模式
......[44][33][22][11][][][].....小端字节序存储模式
可见其实这个问题就是,讨论一个数据,放在内存中存放字节顺序。
int check()
{
int a = 1;
return *(char*)&a;
}
int check_sys()
{
union un
{
char c;
int i;
}u;
u.i = 1;
//返回1,表示小端
//返回0,表示大端
return u.c;
}
int main()
{
int a=check_sys();
if (1 == a)
printf("小端\n");
else
printf("大端\n");
return 0;
}
这两个函数都可以实现,第二个使用到了联合体的作用
5.枚举
为什么使用枚举?
1.增加代码的可读性和可维护性
2.和#define定义的标识符比较枚举有类型检查,更加严谨。
3.防止了命名污染(封装)
4.便于调试
5.使用方便,一次可以定义多个常量。
枚举类型的定义
#include<stdio.h>
enum sex
{// 枚举的可能取值-常量
male,
female,
secret
};
int main()
{
enum sex s1, s2, s3;//枚举常量只能是类型里面定义的那些值
s1 = male;
s2 = female;
s3 = secret;
printf("%d %d %d",s1,s2,s3 );
return 0;
}
打印在屏幕上的值为,0 1 2,可见枚举常量是有值的,并且递增
枚举成员是常量,不能对他们进行赋值,如male=0;是错误的,但在定义枚举类型时可以指定枚举成员的值。
#include<stdio.h>
enum sex
{
male=4,
female=2,
secret
};
int main()
{
enum sex s1, s2, s3;
s1 = male;
s2 = female;
s3 = secret;
printf("%d %d %d",s1,s2,s3 );
return 0;
}
此时打印出来的值就是 4 2 3
枚举类型的数据的输入和输出
#include<stdio.h>
typedef enum sex{male,female,secret}sex;
int main()
{
char* sec[20] = { "male","female","secret" };
sex x;
int i;
scanf("%d", &i);
x = (sex)i;
printf("%s\n",sec[x]);
return 0;
}
另外枚举类型数据的比较是对其序号进行比较的,可以这样:
if(x==male)
printf("yes");
三、计算结构的大小
#include<stdio.h>
struct S
{
char a;
double b;
char c;
};
struct V
{
char a;
char b;
double c;
};
int main()
{
struct S s;
struct V v;
printf("%d %d", sizeof(s),sizeof(v));
return 0;
}
你是否还能够正确的判断出这两个结构的大小呢?
想要知道这两个答案怎么出来的,就需要了解什么是内存对齐。
1.内存对齐是什么意思?
现代计算机中内存空间都是按照字节(byte)进行划分的,所以从理论上讲对于任何类型的变量访问都可以从任意地址开始,但是在实际情况中,在访问特定类型变量的时候经常在特定的内存地址访问,所以这就需要把各种类型数据按照一定的规则在空间上排列,而不是按照顺序一个接一个的排放,这种就称为内存对齐,内存对齐是指首地址对齐,而不是说每个变量大小对齐。
2.对齐的规则
2.1.第一个成员对齐在与结构体变量偏移量为0的地址处。
2.2.其他成员变量要对齐到某个数字(对齐数)的整数倍处。
2.2.1.对齐数=编译器默认的一个对齐数与该成员大小的较小值。
2.2.2.vs中默认的对齐数为8,Gcc编译器没有默认对齐数*
2.3.结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
2.4.如果嵌套了结构体的情况下,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构的对齐数)的整数倍。
3.为什么存在内存对齐?
根据统计资料数据显示
1.平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据;某些硬件平台只能在某些地址取某些特定的类型数据,否则会抛出硬件异常。
2.性能原因:数据结构尤其是栈,应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要两次内存访问;而对齐的内存访问仅需要访问一次。
总的来说,
结构体的内存对齐是拿空间来换取时间的做法,
想要结构体省空间又省时间,
就要让空间小的成员尽量集中在一起。
四、总结
以上内容,和数据结构关系非常之大,很想马上就去学习这个数据结构,我非常感兴趣。虽然学了这些东西,但对我来说好像这些似乎并没有其他内容有作用。当然或许我这话说的有点早了,实践出真知,这些东西必须在操作中,才能真正领悟这些意义。正在学习中!好期待!如果有大佬看出了我的错误,还望不吝指正。谢谢啦!