自定义数据类型

前言

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;
}

你是否还能够正确的判断出这两个结构的大小呢?
Alt
想要知道这两个答案怎么出来的,就需要了解什么是内存对齐。

1.内存对齐是什么意思?

现代计算机中内存空间都是按照字节(byte)进行划分的,所以从理论上讲对于任何类型的变量访问都可以从任意地址开始,但是在实际情况中,在访问特定类型变量的时候经常在特定的内存地址访问,所以这就需要把各种类型数据按照一定的规则在空间上排列,而不是按照顺序一个接一个的排放,这种就称为内存对齐,内存对齐是指首地址对齐,而不是说每个变量大小对齐。

2.对齐的规则

2.1.第一个成员对齐在与结构体变量偏移量为0的地址处。
2.2.其他成员变量要对齐到某个数字(对齐数)的整数倍处。

2.2.1.对齐数=编译器默认的一个对齐数与该成员大小的较小值。
2.2.2.vs中默认的对齐数为8,Gcc编译器没有默认对齐数*
2.3.结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
2.4.如果嵌套了结构体的情况下,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构的对齐数)的整数倍。

3.为什么存在内存对齐?

根据统计资料数据显示
1.平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据;某些硬件平台只能在某些地址取某些特定的类型数据,否则会抛出硬件异常。
2.性能原因:数据结构尤其是栈,应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要两次内存访问;而对齐的内存访问仅需要访问一次。
总的来说,
结构体的内存对齐是拿空间来换取时间的做法,
想要结构体省空间又省时间,
就要让空间小的成员尽量集中在一起。

四、总结

以上内容,和数据结构关系非常之大,很想马上就去学习这个数据结构,我非常感兴趣。虽然学了这些东西,但对我来说好像这些似乎并没有其他内容有作用。当然或许我这话说的有点早了,实践出真知,这些东西必须在操作中,才能真正领悟这些意义。正在学习中!好期待!如果有大佬看出了我的错误,还望不吝指正。谢谢啦!

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值