c语言基础学习09_复合类型

简介: =============================================================================涉及到的知识点有:一、结构体1、定义结构体struct和初始化、2、访问结构体中的成员、3、结构体的内存对齐模式、4、结构体中元素的位字段、5、结构体中的数组、6、结构体的嵌套、7、结构体的赋值、8、通过指针访问结构体成员(即指向结构体的指针)、9、通过指针访问结构体数组(即指向结构体数组的指针)、10、结构体变量的指针成员与浅拷贝、深拷贝的操作(即结构中的数组成员和指针成员)。

=============================================================================
涉及到的知识点有:
一、结构体
1、定义结构体struct和初始化、2、访问结构体中的成员、3、结构体的内存对齐模式、4、结构体中元素的位字段、
5、结构体中的数组、6、结构体的嵌套、7、结构体的赋值、8、通过指针访问结构体成员(即指向结构体的指针)、
9、通过指针访问结构体数组(即指向结构体数组的指针)、10、结构体变量的指针成员与浅拷贝、深拷贝的操作(即结构中的数组成员和指针成员)。
二、联合体
三、枚举类型
1、c语言中枚举的定义、2、改变枚举的默认值。
四、typedef
课堂练习:在堆中处理结构体的指针成员。
=============================================================================
=============================================================================
  之前学过的数据类型叫做基本数据类型,也叫做单一数据类型。
  例如:整型、浮点型、指针类型。

  结构体、联合体、枚举类型、typedef叫做符合类型。

一、结构体

1、定义结构体struct和初始化
--------------------------------------
01、定义结构体struct不初始化

vs2017下示例代码如下:

 1 #include <stdio.h>
 2 #include <string.h>
 3 #pragma warning(disable:4996)
 4 
 5 struct student
 6 {
 7     char name[100];
 8     int age;
 9 };
10 
11 int main()
12 {
13     auto struct student st;    //定义了一个student类型的结构体变量,名字叫st。不初始化成员变量的值。
14     //st.name = "刘德华";    //不能这么写,因为name是一个数组名,是一个数组变量,而"刘德华"是常量。
15     strcpy(st.name, "刘德华");
16     st.age = 20;
17     printf("name = %s, age = %d\n", st.name, st.age);    //name = 刘德华, age = 20
18 
19     return 0;
20 }

-----------------------------------------------------------------------------

02、定义结构体struct并初始化

vs2017下示例代码如下:

 1 #include <stdio.h>
 2 #include <string.h>
 3 #pragma warning(disable:4996)
 4 
 5 struct student
 6 {
 7     char name[100];
 8     int age;
 9 };
10 
11 int main01()
12 {
13     auto struct student st = { "周星驰", 40 };    //定义了一个student类型的结构体变量,名字叫st。并同时初始化成员变量的值。
14     printf("name = %s, age = %d\n", st.name, st.age);    //name = 周星驰, age = 40
15 
16     return 0;
17 }
18 
19 int main02()
20 {
21     auto struct student st = { "周杰伦" };    //定义了一个student类型的结构体变量,名字叫st。并同时初始化成员变量的值。
22     printf("name = %s, age = %d\n", st.name, st.age);    //name = 周杰伦, age = 0
23 
24     return 0;
25 }
26 
27 int main03()
28 {
29     auto struct student st = { 0 };    //定义了一个student类型的结构体变量,名字叫st。并同时初始化成员变量的值。
30     printf("name = %s, age = %d\n", st.name, st.age);    //name = , age = 0
31 
32     return 0;
33 }
34 
35 int main()
36 {
37     auto struct student st = { .age = 30, .name = "张学友" };    //定义了一个student类型的结构体变量,名字叫st。并同时初始化成员变量的值。
38     printf("name = %s, age = %d\n", st.name, st.age);    //name = 张学友, age = 30
39 
40     return 0;
41 }

=============================================================================
2、访问结构体中的成员

  . 点操作符
=============================================================================
3、结构体的内存对齐模式

  编译器在编译一个结构的时候采用内存对齐模式。
  结构体总是以最大的成员变量作为对齐单位。(即一个结构体变量的成员总是以最大的那个元素作为对齐单位的。)

  结构体对齐说明,如下图所示:

  结构体成员出现的先后顺序不同,导致结构体会占用不同的内存,如下图所示:

  如果结构体成员出现数组,那么是以数组的具体每个成员作为对齐标准。(即:不会以数组本身的大小作为对齐单位的。)

  如果一个结构体中所有成员都是一种类型,那么这个结构体变量在内存中就基本和一个数组类似了。

  结构体变量的地址就是这个结构体首元素的地址
-----------------------------------------------------------------------------
vs2017下示例代码如下:

 1 #include <stdio.h>
 2 
 3 struct D
 4 {
 5     char a[10];
 6     char b;
 7 };
 8 
 9 struct E
10 {
11     char a1;
12     short a2;
13     int a3;
14 };
15 
16 //结构体对齐小规律:总是以2的倍数字节对齐的,因为计算机是二进制的,且c语言里面没有3个字节的数据。
17 //1个字节,2个字节,4个字节,8个字节。1个字节可以等同于占用2个字节。
18 struct F
19 {
20     char a1;
21     short a2;
22     int a3;
23     short a4;
24     char a5;
25 };
26 
27 int main()
28 {
29     struct D d = { 0 };
30     printf("%u\n", sizeof(d));    //11
31 
32     char *s = (char *)&d;    //结构体D *类型和char *类型不兼容,需要强转。
33     s[0] = 'a';
34     s[1] = 'b';
35     s[10] = 'c';
36     printf("%s%c\n", d.a, d.b);    //abc
37     //由以上可知,所有的数据类型,都可以把它抽象成一个char的数组。包括结构体变量也不例外。
38     //但是呢结构体变量本身也是个变量,所以不能直接把它当数组名来用,必须要取地址。
39     printf("%p, %p, %p\n", &d, d.a, &s[0]);    //00CFF9A4, 00CFF9A4, 00CFF9A4
40     //结构体变量的地址就是这个结构体首元素的地址。
41 
42     //可以通过指针来利用被闲置的内存。
43     struct E e = { 1, 2, 3 };
44     s = &e;
45     s[1] = 10;
46     printf("%p\n", &e);    //可以通过调试的方法看下内存。
47 
48     return 0;    //在这一行加一个断点。
49 }
结构体对齐小规律:总是以2的倍数字节对齐的。如下图所示:

=============================================================================
4、结构体中元素的位字段

定义一个结构体的时候可以指定具体元素的位长。

小知识补充:

一个字节的二进制表示的最大整数和最小整数是多少?
首先分清楚8个bit可以表示256种可能,但是表现为无符号的整数时是0到255,因为0到255共计256个数。

无符号位时:
  无符号的正数的最大值:max = 1111 1111 = 2^8 - 1 = +255
  无符号的正数的最小值:min = 0000 0000 = 2^0 = 1 = +0

有符号位时:
注意:在计算机内部,正数和负数都是以补码的方式存放的。又因为正数的补码是其本身,所以简言之:所有的负数都是以补码的方式存放的。
而打印的时候是以原码输出的。

  有符号的正数的最大值:max = 0111 1111 = (2^8) / 2 - 1 = +127
  有符号的负数的最小值:min = 1000 0000 = -128
  -128到127共计256个数。
-----------------------------------------------------------------------------
vs2017下示例代码如下:

 1 #include <stdio.h>
 2 struct A
 3 { 
 4     unsigned char a : 2;    //a有2个bit大小。无符号:00~11            0~3
 5     unsigned char b : 4;    //b有4个bit大小。无符号:0000~1111        0~15
 6     char c : 4;                //c有4个bit大小,有符号:最高位是符号位    -8~7    
 7     char d;                    //d有8个bit大小,有符号:最高位是符号位    -128~127
 8 };
 9 
10 //结构体位字段的应用:霓虹灯管、音乐喷泉。每个小灯只有两种状态:亮或灭。
11 //硬件特别差的情况下使用c语言写程序。
12 struct B
13 {
14     unsigned char a1 : 1;
15     unsigned char a2 : 1;
16     unsigned char a3 : 1;
17     unsigned char a4 : 1;
18     unsigned char a5 : 1;
19     unsigned char a6 : 1;
20     unsigned char a7 : 1;
21     unsigned char a8 : 1;
22 };
23 
24 struct C
25 {
26     char a1 : 1;
27     int a2 : 1;    //这样写是不合理的!
28 };
29 
30 int main()
31 {
32     struct A a;
33     a.a = 5;
34     printf("%x\n", a.a);    //101-->01-->1(无符号的16进制的1)
35     a.b = 16;
36     printf("%x\n", a.b);    //1 0000-->0000-->0(无符号的16进制的0)
37     a.c = 13;    //1101
38     printf("%d\n", a.c);    //1101-->最高位为符号位,-101-->-3(有符号的10进制-3)
39     a.d = 13;
40     printf("%d\n", a.d);    //13(有符号的10进制13)
41 
42     struct B b;
43     printf("%u\n", sizeof(b));    //1
44     b.a1 = 1;
45     b.a2 = 0;
46     b.a3 = 1;
47     b.a4 = 0;
48     b.a5 = 1;
49     b.a6 = 0;
50     b.a7 = 1;
51     b.a8 = 0;
52 
53     struct C c;
54     printf("%u\n", sizeof(c));    //8    因为是以int对齐的。
55 
56     return 0;
57 }

=============================================================================
5、结构体中的数组

  结构体既可以定义变量,又可以定义数组!

  对于cpu来讲,处理int的效率是最高的,但是int比char要多占内存而已。如下例子:
  int i;    //由于i < 100;按道理来讲,我们应该定义为char i;的,但是我们却不这样做,原因如上,且i是栈变量,只是在内存中呆一会就会消失的。
  for(i = 0 ; i < 100; i++)
  {

  }
-----------------------------------------------------------------------------
vs2017下示例代码如下:

 1 #include <stdio.h>
 2 #include <string.h>
 3 #pragma warning(disable:4996)
 4 
 5 struct student
 6 {
 7     char name[20];
 8     unsigned char age;
 9     int sex;
10 };
11 
12 int main()
13 {
14     struct student st[3];    //这句话的意思是:定义一个结构体数组,有3个成员,每个成员类型都是struct student的。
15     //可以通过strcpy来进行赋值。
16     //strcpy(st[0].name, "刘德华");
17     
18     //也可以使用手动输入赋值。
19     //scanf("%s", &st[0].name);    //等价于scanf("%s", st[0].name);
20     //scanf("%d", &st[0].age);
21     //scanf("%d", &st[0].sex);
22     //printf("%s, %d, %d\n", st[0].name, st[0].age, st[0].sex);
23 
24     //用循环给每个结构体数组成员赋值
25     int i;
26     for (i = 0; i < 3; i++)
27     {    
28         printf("please input name ");
29         scanf("%s", st[i].name);    //等价于scanf("%s", &st[0].name);
30         printf("please input age ");
31         scanf("%d", &st[i].age);
32         printf("please input sex ");
33         scanf("%d", &st[i].sex);
34     }
35 
36     for (i = 0; i < 3; i++)
37     {
38         if (st[i].sex == 1)
39         {
40             printf("%s, %d, 男\n", st[i].name, st[i].age);
41         }
42         else
43         {
44             printf("%s, %d, 女\n", st[i].name, st[i].age);
45         }    
46     }
47 
48     return 0;
49 }

--------------------------------------
//也可以定义一个结构体数组的同时进行初始化

struct student st[3];    //这句话的意思是:定义一个结构体数组,有3个成员,每个成员类型都是struct student的。

struct student st[3] = { {"刘德华", 20, 1}, {"安倍", 30, 0}, {"苍老师", 25, 0} };
输出结果是:
刘德华, 20, 男
安倍, 30, 女
苍老师, 25, 女

struct student st[3] = { {"刘德华", 20, 1}, {"安倍", 30, 0} };
输出结果是:
刘德华, 20, 男
安倍, 30, 女
, 0, 女

struct student st[] = { {"刘德华", 20, 1}, {"安倍", 30, 0} };
    
    //此时循环条件需要变一下。如下:
    for (i = 0; i < sizeof(st) / sizeof(st[0]); i++)
    {

    }

-----------------------------------------------------------------------------
//小练习:把一个结构体数组按照年龄大小排序。
//方法:使用冒泡排序结构体数组。

vs2017下示例代码如下:

 1 #include <stdio.h>
 2 #include <string.h>
 3 #pragma warning(disable:4996)
 4 
 5 struct student
 6 {
 7     char name[20];
 8     unsigned char age;
 9     int sex;
10 };
11 
12 void swap_str(char *a, char *b)
13 {
14     char tmp[20] = { 0 };
15     strcpy(tmp, a);
16     strcpy(a, b);
17     strcpy(b, tmp);
18 }
19 
20 void swap_int(int *a, int *b)
21 {
22     int tmp = *a;
23     *a = *b;
24     *b = tmp;
25 }
26 
27 int main()
28 {
29     struct student st[] = { { "刘德华", 54, 1 }, { "苍井空", 23, 0 }, { "陈冠希", 56, 1 } };
30     int i, j;
31     for (i = 0; i < sizeof(st) / sizeof(st[0]); i++)
32     {
33         for (j = 1; j < sizeof(st) / sizeof(st[0]) - 1; j++)
34         {
35             if (st[j].age < st[j - 1].age)
36             {
37                 swap_str(st[j].name, st[j - 1].name);
38                 swap_int(&st[j].age, &st[j - 1].age);
39                 swap_int(&st[j].sex, &st[j - 1].sex);
40             }
41         }
42     }
43 
44     for (i = 0; i < sizeof(st) / sizeof(st[0]); i++)
45     {
46         if (st[i].sex == 1)
47         {
48             printf("%s, %d, 男\n", st[i].name, st[i].age);
49         }
50         else
51         {
52             printf("%s, %d, 女\n", st[i].name, st[i].age);
53         }
54     }
55 
56     return 0;
57 }
58 输出结果是:
59 苍井空, 23, 女
60 刘德华, 54, 男
61 陈冠希, 56, 男

=============================================================================
6、结构体的嵌套

  一个结构的成员还可以是另外一个结构体类型。

vs2017下示例代码如下:

 1 #include <stdio.h>
 2 
 3 struct A
 4 {
 5     char a1;
 6 };
 7 
 8 struct B
 9 {
10     struct A a;
11     char a2;        //如下图所示:
12     int a1;
13 };
14 
15 //error C2016 : C 要求一个结构或联合至少有一个成员。
16 //但是在c++中,下面这个语法是成立的。大小为1个字节。
17 /*
18 struct D
19 {
20 
21 };
22 */
23 
24 //注意与上面的区别:
25 struct A1
26 {
27     int a1;
28     char a2;
29 };
30 
31 struct A2
32 {
33     struct A1 a1;
34     char a2;        //上面结构体变量是作为一个整体存在的,占用了8个字节。不可能把a2补到结构体a1的后面去的,所以此时的a2是单独的一个对齐单位。如下图所示:
35     int a3;
36 };
37 
38 int main()
39 {
40     struct B b;
41     printf("%u\n", sizeof(b));    //8
42     b.a.a1= 1;
43     b.a2 = 2;
44     printf("%p\n", &b.a.a1);    //005FF8B4
45 
46     struct A2 a2;
47     printf("%u\n", sizeof(a2));    //16
48 
49     return 0;
50 }

如下图所示:结构体嵌套说明

=============================================================================
7、结构体的赋值

结构体变量之间的赋值就是简单的结构体变量之间的内存拷贝。
=============================================================================
8、通过指针访问结构体成员(即指向结构体的指针)

  -> 操作符
  (*p).a 等同于 p->a

vs2017下示例代码如下:

 1 #include <stdio.h> 
 2 #include <string.h>
 3 #pragma warning(disable:4996)
 4 
 5 struct student
 6 {
 7     char name[20];
 8     int age;
 9 };
10 
11 int main()
12 {
13     struct student st1 = { "abc", 30 };
14     struct student st2;
15     st2 = st1;    //结构体变量之间的赋值就是简单的结构体变量之间的内存拷贝。等价于下句:
16     //memcpy(&st2, &st1, sizeof(st1));
17     printf("%s, %d\n", st2.name, st2.age);    //abc, 30
18 
19     struct student *p;
20     p = &st1;    //通过指针访问结构体成员(即指向结构体的指针)
21     //strcpy((*p).name, "hello");
22     //(*p).age = 50;
23     strcpy(p->name, "hello");
24     p->age = 50;
25     printf("%s, %d\n", st1.name, st1.age);    //hello, 50
26 
27     return 0;
28 }

=============================================================================
9、通过指针访问结构体数组(即指向结构体数组的指针)

vs2017下示例代码如下:

 1 #include <stdio.h> 
 2 #include <string.h>
 3 #pragma warning(disable:4996)
 4 
 5 struct student
 6 {
 7     char name[20];
 8     int age;
 9 };
10 
11 int main()
12 {
13     struct student st[3] = { { "张三", 34 }, { "李四", 52 }, { "王二麻子", 82 } };
14     struct student *p = st;
15     p->age = 100;
16     p++;
17     p->age = 200;
18     p--;
19 
20     int i;
21     for (i = 0; i < 3; i++)
22     {
23         printf("%s, %d\n", p[i].name, p[i].age);
24     }
25 
26     return 0;
27 }

=============================================================================
10、结构体变量的指针成员与浅拷贝、深拷贝的操作(即结构中的数组成员和指针成员)

  一个结构中可以有数组成员,也可以有指针成员;
  如果是指针成员,结构体成员在初始化和赋值的时候就需要提前为指针成员分配内存!

vs2017下示例代码如下:

 1 #include <stdio.h> 
 2 #include <string.h>
 3 #pragma warning(disable:4996)
 4 
 5 struct student
 6 {
 7     char name[20];
 8     int age;
 9 };
10 
11 struct man
12 {
13     char *name;
14     int age;
15 };
16 
17 //浅拷贝。
18 int main()
19 {
20     struct student st = { 0 };
21     struct student st1 = { 0 };
22     strcpy(st.name, "刘德华");
23     st.age = 30;
24     st1 = st;    //结构体变量之间的赋值就是简单的结构体变量之间的内存拷贝。
25     printf("%s, %d\n", st1.name, st1.age);    //刘德华, 30
26 
27     struct man m = { 0 };    //m里面的name是什么?答:是空指针null。没有指向有效地址,就不能进行strcpy的操作。用堆内存解决如下:
28     struct man m1 = { 0 };
29     m.name = calloc(20, sizeof(char));    //因为struct man里面的name是char *,即通过堆内存分配空间的地址编号给它,它才能进行strcpy的操作。
30     strcpy(m.name, "张学友");
31     m.age = 40;
32 
33     m1 = m;        //浅拷贝。结构体变量之间的赋值就是简单的结构体变量之间的内存拷贝。
34     free(m.name);    //把m.name释放掉了,m1就指向了一块未知的空间了。变成野指针了。输出就变成未知的了。
35     printf("%s, %d\n", m1.name, m1.age);    //葺葺葺葺葺葺葺葺葺葺葺葺
我的GitHub地址: https://github.com/heizemingjun
我的博客园地址: http://www.cnblogs.com/chenmingjun
我的蚂蚁笔记博客地址: http://blog.leanote.com/chenmingjun
Copyright ©2018 黑泽明军
【转载文章务必保留出处和署名,谢谢!】
相关文章
|
1月前
|
存储 C语言
C语言变量类型
C语言变量类型
|
1月前
|
存储 编译器 C语言
初识C语言——详细入门(系统性学习day4)
初识C语言——详细入门(系统性学习day4)
|
1月前
|
存储 数据处理 C语言
C语言类型的变量
C语言类型的变量
18 1
|
1月前
|
存储 编译器 C语言
c语言中char的作用类型
c语言中char的作用类型
28 0
|
1月前
|
编译器 C语言
c语言中long的作用类型
c语言中long的作用类型
24 0
|
1月前
|
编译器 程序员 开发工具
c语言从入门到实战——在系统学习C语言之前所需要了解的知识
C语言是一种通用的、过程式的计算机编程语言,支持结构化编程、词汇变量作用域和递归等功能,其设计提供了低级别的存取权限,并且要求程序员管理所有的内存细节。C语言的基本构成包括数据类型(如整型、浮点型、字符型等)、运算符(如算术运算符、关系运算符、逻辑运算符等)、控制结构(如顺序结构、选择结构、循环结构等)以及函数等。此外,C语言还提供了指针的概念,这是其他许多编程语言所不具备的。指针是一个变量,其值为另一个变量的地址,通过指针可以间接访问和操作内存中的数据。C语言也支持数组、结构体、联合体等复合数据类型,以及文件操作、动态内存分配等高级功能。
51 0
C4.
|
1月前
|
存储 C语言
C语言的共用体类型
C语言的共用体类型
C4.
16 0
|
1月前
|
存储 C语言
通俗易懂的学习C语言中输入一组数并找出其最大值
通俗易懂的学习C语言中输入一组数并找出其最大值
|
1月前
|
存储 C语言
C语言 -共用体类型
C语言 -共用体类型
15 1
|
1月前
|
存储 C语言
C语言指针类型和空类型详解
C语言指针类型和空类型详解
26 0