结构体struct :类型设计
- 形参的改变不会改变实参
- 单独的定义会存在数组名的退化,但在结构体中的数组不存在数组名的退化
3)调用对象的属性使用 对象名.属性
调用指针的属性使用 指针->属性
struct Student//类型设计
{
char s_id[10];
char s_name[10];
int s_age;
}stud; //对象
void InitStudent(struct Student *stud) //初始化结构体 传入自己的地址
{
printf("id name age \n");
scanf_s("%s",stud->s_id);
scanf_s("%s",stud->s_name); //数组名本身就表示地址
scanf_s("%d",&stud->s_id); //整形 要传地址 否则形参不能改变实参
}
void PrintStudent(const struct Student *stud) //输出结构体内容 不希望改变参数值
{
assert(stud!=NULL);
printf("id name age \n");
scanf_s("%s",stud->s_id);
scanf_s("%s",stud->s_name); //数组名本身就表示地址
scanf_s("%d",stud->s_id); //整形 要传地址 否则形参不能改变实参
}
int main()
{
struct Student x; //x被称为对象,操纵结构体变量,用(.)进行使用
struct Student x = {"09001","lili",18}; //按照顺序初始化,和结构体顺序要保持一致
struct Student *p = &x;
printf("%s \n",(*p).s_id); //指针的属性输出
printf("%s \n",p->s_id);
struct Student y = x; //可以进行初始化
struct Student z = {};
z = x; //可以进行赋值
printf("%s \n",x.s_id); //输出使用.运算符
strcpy_s(x.s_id,10,"09001");//引入#in clude<string.h>,vs2019不用strcpy,改为strcpy_s
int a;//a被称为变量
InitStudent(&studx); //初始化studx 使用自己本身
PrintStudent(&studx);
return 0;
}
int main() //字符串
{
char s_id[20] = {"020303"}; //ok 初始化可以
char s_id[20];
s_id = {"292929"} //err 不能赋值 数组名退化为指针常量 不能进行赋值
int ar[10]; 数组定义:类型 大小
sizeof(ar); //sizeof(int)*10 40
}
结构体赋值
- 定义结构体变量 { }初始化
- 用已定义的结构变量初始化
- 结构体类型相同的变量可以作为整体相互赋值 //赋值
结构体类型指针
- (*p).成员名
- p->成员名
#include<stdio.h>
#include<iostream>
using namespace std;
struct Student
{
char s_name[8]; //8
int s_age;//12
};
void Prinf(const struct Student* sp) //打印函数 不会改变 加上常属性
{
cout << sp->s_age << " " << sp->s_name << endl;
}
int main()
{
int n = sizeof(Student);
cout << n<<endl; //12
struct Student sta = { "lee",21 };
Student* stb = &sta; //初始化
char name[20] = { 0 };
(*stb).s_age = 18;
stb->s_age = 18;
strcpy_s(name, stb->s_name);
printf("%d\n", stb->s_age);
cout << (*stb).s_name << endl;
Prinf(stb);
return 0;
}
结构体的内存对齐
- 结构体变量的首地址,必须是结构体变量中的“最大基本数据类型成员所占字节数”的整数倍
- 结构体变量中的每个成员相对于结构体首地址的偏移量,都是该成员基本数据类型所占字节数的整数倍
- 结构体变量的总大小,为结构体变量中“最大基本数据类型成员所占字节数”的整数倍
基本数据类型字节数
注意:
CPU不是以逐字节读写内存,而是以2,4,8的倍数的字节块来读写内存,因此基本数据类型的地址必须是2,4,8的倍数,那就要求各种数据类型按照一定的规则在空间上排列,这就是对齐
struct node
{
char cha; //1 偏移7个字节 8
double da; //16
char chb; //1 偏移7个字节 24
}; //sizeof(struct node) = 24
struct node
{
char ch[3]; //3 偏移1个字节
int ia; //8
}; //sizeof(struct node) = 8
struct sdate //12
{
int year;
int month;
int day;
};
struct Student
{
char s_id[10]; //10
char s_name[8];//18
struct sdate birthday; //12+18 30 基本数据类型 int
double grade; //30+8 38 偏移2 到40 最大数据类型
}; //sizeof(Student) = 40
指定对齐值
预处理指令 #pragma pack(n) 可以改变默认对齐数 n = 1,2,4,8,16
需要以**#pragma pack()结束**,表示该种对齐方式到此为止
VS中默认值 = 8; gcc默认值 = 4
#pragma pack(4)
struct node
{
char cha; //1
int ia; //8
short sa; //12
}; //sizeof(node) = 12
#pragma pack()
#pragma pack(1)
struct node
{
char cha; //1
int ia; //1+4 = 5
short sa; //5+2 = 7
}; //sizeof(node) = 7
#pragma pack()
#pragma pack(8)
struct anode
{
short sa; //2
long int ib; //8
};//8
struct bnode
{
char ca; //1 8
struct anode ad; //8+8 16
double dx; //24
}; //24
#pragma pack()
例题:
struct Student
{
char s_name[20];
int age;
int score;
};
int Sumscore(struct Student* sp,int n) //总成绩
{
int sum = 0;
for(int i = 0; i < n; i++)
{
sum += sp[i].score;
}
return sum;
}
int Ave(struct Student* p, int n) //平均成绩
{
int sum = Sumscore(p, n);
int ave = sum / n;
return ave;
}
void SortByScore(struct Student* p, int n) //按成绩排序
{
for (int i = 0; i < n; i++)
{
if (p[i].score > p[i + 1].score)
{
swap(p[i].score, p[i + 1].score);
}
}
}
void Printf(struct Student* p, int n)
{
for (int i = 0; i < n; i++)
{
cout << p[i].s_name << " " << p[i].age << " " << p[i].score << endl;
}
}
int main()
{
struct Student cla[ ] =
{
{"lee",18,40},
{"lolo",40,23},
{"rere",34,23},
};
int k = sizeof(cla) / sizeof(cla[0]);
int sum = Sumscore(cla, k);
cout << sum << endl;
int ave = Ave(cla, k);
cout << ave << endl;
SortByScore(cla, k);
Printf(cla, k);
return 0;
}
联合体union
1.结构体各成员有各自的内存空间,在联合体中,各成员共享同一段内存空间。
2.一个联合体变量的长度等于各成员中最长的长度
3.一个联合体类型必须经过定义之后,才能使用它,才能把一个变量声明定义为联合体类型
共享指的是联合体变量可被赋予任一成员值,但每次只能赋一种值,赋入新值则冲去旧值。
union Data
{
short st;
char cs[2];
}x;
枚举:把需要的取值一一列举
特点:
1)受到限制的有符号整形类型;
2)枚举值必须是整型类型;
3)枚举变量的取值范围必须是枚举值集合
4)枚举变量的取值只能赋值为{枚举值集合中值};
5)枚举变量不能++,–
//不指定值,默认从0开始 往后逐个加一
enum week{mon,tue,wend,thurs,fri,sat,sun};
//指定值
enum week{mon = 1,tues = 2,wed = 3,thurs = 4};
//只给第一个名字指定值 往后逐个加一
enum week{mon = 1,thes,wed,thurs,fri,sat,sun};
enum week x = Mon;
枚举与#define的区别
1.增加代码的可读性和可维护性
2.枚举类型有类型检查,更严谨
3.封装性好
3.一次可以定义多个常量
//整型转换成枚举
int main(){
enum day{saturday,sunday,monday,tuesday,wednesday,thursday,friday}workday;
int a=1;
enum day weekend;
weekend=(enum day)a;
printf("weekend:%d",weekend);
return 0;
}
字符串
- print输出格式