自定义类型



【结构体】

     结构体是将具有不同或者相同元素类型放在一起的聚合类型。

一,结构体类型的声明

struct tag//struct是关键字,tag是标签,要见名知意。这里tag可省略,但不建议省略。
{
member_list;//声明一种类型,在c语言中,这里不能为空。
}variable_list;//结构体变量列表,变量可以有多个。这里建议省略,需要用的时候再定义。

注意{};,后面的分号不能丢。

看下面一个声明:


struct A
{
	int a;
	char b;
	float c;
}a[20],*p;


struct B
{
	int a;
	char b;
	float c;
}x;
p = &x;

结果有报错:

编译器会把上面两个声明当成两个完全不同的两个类型,所以是非法的。

二,结构体成员

 结构体成员可以是标量,数组,指针,也可以是其他结构体。

那么结构体是怎样访问成员呢?

结构体变量的成员是通过点操作符(.)或者—>访问的.

举个例子:

#include<stdio.h>
#include<windows.h>

struct Test
{
	int a;
	char b;
	float c;
};



int main()
{
	struct Test A;
	struct Test *p;
	A.a = 10;//使用.访问a成员
	A.b = 'B';//使用.访问b成员

	p = &A;
	(*p).a = 20;
	(*p).b = 'b';

	printf("a=%d  b=%c", (*p).a, (*p).b);
	printf("\n");
	printf("a=%d  b=%c", p->a, p->b);
	system("pause");
	return 0;
}

最后结果是

三.结构体的自引用:

在结构体中包含一个类型为该结构体本身的成员可以吗?

先来试一试:

struct Node
{
	int data;
	struct Node next;
};

编译显示

编译显示“next”没有被定义,可见直接在结构体中定义一个该结构体本身的变量是不合法的,正确的定义应该如下:

struct Node
{
	int data;
	struct Node *next;//定义成指针
};

四,结构体的不完整声明

在结构体套结构体使用时,在不声明的情况下,两个相互套用,虽然在vs2013下能够通过运行,但是最好还是提前声明。

struct B;
struct A
{
	int _a;
	struct B* pb;
};
struct B
{
	int _b;
	struct A* pa;
};

五,结构体变量的定义和初始化(类比数组)


//声明:
struct Test
{
	int a;
	char b;
};

//定义:
struct Test p1;
struct Test p2;

//初始化:
//p1 = {a,b}//这种初整体始化的方式是不允许的,类比数组;
struct Test p1 = {20,'a'};

struct Node
{
	int d[10];
	struct Test;
	struct Node *next; 
};

struct Node n = { { 1, 2, 3 }, { 20, 'a' }, NULL };//结构体嵌套初始化 

六,结构体内存对齐

前面了解了结构体的概念,那么结构体的大小如何计算呢?它是是不是也可以类比数组呢?经过验证我们发现,当结构体内声明的类型顺序不一样时,结构体的大小也不一样。所以,计算结构体大小不能类比数组,这里还需要考虑内存对齐问题。

1.为什么存在内存对齐呢?

(1)、平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。

(2)、性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。

2.结构体的内存对齐规则:

【1】.第一个成员在与结构体变量偏移量为0的地址处。
【2】.其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
    对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。
    VS中默认的值为8
    linux中的默认值为4
【3】.结构体总大小为最大对齐数(每个成员变量除了第一个成员都有一个对齐数)的整数倍。
【4】.如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

现在来看一个例子:

struct S1
{
	char c1;
	int i;
	char c2;
};

结构体s1里的char c1的对齐数是1,第一个成员默认偏移量为0;int i的对齐数是4,1不能整除自身对齐数,所以得加上偏移量3;char c2的对齐数是1,8能够整除自身对齐数,就不需要加偏移量;最后看结构体总的大小为9,不能整除结构体中最大对齐数4,所以加偏移量3,最后结构体大小为12.

用图形理解如下:

结构体嵌套问题:

struct S1
{
	char c1;//1+3
	int i;//4+4
	char c2;//8+1
};//12

struct S2
{
	int a[5];//20
	struct S1 s1;//12+20
	double d;//8
};//40

结构体s2里,int a[5]的对齐数为20;然后嵌套了结构体S1,嵌套的结构体对齐到自己的最大对齐数的整数倍处,这里20能够整除最大对齐数4,所以不需要偏移量;double d 对齐数是8,32能够整除8,;结构体总大小为40,能够整除最大对齐数8;最后S2的大小为40.

七,结构体传参

   结构体传参传指针。

#include<stdio.h>
#include<windows.h>

struct S
{
	int date[1000];
	int num;
};
void print(struct S *s)
{
	printf("%d\n", s->num);//20
}
int main()
{
	struct S s = { { 1, 2, 3, 4 }, 20 };
	print(&s);
	system("pause");
	return 0;
}

【枚举】

一,枚举的定义

//一般的定义方式如下:
enum enum_type_name
{
ENUM_CONST_1,
ENUM_CONST_2,
...
ENUM_CONST_n
} enum_variable_name;

注意:enum_type_name是自定义的一种数据数据类型名,而enum_variable_name为
enum_type_name类型的一个变量,也就是我们平时常说的枚举变量。实际上enum_type_name
类型是对一个变量取值范围的限定,而花括号内是它的取值范围,即enum_type_name类型
的变量enum_variable_name只能取值为花括号内的任何一个值,如果赋给该类型变量的值
不在列表中,则会报错或者警告。ENUM_CONST_1、ENUM_CONST_2、...、
ENUM_CONST_n,这些成员都是常量,也就是我们平时所说的枚举常量(常量一般用大写)。

                                                                                           ------------------- ---摘录《c语言深度解剖》

二,枚举的初始化

enum Color
{
	RED,
	BIACK,
	BLUE,
};
 
int main()
{
	enum color c;
	c = BLUE;
	printf("%d\n", RED);//0
	system("pause");
	return 0;
}

{}里的内容是枚举类型的可取值,也叫枚举常量,这些可取值都是有值的,默认从0开始,一次递增1.

在定义的时候也可以赋值(枚举常量)。

三,下面再看看枚举与#define宏的区别:

1),#define宏常量是在预编译阶段进行简单替换。枚举常量则是在编译的时候确定其值。
2),一般在编译器里,可以调试枚举常量,但是不能调试宏常量。
3),枚举可以一次定义大量相关的常量,而#define宏一次只能定义一个。

【联合体】

一,联合体类型的声明,定义与初始化

//声明
union Un
{
	int a;
	float b;
	char c;
};

int main()
{
	union Un x;//定义
	x.a = 25;
	printf("%d\n", x.a );
	x.b=3.14;
	printf("%lf\n", x.b); //3.140000
	x.c = 'c';
	printf("%c\n", x.c);
	system("pause");
	return 0;
}

二,联合的特点

            在union中所有的数据成员共用一个空间,同一时间只能储存其中一个数据成员,所有的数据成员具有相同的起始地址。这样一个联合变量的大小,至少是最大成员的大小。

看一个例子:

union Un
{
	int i;
	char c;
};

int main()
{
	union Un un;
	
	printf("%d\n", &(un.i)); 
	printf("%d\n", &(un.c)); 

	un.i = 0x11223344;
	un.c = 0x55;
	printf("%x\n", un.i); //11223355
	system("pause");
	return 0;
}

编译后,我们发现,结构体成员的地址都是相同的.

现在看看内存:

对union型的成员的存取都是相对于该联合体基地址的偏移量为0处开始,也就是联合体的访问不论对哪个变量的存取都
是从union的首地址位置开始。然后根据大端小端判断,计算机是小端模式,所以结果是0x11223355。

【判断当前计算机的大小端存储】

int check_sys()
{
	int i = 1;//0000  0000 0000 0000 0000 0000 00001
	return (*(char *)&i);//对&i解引用就是首地址;
}
int main()
{
	int ret = check_sys();
	if (ret == 1)
	{
		printf("xiaoduan\n");
	}
	else
	{
		printf("daduan\n");
	}
	system("pause");
	return 0;
}

看内存,低位保存在低地址中,编译后结果也是小端。

三,联合体大小是计算

union Un
{
	char c[5];//5
	int i;//4
};
int main()
{
	
	printf("%d\n",sizeof(union Un));//8
	
	system("pause");
	return 0;
}

联合体大小至少是最大成员的大小;当最大成员不是最大对齐数的整数倍时,就要对齐到最大对齐数的整数倍。










猜你喜欢

转载自blog.csdn.net/snowyuuu/article/details/80493079