C_结构体(精讲)
作者:互联网
目录
4、结构体指针数组空间在堆区 结构体数组元素在堆区 成员指针 指向了堆区
知识点1【结构体的概述】(了解)
基本类型:就是常见的char short int long float double
构造类型:由基本类型或其他构造类型 封装而成比如:数组、结构体、枚举、共用体
结构体:是一种或多种基本类型或构造类型的数据的集合。
知识点2【结构体类型的定义】(重要)
结构体类型的定义 必须加关键字"struct"修饰
1、先定义结构体类型 再用类型定义结构体变量(推荐)
结构体的类型 一般定义在 头文件中
定义结构体类型
struct 类型名
{
成员定义;
};//一定要加;分号
struct student 才能完整的表示结构体的类型
struct student
{
int num;
char name[32];
int score;
};
struct student lucy;//lucy就是结构体变量名
结构体中的成员 拥有独立的空间
2、定义结构体类型的同时 定义结构体变量
struct student
{
int num;
char name[32];
int score;
}lucy;//lucy是结构体变量名
struct student bob;
3、定义一次性结构体变量
struct
{
int num;
char name[32];
int score;
}lucy;//lucy是结构体变量名
无法继续定义该类型的其他结构体变量
知识点3【结构体变量的定义】(重要)
#include <stdio.h>
struct student{
int num; //这里是类型定义,不要赋值
char name[16];
float score;
};
void test01(){
struct student lucy; //没有初始化,内部数据为不确定值
printf("num = %d\n", lucy.num);
printf("name = %s\n", lucy.name);
printf("score = %f\n", lucy.score);
}
int main(int argc, char const *argv[]){
test01();
return 0;
}
知识点4【结构体变量的初始化】(重要)
创建结构体变量并初始化为非零数据
#include <stdio.h>
struct student{
int num; //这里是类型定义,不要赋值
char name[16];
float score;
};
void test01(){
struct student lucy = {100, "lucy", 88.8f};
printf("num = %d\n", lucy.num);
printf("name = %s\n", lucy.name);
printf("score = %f\n", lucy.score);
}
int main(int argc, char const *argv[]){
test01();
return 0;
}
创建结构体变量并初始化为零
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct student{
int num; //这里是类型定义,不要赋值
char name[16];
float score;
};
void test01(){
struct student lucy;
memset(&lucy, 0, sizeof(lucy));
printf("num = %d\n", lucy.num);
printf("name = %s\n", lucy.name);
printf("score = %f\n", lucy.score);
}
int main(int argc, char const *argv[]){
test01();
return 0;
}
知识点5【结构体变量的操作】(重要)
逐个成员赋值
void test02(){
struct student lucy;
lucy.num = 100;
lucy.score = 88.8;
strcpy(lucy.name, "lucy");
printf("num = %d\n", lucy.num);
printf("name = %s\n", lucy.name);
printf("score = %f\n", lucy.score);
}
int main(int argc, char const *argv[]){
test02();
return 0;
}
结构体变量整体赋值
void test02()
{
struct student lucy={100,"lucy", 88.8f};
struct student bob;
//将lucy的空间内容 赋值给bob
#if 0
//第一种方式:逐个成员赋值(不推荐)
bob.num = lucy.num;
strcpy(bob.name, lucy.name);
bob.score = lucy.score;
#endif
#if 0
//第二种方式:相同类型的结构体变量 可以整体赋值(推荐)
bob = lucy;//单纯的值拷贝(浅拷贝)
#endif
#if 1
//第三种方式:内存拷贝(浅拷贝)
memcpy(&bob, &lucy, sizeof(struct student));
#endif
printf("num = %d\n", bob.num);
printf("name = %s\n", bob.name);
printf("score = %f\n", bob.score);
}
标准输入给结构体变量赋值
void test03()
{
struct student lucy;
memset(&lucy, 0, sizeof(lucy));
printf("请输入num name score:");
// &lucy.num:取的是lucy的num成员的地址
scanf("%d %s %f", &lucy.num, lucy.name, &lucy.score);
printf("num = %d\n", lucy.num);
printf("name = %s\n", lucy.name);
printf("score = %f\n", lucy.score);
}
两个结构体变量,不能直接比大小
lucy > bob;//err
lucy.num > bob.num;//ok
lucy.name < bob.name;//此处只是比较地址的大小 而不是字符串的大小
strcmp(lucy.name, bob.name) == 0;相等
lucy.score != bob.score;//ok
知识点6【结构体指针变量】(重要)
void test04()
{
struct student lucy = {100, "lucy", 99.9f};
//定义指针变量p指向lucy
struct student *p = &lucy;
//第一种方式:*p访问 ==lucy
printf("num = %d\n", (*p).num);
printf("name = %s\n", (*p).name);
printf("score = %f\n", (*p).score);
//第二种方式:通过p(地址)直接访问成员内容
printf("num = %d\n", p->num);
printf("name = %s\n", p->name);
printf("score = %f\n", p->score);
//不管是.还是-> 只看左边是普通变量就用. 如果是地址 就用->
}
struct student lucy;
struct student *p=&lucy;
一下访问成员正确的是__AB___
A:lucy.num B:(&lucy)->num C:*p->num D:(&(*p)).num
知识点7【结构体数组】(重要)
1、遍历结构体数组
void test01()
{
HERO hero_buf[]={ {"德玛西亚", 80, 70}, {"小炮", 50, 80}, {"小法", 40, 90}};
int n = sizeof(hero_buf)/sizeof(hero_buf[0]);
int i = 0;
for ( i = 0; i < n; i++)
{
//printf("%s %d %d\n", hero_buf[i].name, hero_buf[i].def, hero_buf[i].atk);
printf("%s %d %d\n", (hero_buf+i)->name, (hero_buf+i)->def, (hero_buf+i)->atk);
}
}
2、给结构体数组的元素获取键盘输入
#include <stdio.h>
typedef struct hero
{
char name[32];
int def;
int atk;
}HERO;//HERO是类型名
#include <string.h>
void test01()
{
HERO hero_buf[3];
memset(hero_buf, 0, sizeof(hero_buf));
int n = sizeof(hero_buf)/sizeof(hero_buf[0]);
//获取键盘输入
int i = 0;
for ( i = 0; i < n; i++)
{
printf("请输入第%d个英雄的属性:", i+1);
scanf("%s %d %d", hero_buf[i].name, &hero_buf[i].def, &hero_buf[i].atk);
}
for ( i = 0; i < n; i++)
{
//printf("%s %d %d\n", hero_buf[i].name, hero_buf[i].def, hero_buf[i].atk);
printf("%s %d %d\n", (hero_buf+i)->name, (hero_buf+i)->def, (hero_buf+i)->atk);
}
}
int main(int argc, char const *argv[])
{
test01();
return 0;
}
知识点8【结构体成员指针】(重要)
typedef struct hero
{
char *name;//成员指针
int def;
int atk;
}HERO;//HERO是类型名
1、结构体变量在栈区、全局区 成员指针 指向了文字常量区
typedef struct
{
char *name;
int def;
int atk;
}HERO2;//HERO2是类型名
void test02()
{
printf("%d\n", sizeof(HERO2));
HERO2 hero;
hero.name = "德玛西亚";
hero.def = 80;
hero.atk = 100;
hero.name[2]='h';//操作文字常量区 出现段错误
printf("%s %d %d\n", hero.name, hero.def, hero.atk);
}
2、结构体变量在栈区、全局区 成员指针 指向了堆区
#include <string.h>
#include <stdlib.h>
void test03()
{
HERO2 hero;
hero.name = (char *)calloc(1, 16);
if(hero.name == NULL)
{
perror("calloc");
return;
}
strcpy(hero.name, "德玛西亚");
hero.def = 80;
hero.atk = 100;
printf("%s %d %d\n", hero.name, hero.def, hero.atk);
if(hero.name != NULL)
{
free(hero.name);
hero.name = NULL;
}
}
3、结构体空间在堆区 成员指针 指向了堆区
void test04()
{
HERO2 *p = NULL;
//为结构体指针变量p申请结构体空间
p = (HERO2 *)calloc(1,sizeof(HERO2));
if(p == NULL)
{
perror("calloc");
return;
}
p->def = 80;
p->atk = 100;
//为p->name指针成员 申请指向的空间
p->name = (char *)calloc(1, 16);
if(p->name == NULL)
{
perror("calloc");
return;
}
strcpy(p->name, "德玛西亚");
printf("%s %d %d\n", p->name, p->def, p->atk);
//释放成员指向的空间
if(p->name != NULL)
{
free(p->name);
p->name = NULL;
}
//释放结构体的空间
if(p != NULL)
{
free(p);
p = NULL;
}
}
4、结构体指针数组空间在堆区 结构体数组元素在堆区 成员指针 指向了堆区
typedef struct
{
char *name;
int def;
int atk;
}HERO2;//HERO2是类型名
void test05()
{
int n = 0;
printf("请输入结构体指针数组的元素个数:");
scanf("%d", &n);
//p_arr保存堆区空间的结构体指针数组的首元素地址
HERO2 **p_arr = NULL;
p_arr = (HERO2 **)calloc(n,sizeof(HERO2 *));
if(NULL == p_arr)
{
perror("calloc");
return;
}
//给结构体指针数组的每个元素的指向 申请空间
int i = 0;
for ( i = 0; i < n; i++)
{
p_arr[i] = (HERO2 *)calloc(1,sizeof(HERO2));
if(NULL == p_arr[i])
{
perror("calloc");
return;
}
printf("请输入第%d个英雄的信息:", i+1);
char name[128]="";
int def;
int atk;
scanf("%s %d %d", name, &def, &atk);
p_arr[i]->def = def;
p_arr[i]->atk = atk;
//给结构体空间中的成员 申请指向
p_arr[i]->name = (char *)calloc(1, strlen(name)+1);
if(NULL == p_arr[i]->name)
{
perror("calloc");
return;
}
strcpy(p_arr[i]->name, name);
}
//遍历
for ( i = 0; i < n; i++)
{
printf("%s %d %d\n", \
p_arr[i]->name, p_arr[i]->def, p_arr[i]->atk);
}
//释放空间
for ( i = 0; i < n; i++)
{
//释放结构体空间中的name的指向
if(p_arr[i]->name != NULL)
{
free(p_arr[i]->name);
p_arr[i]->name = NULL;
}
//释放结构体的空间
if(p_arr[i] != NULL)
{
free(p_arr[i]);
p_arr[i] =NULL;
}
}
//释放整个结构体指针数组的空间
if(p_arr != NULL)
{
free(p_arr);
p_arr = NULL;
}
return;
}
int main(int argc, char const *argv[])
{
test05();
return 0;
}
知识点9【结构体嵌套结构体】(重要)
struct stu
{
int num;
char name[16];
int age;
};
struct info
{
//结构体变量 作为结构体的成员
struct stu student;
int score;
};
void test06()
{
struct info ob = {{100,"lucy", 18}, 99};
//如果结构体 嵌套 结构体 访问必须到最底层
printf("%d %s %d %d\n", \
ob.student.num, ob.student.name, ob.student.age, ob.score);
}
知识点10【结构体对齐问题】(重要)
1、知识点的引入
struct data1
{
char a;//1B
int b;//4B
};
void test01()
{
printf("%d\n",sizeof(struct data1));//8B 为啥?
}
2、对齐规则(默认对齐)
第一步:确定分配单位(每行开辟多少字节)
结构体中最大的基本类型的长度 为分配单位。
第二步:确定成员的偏移位置。
偏移位置:成员自身类型的整数倍(0~n倍)
第三步:收尾工作:
结构体的总大小必须是分配单位的整数倍
案例1:
struct data1
{
char a;//1B
int b;//4B
};
案例2:
struct data2
{
char a;
short b;
char c;
int d;
};
案例3:
struct data2
{
char a;
short b;
short c;
char d;
};
3、结构体嵌套结构体
第一步:确定分配单位(每行开辟多少字节)
所有结构体中最大的基本类型的长度 为分配单位。
第二步:确定成员的偏移位置。
普通成员偏移位置:成员自身类型的整数倍(0~n倍)
结构体成员的偏移量:该结构体的最大基本类型的整数倍
第三步:收尾工作:
结构体成员:是该结构体的最大基本类型整数倍。
结构体的总大小必须是分配单位的整数倍
struct A
{
char b;
short c;
};
struct B
{
int a;
struct A ob;//结构体成员的偏移量
int d;
};
案例1:
struct A
{
short b;
char c;
};
struct B
{
int f;
char a;
struct A ob;//结构体成员的偏移量
char d;
};
#include <stdio.h>
struct A
{
short b;
char c;
};
struct B
{
int f;
char a;
struct A ob;//结构体成员的偏移量
char d;
};
void test01()
{
printf("%d\n",sizeof(struct B));//8B 为啥?
struct B data = {10,'A',{20,'c'}, 'd'};
char *p = (char *)&data;
printf("%hd\n", *(short *)(p+6));//20
}
3、强制内存对齐
#pragma pack (value)时的指定对齐值value
第一步:确定分配单位(每行开辟多少字节)
min(value,最大的基本类型的长度) 为分配单位。
第二步:确定成员的偏移位置。
偏移位置:成员自身类型的整数倍(0~n倍)
第三步:收尾工作:
结构体的总大小必须是分配单位的整数倍
#include <stdio.h>
#include<stdio.h>
#pragma pack (4)
struct stu
{
char a;
short b;
short c;
};
void test01()
{
printf("%d\n",sizeof(struct stu));//6
}
struct stu1{
char a;
int b;
char c;
int d;
};
struct stu2{
char a;
char c;
int b;
int d;
};
void test03(){
printf("%lu\n",sizeof(struct stu1));//16
printf("%lu\n",sizeof(struct stu2));//12
}
int main(int argc, char const *argv[]){
test03();
return 0;
}
知识点11【拓展求结构体成员的偏移量】(了解)
struct stu1
{
char a;
int b;
char c;
int d;
};
#include <stdio.h>
#include<stdio.h>
struct stu1
{
char a;
int b;
char c;
int d;
};
#define OFF_SET(TYPE, member) (int)&(((TYPE *)0)->member)
void test01()
{
struct stu1 data;
printf("偏移量:%d\n", OFF_SET(struct stu1, b) );//8
}
知识点12【位段(位域)】(重要)
在结构体中,以位为单位的成员,咱们称之为位段(位域)
struct packed_data{
unsigned int a:2; //成员a只占unsigned int类型中的2位
unsigned int b:6;
unsigned int c:4;
unsigned int d:4; // a b c d相邻位域
unsigned int i; //非位域
} data;
相邻位域可以压缩,但是压缩的位数 不能超过 自身类型的大小。
struct stu1
{
unsigned char a:2;
unsigned char b:3;
unsigned char c:1;
unsigned char d:2;
};
void test01()
{
printf("大小:%d\n", sizeof(struct stu1) );//1
}
struct stu1
{
unsigned char a:2;
unsigned char b:3;
unsigned char c:1;
unsigned char d:3;
};
void test01()
{
printf("大小:%d\n", sizeof(struct stu1) );//2
}
如果给位域赋值 不能超过位域大小 如果超过 那么超过部分自动丢弃
struct stu1
{
unsigned char a:2;
unsigned char b:3;
unsigned char c:1;
unsigned char d:2;
};
void test01()
{
struct stu1 data;
data.a = 7;//0111只取11
printf("data.a = %d\n", data.a);//3
}
struct stu1{
unsigned char a:2;
unsigned char b:3;
unsigned char c:1;
unsigned char d:2;
};
void test04(){
struct stu1 data;
&data.a; //错误,无法取得位段‘a’的地址
}
int main(int argc, char const *argv[]){
test03();
return 0;
}
register int data;//还记得为啥不能&data吗?
如一个位段要从另一个存储单元开始。
unsigned char a:1;
unsigned char b:2;
unsigned char :0;
unsigned char c:3;(另一个单元)
struct stu4{
unsigned char a:2;
unsigned char b:3;
unsigned char :0; //另起一个存储单元
unsigned char c:1;
unsigned char d:2;
};
void test05(){
struct stu4 data;
printf("%d\n", sizeof(data)); //2
}
无意义位段
struct stu1{
unsigned char a: 2;
unsigned char : 2;
unsigned char b: 4;
}
应用场景:
知识点12【共用体】(了解)
共用体和结构体 区别:
结构体的成员拥有独立的空间
共用体成员共享同一份的空间, 空间的大小由最大的成员类型长度决定
union data
{
char a;
short b;
int c;
};
void test01(){
printf("%d\n", sizeof(union data));
union data ob;
ob.a = 10;
ob.b = 20;
ob.c = 30;
printf("%d\n", ob.a + ob.b + ob.c);
}
共用体的成员虽然共享同一块空间 但是成员操作空间的大小由自身大小决定。
知识点13【枚举】(了解)
枚举:将变量要赋的值 一一列举出来
enum poke_color{HONGTAO, MEIHUA, FANGKUAI, HEITAO};
enum poke_color p_color;//p_color只能赋HONGTAO, MEIHUA, FANGKUAI, HEITAO中的某一个值
p_color = 100;//无意义
enum poke_color{HONGTAO, MEIHUA, FANGKUAI, HEITAO};
void test02()
{
//p_color只能赋HONGTAO, MEIHUA, FANGKUAI, HEITAO中的某一个值
enum poke_color p_color;
p_color = MEIHUA;
printf("p_color = %d\n", p_color);//1
//默认情况下 枚举的符号常量 从0开始递增
printf("%d %d %d %d\n", HONGTAO, MEIHUA, FANGKUAI, HEITAO);//0 1 2 3
//可以在定义枚举符号常量时 修改枚举符号常量的值
enum poke_color2{HONGTAO, MEIHUA=5, FANGKUAI, HEITAO};
printf("%d %d %d %d\n", HONGTAO, MEIHUA, FANGKUAI, HEITAO);//0 1 2 3
}
标签:struct,int,精讲,lucy,char,printf,结构,name 来源: https://blog.csdn.net/qq_34981463/article/details/118443753