概述
目录
结构体诞生 构造新类型!
结构体构造类型 typedef
(1)typedef 使用
(2)typedef 与 define
结构体初始化&赋值
1、初始化
手动 scanf 赋值
结构体类型作参数和返回值
1、结构体变量 作参数和返回值
2、结构体指针作参数 ——提升效率
.结构体数组
结构体 sizeof 大小 以及内存对齐
(1)什么是内存对齐 (见下图)
(2)对齐规则
结构体使用注意事项
结构体诞生 构造新类型!
道家思想:“道生一,一生二,二生三,三生万物”
也就是 : 从单变量->数组->结构体
1:1 一个变量,一个类型 单一类型变量
N:1 N个变量,一个类型 数组
N:M N个变量,M个类型 结构体
为了更方便的描述事情,C语言对变量类型 开放权限,允许在基础类型的基础上进行自由构造。
typedef struct stu
{
int num;
char name[20];
char sex;
float score;
}Stu;
Stu s1; //定义 与 int a; 地位等价
结构体构造类型 typedef
(1)typedef 使用
1、先用原类型定义变量
2、定义之前加 typedef
3、将原变量的名字 换成你需要的类型名
注意:typedef 只是对现有类型 取别名,不能创造新类型。
(2)typedef 与 define
Define 构成的语句在预处理阶段——进行文本替换,不过脑子的那种替换
typedef 构成C语言的语句;参与编译
#define pointer char*
pointer m,n;
printf("nsizeof(m) = %dt sizeof(n) = %dn",sizeof(m),sizeof(n));
typedef pointer char*
pointer m,n;
printf("nsizeof(m) = %dt sizeof(n) = %dn",sizeof(m),sizeof(n));
结构体初始化&赋值
1、初始化
- 凡是基本类型,既可以在定义的时候初始化,也可以先定义再赋值。
int a = 100;
int a; a = 100;
- 凡是构造类型,可以在定义的时候初始化,也可以先定义再对每个成员进行赋值~ 绝不可以先定义再以初始化的方式赋值
//数组
int arr[10] = {1,2,3,4}; //定义的同时初始化
int arr[10];
arr[0] = 1; arr[2] = 2; //先定义 再对每个成员赋值
int arr[10];
arr[10] = {1,2,3,4}; //先定义再以初始化的形式赋值 不可以哦
//结构体也是如此
struct stu s = {"jiaomingxin",07,'f',100}; //定义+初始化
s.num = 10; //每个成员赋值
s.score = 100;
strcpy(s.name,"jiao"); //对于字符数组的赋值可以使用strcpy拷进去
手动 scanf 赋值
scanf("%s%d %c%f",s.name,&s.num,&s.sex,&s.score);
(1)和 %c 打交道的时候 容易出错
在输入num %d的数值之后,空格或者换行都会占据 %c 的位置!使 %c 成功被忽略。在连续输入时,%c前边加一个空格解决此类问题。
(2)取地址符 &
结构体定义时,name为数组类型,num、sex、score都是基本类型。 取地址时,数组不用 &符号,其他得用。
结构体类型作参数和返回值
分两种:一种为结构体变量整个作参数并返回;一种是以结构体指针形式作参并返回。
1、结构体变量 作参数和返回值
结构体作参数或返回值,会发生同类型复制(本质是赋值)。同类型结构体间,是可以相互赋值的。
BUT 当结构体内的变量数目较大的时候,会进行大量的内存拷贝。可能会导致栈的进程空间不足而崩溃。
#include <stdio.h>
typedef struct _Mycomplex
{
float real;
float imag;
}Mycomplex;
Mycomplex ADDcomplex (Mycomplex pa,Mycomplex pb)
{
Mycomplex t;
t.real = pa.real + pb.real;
t.imag = pa.imag + pb.imag;
return t;
}
int main()
{
//结构体变量 作参数
Mycomplex s1,s2,res;
s1.real = 1;
s1.imag = 2;
s2.real = 3;
s2.imag = 4;
res = ADDcomplex(s1,s2);
printf("%.2f+%.2f",res.real,res.imag);
return 0;
}
2、结构体指针作参数 ——提升效率
传结构体的成本是很高的,用指针作参数有一个好处,就是避免了同类型复制,无论结构体多大,只传 4 个字节的指针。
(注意:结构体变量的 点成员运算符 和 地址的 指向成员运算符 的使用)
#include <stdio.h>
typedef struct _Mycomplex
{
float real;
float imag;
}Mycomplex;
Mycomplex ADDcomplex (Mycomplex *pa,Mycomplex *pb)
{
Mycomplex t;
t.real = pa->real + pb->real;
t.imag = pa->imag + pb->imag;
return t;
}
int main()
{
//传地址
Mycomplex s1 = {1,2},s2 = {3,4},res;
//s1.real = 6;
//s1.imag = 2;
//s2.real = 3;
//s2.imag = 4;
//要么在结构体初始化时赋值,要么对结构体成员单独赋值。不存在 s1 = {1,2};
res = ADDcomplex(&s1,&s2);
printf("%.2f+%.2f",res.real,res.imag);
return 0;
}
//传地址与返回地址
/*
Mycomplex * ADDcomplex (Mycomplex *pa,Mycomplex *pb)
{
Mycomplex *t;
t->real = pa->real + pb->real;
t->imag = pa->imag + pb->imag;
return t;
}
int main()
{
//传地址
Mycomplex s1 = {1,2},s2 = {3,4};
Mycomplex * res;
res = ADDcomplex(&s1,&s2);
printf("%.2f+%.2f",res->real,res->imag);
return 0;
}
*/
.结构体数组
结构体数组的本质还是一维数组,只不过一维数组的每一个成员又是结构体。
#include <stdio.h>
typedef struct stu
{
int num;
char name[20];
char sex;
float score;
}Stu;
int main()
{
//结构体数组
Stu s[] ={{101,"jiaomingxin",'m',100},{102,"jackma",'m',99},{103,"mars",'f',20}};
int n = sizeof(s)/sizeof(*s);
for(int i=0; i<n; i++)
{
printf("s[%d]num --> %dn",i,s[i].num);
printf("s[%d]name --> %sn",i,s[i].name);
printf("s[%d]sex --> %cn",i,s[i].sex);
printf("s[%d]score --> %.1fn",i,s[i].score);
puts("");
}
return 0;
}
结构体 sizeof 大小 以及内存对齐
(1)什么是内存对齐 (见下图)
首成员在低地址,尾成员在高地址
(2)对齐规则
x86(linux 默认#pragma pack(4), window 默认#pragma pack(8))。linux 最大支持 4
字节对齐。
方法:
①取 pack(n)的值(n= 1 2 4 8--),取结构体中类型最大值 m。两者取小即为外对齐大
小 Y= (m<n?m:n)。
②将每一个结构体的成员大小与 Y 比较取小者为 X,作为内对齐大小.
③所谓按 X 对齐,即为地址(设起始地址为 0)能被 X 整除的地方开始存放数据。
④外部对齐原则是依据 Y 的值(Y 的最小整数倍),进行补空操作。
#include <stdio.h>
#include <string.h>
#pragma pack(8)
typedef struct _staff
{
char sex;
int age;
short num;
}Staff;
#if 0
x86(linux 默认#pragma pack(4), window 默认#pragma pack(8))。linux 最大支持 4
字节对齐。
具体操作步骤:
①取 pack(n)的值(n= 1 2 4 8--),取结构体中类型最大值 m。两者取小即为外对齐大
小 Y= (m<n?m:n)。
②将每一个结构体的成员大小与 Y 比较取小者为 X,作为内对齐大小.
③所谓按 X 对齐,即为地址(设起始地址为 0)能被 X 整除的地方开始存放数据。
④外部对齐原则是依据 Y 的值(Y 的最小整数倍),进行补空操作。
以此为例:
#pragma pack(8)
typedef struct _staff
{
char sex;
int age;
short num;
}Staff;
1、pack n = ? 结构体中类型最大的 m Y=min(m,n) 外对齐 n=8;m=4 --> Y=4
2、每个成员
char sex; 1 与Y相比取小值为X X = 1
int age; 4 4
short num; 2 2
存储位置
3、按X对齐 sex 0 - 1 (1字节) 往后找能被1整除的 那就1了
(设起始地址为 0) age 1 - 5 (4字节) 往后招能被4整除的 到了 8
num 8 - 10(2字节) OK存完了
4、外部对齐原则是依据 Y 的值(Y 的最小整数倍),进行补空操作。 Y=4 4的最小整倍数,10不是,找到12
分析完毕!
#endif
int main()
{
Staff s;
printf("sizeof(Staff) = %dn",sizeof (Staff));
printf("sizeof(s) = %dnn",sizeof (s));
printf("&(s) = %pn",&s);
printf("&(sex) = %pn",&s.sex);
printf("&(age) = %pn",&s.age);
printf("&(num) = %pn",&s.num);
return 0;
}
结构体使用注意事项
1、向未分配空间的结构体指针拷贝!
应用指针之前,一定要确保指针已有空间!否则没有指向合法的地址,考进去就是一堆乱码,并且后续也无法正常访问空间。
#include <stdio.h>
#include<string.h>
#include <stdlib.h>
typedef struct stu
{
char *name;
}Stu;
int main()
{
Stu s;
// strcpy(s.name,"mars");
// printf("s. name = %sn",s.name); //错误1 向未分配空间的指针赋值
s.name = (char *)malloc(20);
strcpy(s.name,"mars");
printf("s. name = %sn",s.name);
Stu *p;
p = (Stu *)malloc(sizeof(Stu *)); //为整个结构体指针分配空间
p->name = (char *)malloc(20); //还得为name指针分配空间
strcpy(p->name,"marsss");
printf("s. name = %s",p->name);
free(p->name); //先释放内层空间
free(p); //后释放外层的
return 0;
}
注意:
1)结构体中,包含指针,注意指针的赋值,切不可向未知区域拷贝
2)name 指针未初始化时,并没有指向一个合法的地址,这时候其内部存的只是一些乱码。所以在调用 strcpy 函数时,会将字符串 "mars" 往乱码所指的内存上拷贝,内存 name 指针根本就无权访问,导致出错
在所有涉及指针的时候,要么先定义的时候初始化,要么在使用之前进行malloc分配堆空间。
3)多层指针每个都要初始化。为指针变量 (Stu) p 分配了内存,但是同样没有给 name 指针分配内存。(p只是申请了name指针的指针类型的4个字节,与2)情况一样崩溃)错误与上面上一种情况一样,解决的办法也一样。这里用了一个 malloc 给人一种错觉,以为也给name 指针分配了内存。
4)记得:完了要释放结构体内指针所指向的空间,由内而外逐个释放。-->内存泄漏(先释放p,p.name就找不到了 也就无法释放,造成内存泄漏)
最后
以上就是无心小松鼠为你收集整理的C 结构体基础知识&(内存对齐详解)& 常见误用结构体诞生 构造新类型!结构体构造类型 typedef 结构体初始化&赋值结构体类型作参数和返回值.结构体数组结构体 sizeof 大小 以及内存对齐结构体使用注意事项的全部内容,希望文章能够帮你解决C 结构体基础知识&(内存对齐详解)& 常见误用结构体诞生 构造新类型!结构体构造类型 typedef 结构体初始化&赋值结构体类型作参数和返回值.结构体数组结构体 sizeof 大小 以及内存对齐结构体使用注意事项所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复