嵌入式初学-C语言-二六

结构体数组的定义

什么时候需要结构体数组

比如:需要管理一个学生对象,只需要定义一个 struct student majie{…};

假如:需要管理多个学生对象,此时就需要一个结构体数组,struct student students[64]

三种形式定义结构体数组
  1. 第一种:先定义结构体类型,然后定义结构体变量,再将变量存储到结构体数组中

  1. 定义结构体类型,然后定义结构体数组并初始化

  1. 第三种:定义结构体类型同时定义数组并初始化

  1. 第四种:定义结构体类型同时定义结构体数组,然后通过索引给结构体成员赋值

注意:

  1. 结构体数组名访问结构体成员:

     2.格式:结构体数组名 ->成员名

小贴士

结构体数组名访问结构体成员:

格式:结构体数组名 -> 成员名

案例:

/**
* 结构体数组案例:对候选人得票的统计程序。设有3个候选人,每次输入一个得票的候选人的名字,要求最后输出各人
得票结果。
*/
#include <stdio.h>
#include <string.h>
/**
* 定义一个候选人结构体(对象)
*/
struct Person
{
char name[20];// 名字
int count; // 票数
};
// 定义候选人数组,并初始化
struct Person persons[3] = {
{"张月",0},
{"李湘",0},
{"第五名",0}
};
void main()
{
int i,j;
char leader_name[20];// 用来接收被投票的候选人姓名
// 使用一个循环,完成10次投票
for(i = 0; i < 10; i++)
{
printf("请输入您要投票的候选人姓名:\n");
scanf("%s",leader_name);
// 给被投票的候选人+1票
for(j = 0; j < 3; j++)
{
// 如何判断两个字符串相等
if(strcmp(leader_name,persons[j].name) == 0)
{
persons[j].count++;
}
}
}
printf("\n投票结果:\n");
for(i = 0; i < 3; i++)
{
printf("%5s:%d\n",persons[i].name,persons[i].count);
}
}

结构体指针

定义:结构体类型的指针变量指向结构体变量或者数组的起始地址

语法:struct 结构体名 *指针变量列表;

struct Dog
{
char name[20];
int age;
};
struct Dog dog;
struct Dog *p = &dog;

结构体成员访问

  1. 结构体数组名访问结构体成员
    1. 格式:结构体数组名 ->成员名;
  2. 结构体成员访问符
    1. .:左侧时结构体变量(结构体对象/实例):也可以叫做结构体对象访问符;右侧是结构体成员
    2. ->:左侧是一个指针,也可以叫做结构体指针访问成员符:右侧是结构体成员
  3. 访问结构体成员有两种类型,三种方式:
    1. 类型1:通过结构体对象访问成员

              2.类型2:通过指针提访问成员

                    1.指针引用访问成员

                     2.指针解引用间接访问成员

 结构体数组元素访问

struct Stu
{
int id;
char name[20];
float scores[3];
} stus[3] = {
{1,"张三",{86,88,56}},
{2,"李四",{75,66,78}},
{3,"王五",{70,99,90}}
};
// 取数据 --- 下标法
printf("%s,%2f\n",stus[1].name,stus[1].scores[2]);// 李四,78
// 结构体成员引用符号:-> 指针法
printf("%s,%2f\n",stus -> name,stus -> scores[2]);// 张三,56
printf("%s,%2f\n",(stus + 1)-> name,(stus + 1)-> scores[2]);// 李四,78
printf("%s,%2f\n",(*(stus + 1)).name,(*(stus + 1)).scores[2]);// 李四,78

小贴士:

  1. 结构体是自定义是数据类型,是数据类型,用法类似于基本类型的int
  2. 结构体数组时存放结构体对象的数组,类似于int数组存放int数据
  3. 基本类型数组怎么用,结构体数组就怎么用à可以遍历,可以作为形式参数,也可以做指针

结构体类型案例: 

#include <stdio.h>
// 定义结构体
struct Cat
{
char *name;// 姓名
int age;// 年龄
char color[20];// 颜色
}
// 1.结构体类型作为形式参数
void test1(struct Cat c);
// 2.结构体类型作为形式参数,结构体类型作为返回值类型
struct Cat test2(struct Cat c);
// 3.结构体数组作为形式参数
void test3(struct Cat cats[],int len);
// 4.结构体数组作为形式参数,结构体指针作为返回值数据类型
struct Cat *test4(struct Cat cats[],int len);

测试:

int main()
{
// 定义结构体对象
struct Cat cat = {"小黑",8,"baise"};
// 结构体对象作为实际参数
test1(cat);
// 定义结构体类型对象
struct Cat cat1 = {"小白",8,"heise"};
// 调用函数并接收返回值
struct Cat c1 = test2(cat1);
// 通过返回值访问结构体对象的成员
printf("%s==%d==%s\n",c1.name,c1.age,c1.color);
// 定义结构体数组
struct Cat cats[3] = {
{"汤姆",16,"蓝色"},
{"杰瑞",18,"褐色"},
{"唐老鸭",19,"白色"}
};
// 结构体数组名作为实际参数
test3(cats,3);
// 定义结构体数组并初始化
struct Cat cats1[3] = {
{"汤姆",16,"蓝色"},
{"杰瑞",18,"褐色"},
{"唐老鸭",19,"白色"}
};
// 调用函数
struct Cat *p = test4(cats1,3);
struct Cat *w;
// 通过指针运算遍历数组
for(w = p; w < p + 3; w ++)
{
// p[i][j] = *(p[i]+j) = *(*(p+i)+j) 三者等价
// 通过结构体指针访问符访问结构体的成员
printf("%s----%d----%s\n",w -> name,w -> age,w -> color);
}
}

结构体求大小

规则:(字节对齐)数据在内存中存储在其类型大小的整数倍上

  1. 首先保证结构体中的成员保存在自身的对其边界
  2. 在满足1的条件下,最终大小要满足组大成员所占存储单元的整数倍

为什么使用字节对齐:

       节省内存,提高访问效率。

柔性数组:

柔性数组不占有结构体的大小

 案例:

/**
* 求结构体数据类型的大小
*/
#include <stdio.h>
// 定义测试结构体
struct TEST1
{
char a;// 1
int b; // 4
};
struct TEST1_1
{
char a;// 1
int b;// 4
}__attribute__((packed));// 取消字节对齐,取消之后,结构体数据类型大小就等于其所有成员的数据类型之和
struct TEST1_2
{
char a __attribute__((aligned(2)));
int b;
};
struct TEST2
{
char a;// 1
short c; // 2
int b; // 4
};
struct TEST3
{
int num;// 4
char name[10];// 10
char sex;// 1
int age;// 4
double score;// 8
};
struct TEST4
{
int num;// 4
short name[5];// 10
char sex;// 1
int age;// 4
int scores[2];// 8
};
int main()
{
// 创建结构体变量
struct TEST1 test1;
struct TEST2 test2;
struct TEST3 test3;
struct TEST4 test4;
struct TEST1_1 test1_1;
struct TEST1_2 test1_2;
// 计算大小
printf("%lu\n",sizeof(test1));
printf("%lu\n",sizeof(test2));
printf("%lu\n",sizeof(test3));
printf("%lu\n",sizeof(test4));
printf("%lu\n",sizeof(test1_1));
printf("%lu\n",sizeof(test1_2));
}

推导过程:

字节对齐:

概念:

对齐:计算机中内存空间都是按照字节byte划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定类型变量的时候经常在特定的内存地址访问,这就需要各种类型数据按照一定的规则在空间上排列,而不是顺序的一个接一个的排放,这就是对齐。如果一个变量的内存地址正好位于它长度的整数倍,被称做自然对齐。

字节对齐涉及到的四个重要基本概念

1.数据类型自身的对齐值:

    对于char型数据,其自身对齐值为1,对于short型为2,对于int,float等类型,其自身对齐值为4,单位字节。

2.结构体自身对齐值:其成员中自身对齐值最大的那个值。

3.指定对齐值:__attribute__ ((aligned (value)))指定的对齐值value。

4.有效对齐值:自身对齐值和指定对齐值中小的那个值。

共用体/联合体类型

定义:

使几个不同的变量占用同一段内存的结构,共用体按定义的需要存储空间最大的成员来分配存储单元,其他成员也使用该空间,他们的首地址是相同的。

定义格式:

union 共用体名称

{

       数据类型 变量名

       …

}

共用体的定义和结构体类型类似:

  1. 可以有名字,也可以匿名
  2. 共用体在定义时也可以定义共用体变量
  3. 共用体在定义时也可以初始化成员
  4. 共用体也可以作为形参和返回值类型使用
  5. 共用体也可以定义共用体数组

……

也就是说结构体的语法共用体都支持。

注意:

  1. 共用体弊大于利,尽量少用,一般很少用
  2. 共用体变量在某一刻只能存一个数据,并且也只能取出一个数
  3. 共用体和结构体都是自定义数据类型,用法类似于基本数据类型
    1. 共用体可以是共用体的成员,也可以是结构体的成员
    2. 结构体可以是共用体的成员,也可以是共用体的成员

案例: 

/**
* 共用体
*/
#include <stdio.h>
// 定义共用体
union S
{
char a;
float b;
int c;
};
// 共用体作为共用体的成员
union F
{
char a;
union S s;
};
// 共用体作为结构体的成员
struct G
{
int a;
union S s;
};
// 定义一个结构体
struct H
{
int a;
char b;
};
// 结构体作为结构体成员
struct I
{
int a;
int b;
struct H h;
};
// 共用体作为结构体成员
struct J
{
int a;
char b;
union S s;
};
void test1()
{
// 定义共用体类型
union Stu
{
int num;
char sex;
double score;
};
// 定义匿名共用体:匿名共用体一般作为结构体成员或者其他共用体成员
union
{
int a;
char c;
} c;
printf("%lu,%lu\n",sizeof(union Stu),sizeof(c));
}
void test2()
{
union C
{
int a;
char b;
};
// 定义变量
union C c;
// 存数据
c.a = 10;
c.b = 'A';
printf("%d---%d\n",c.a,c.b);// 取数据
c.a += 5;
printf("%d---%d\n",c.a,c.b);// 取数据
union E
{
char *f;
long a;
int b;
} e = {"hello world!"};
printf("%s,%p---%ld,%p---%d\n",e.f,&(e.f),e.a,&(e.a),e.b);
}
int main()
{
test1();
test2();
}

枚举

定义:

一般情况下,定义静态常量,使用宏定义(#define 宏名称 值),宏定义非常适合没有关联关系的常量,但是有时候,可能需要对一组拥有关联关系的量进行定义“周一~周天”等等,那么使用宏定义就不是很清晰,这时候就需要使用到枚举,

枚举的存在就是将多个拥有关联关系的常量组织到一起,提高代码的可读性

说明:

  1. 枚举类型定义了一组常量,在开发中直接使用常量(常用)
  2. 枚举类型也可以类似结构体一样定义变量等操作。(不常用)
  3. 枚举常量有默认值,从0开始依次加1,可以在定义是指定它的值,如果个别没有赋值,可以根据赋值依次加1推导

特点:

  1. 定义了一组常量,类似于定义了多个自定义常量(宏定义)
  2. 提供了代码的可读性(避免了魔术数字)

语法:

  1. 定义枚举类型名以后就可以定义该枚举类型的变量,

其定义形式为

           enum  枚举类型名  变量表;

  1. 在定义枚举类型的同时定义该枚举类型的变量。

一般形式为

        enum  枚举类型名{ 枚举元素列表 }变量表;

  1. 直接定义枚举类型变量。

一般形式为

           enum  { 枚举元素列表 }变量表;

typedef

说明:给类型重命名不会影响到类型本身

作用:给已有的类型起别名

语法:typedef 已有类型名 新别名

使用:

应用场景:

    • 数据类型复杂(结构体,共用体,枚举,结构体指针)时使用
    • 为了跨平台兼容性,例如:

本章小结

1. C语言中有两类数据:一类是系统已经定义好的标准数据类型,也称基类型,如:int、long、char、float、double等,可以直接使用;另一类是用户根据需要自己设计的数据类型,必须先声明,然后才能使用,如:结构体、共用体等。

2. 结构体由若干个数据组成的,他们可以是不同类型的。

3. 同类型的结构体变量可以相互赋值,但不能用结构体变量名对结构体进行整体输入输出。可以对结构体变量的成员进行赋值、比较、输入输出等操作。引用结构体成员的方法有:

    ① 结构体变量.成员名        “.”成员运算符

    ② (*指针变量).成员名   指针变量是指向结构体变量的

③ p->成员运算符      p是指向结构体变量的指针变量

4. 结构体变量的指针就是结构体变量的起始地址。

5. 共用体与结构体不同,其成员共享同一段存储空间,因此各成员的值不会同时存在,在某一瞬间,只有最后一次被赋值的成员是有意义的。

6. 枚举类型是把可能的值全部一一列出来,枚举变量的值只能是其中的一个。

7. 可以用typedef对已有的类型重新命名,以方便使用。但typedef并不能产生新的数据类型。

案例: 

//类型重命名
#include <stdio.h>
struct Student
{
int age;
char* name;
double score;
int arr[3];
};
typedef struct Student Stu_t;
typedef Stu_t* pStu_t;
void test1()
{
Stu_t s1 = {23, "zhangsan", 23.33, {11, 22, 33}};
printf("%d, %s, %f, %d\n", s1.age, s1.name, s1.score, s1.arr[0]);
//Stu_t *p = &s1;
Stu_t* p;
p = &s1;
pStu_t p2;
p2 = p;
printf("%d, %s, %f, %d\n", p2->age, p2->name, p2->score, p2->arr[0]);
}
int main()
{
test1();
return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值