辽宁食品 中企动力网站建设,住房和城乡建设行业证书,深圳建设交易中心网站首页,郑州建立一个网站需要哪些自定义类型#xff1a;结构体 一#xff1a;引入二#xff1a;结构体类型的声明1#xff1a;正常声明2#xff1a;特殊声明 三#xff1a;结构体变量的创建和初始化1:结构体变量的创建2#xff1a;结构体变量的初始化 三#xff1a;结构体访问操作符四#xff1a;结构… 自定义类型结构体 一引入二结构体类型的声明1正常声明2特殊声明 三结构体变量的创建和初始化1:结构体变量的创建2结构体变量的初始化 三结构体访问操作符四结构体内存对齐1对齐规则2为什么存在内存对齐3修改默认对齐数 五结构体传参六结构体实现位段1位段的声明2位段的内存分配3位段的跨平台问题4位段的应用5位段的使用注意事项 一引入
C语言中有内置类型如int charfloatlonglong long等这些内置类型可以直接使用。 如果我们想要描述一个学生比如学生的名字年龄分数等只有一个内置类型是无法描述清楚的所以引入结构体的使用。
二结构体类型的声明
1正常声明
结构体是自定义类型 结构体是一些值的集合这些值称为成员变量结构体的每个成员可以是不同类型的变量。 例如描述一个同学
struct Stu//
{char name[20];//名字int age;//年龄float score;//分数};{}里面的内容就是结构体的成员。
2特殊声明
在声明结构体的时候可以不完全声明。
struct
{int a;char b;float c;
}s { 0 };//这种特殊的声明只能此时初始化
struct
{int a;char b;float c;
}*ps;
int main()
{ps s;//error虽然结构类型的成员一模一样但编译器依然认为两边是不同的指针类型return 0;
}三结构体变量的创建和初始化
1:结构体变量的创建
#include stdio.h
struct stu
{char name[20];int age;float score;
}s4, s5;//结构体变量的创建s4,s5
int main()
{struct stu s1, s2, s3; //结构体变量的创建s1,s2,s3return 0;
}2结构体变量的初始化
#include stdio.h
struct stu
{char name[20];int age;float score;
}s2 { lisi,21,89.5f };//结构体变量的创建及初始化
int main()
{struct stu s1 { zhangsan,20,85.5f };//结构体变量的创建及初始化struct stu s2{.age22,.namewanger,.score98.5f};//特殊初始化return 0;}struct Stu//struct是结构体类型的关键字//struct Stu是用户定义的结构体类型
{//name,age,score都是都是结构体成员名char name[20];//名字int age;//年龄int score;//分数}s;//声明的同时定义结构体变量s
//结构体类型不占用空间相当于盖房子时所使用的图纸
struct Stu s { zhangsan,20,84 };//结构体变量的初始化
//结构体变量占用空间相当于盖好了的房子三结构体访问操作符
结构体成员访问操作符有两个一个是 . 一个是- 形式如下 结构体变量.成员变量名 结构体指针-成员变量名
#include stdio.h
struct stu
{char name[20];int age;int score;
};
int main()
{struct stu s { zhangsan,20,90 };struct stu* p s;printf(%d %d\n, s.age,s.score);printf(%d %d\n, p-age, p-score);return 0;
}四结构体内存对齐
结构体的大小与结构体内存对齐有关。
1对齐规则
1结构体的第一个成员对齐到相对结构体变量起始位置偏移量为0的地址处。 2其他成员变量要对齐到对齐数的整数倍的地址处。 对齐数编译器默认的对齐数VS默认的是8与该成员变量大小的较小值. 3结构体总大小为最大对齐数结构体中的每个成员都有一个对齐数所有对齐数中最大的的整数倍。 4如果嵌套了结构体的情况嵌套的结构体成员对齐到自己的成员中最大对齐数的整数倍处结构体的整体大小就是所有最大对齐数含嵌套结构体成员的对齐数的整数倍。 例1
#include stdio.h
struct S1
{char c1;char c2;int a;
};struct S2
{char c1;int a;char c2;
};int main()
{struct S2 s2 { a, 100, b};printf(%zd\n, sizeof(s2));struct S1 s1 { a, b, 100 };printf(%zd\n, sizeof(s1));return 0;
} 例2结构体的嵌套
#include stdio.h
struct S3
{double d;char c;int i;
};struct S4
{char c1;struct S3 s3;double d;
};int main()
{printf(%zd\n, sizeof(struct S3));//?printf(%zd\n, sizeof(struct S4));//?return 0;
} 2为什么存在内存对齐
1平台原因不是所有的硬件平台都能访问任意地址上的任意数据的某些硬件平台只能在某些地址处取某些特定类型的数据否则抛出硬件异常。 2性能原因数据结构尤其是栈应该尽可能地在自然边界上对齐。原因在于为了访问未对齐的内存处理器需要两次内存访问而对齐的内存访问仅需要一次访问。假设一个处理器总是从内存中取8个字节则地址必须是8的倍数。如果我们能保证所有的double类型的数据的地址都对齐成8的倍数那么就可以用一个内存操作数 来读或者写值了。否则我们可能执行两次内存访问因为对象可能被分放在两个8字节内存块中。 总体来说结构体的内存对齐是拿空间来换取时间的做法。 在设计结构体的时候我们既要满足对齐又要节省空间可以让内存占用少的成员尽量放在一起。
#include stdio.h
struct S1
{char c1;char c2;int a;
};struct S2
{char c1;int a;char c2;
};int main()
{struct S2 s2 { a, 100, b};printf(%zd\n, sizeof(s2));//12struct S1 s1 { a, b, 100 };printf(%zd\n, sizeof(s1));//8两个char类型的数据放在了一起return 0;
}3修改默认对齐数
#pragma这个预处理命令可以改变编译器的默认对齐数。 #pragma pack()//取消设置的默认对齐数还原为系统默认的对齐数
#include stdio.h
#pragma pack(1)//将默认对齐数设置为1
struct s
{char c1;int i;char c2;
};int main()
{printf(%d , sizeof(struct s));return 0;
}五结构体传参
#include stdio.h
struct S
{int age;int num;
};
struct S s { 20,100 };
void print1(struct S t)
{printf(%d\n, t.num);
}
void print2(struct S* ps)
{printf(%d\n, ps-num);
}
int main()
{print1(s);//传递的是结构体变量也就是传值调用print2(s);//传递的是结构体变量的地址也就是传址调用return 0;
}传递结构体变量的时候形参是实参的一份临时拷贝形参会单独占用空间传数据 也需要时间如果实参数据较大那么浪费的空间和时间将非常大。 函数传参的时候参数需要压栈会有时间和空间上的系统开销如果传递一个结构体对象的时候结构体过大参数压栈的系统开销比较大会导致性能的下降。
六结构体实现位段
位段是基于结构体的结构体可能浪费空间而位段可以节省空间。位段的位指的是二进制位。
1位段的声明
#include stdio.h
struct A
{int a : 2;//a占2个bit位int b : 4;//b占4个bit位int c : 7;//c占7个bit位
};
int main()
{printf(%d , sizeof(struct A));return 0;
}2位段的内存分配
1位段的成员可以是int unsigned intsigned intchar类型。 2位段的空间上是按照需要4个字节int或一个字节char来开辟的。 3位段涉及很多不确定的因素位段是不可以跨平台的如果想要跨平台需要使用不同的代码。当开辟了内存后内存中的每个bit从左向右使用还是从右向左使用不确定当前面的内存使用后剩下的空间不能够放下一个成员时剩下的空间是否使用也不确定。 下面主要讲述在VS编译器下位段的内存分配。 在VS编译器下每个bit位从右向左使用使用后剩余的空间不能够放下下一个元素时该剩余空间将不再使用需要再开辟一个字节的空间放下一个元素。
#include stdio.h
struct S
{char a : 3;//开辟第一个字节,a占3个bit位还剩5个比特位char b : 4;//剩下的5个bit位能够放下b,还剩下1个bit位char c : 5;//剩下的1个比特位 不能够放下c,再开辟一个新的字节放c,//放c后还剩下3个字节剩下的3个字节放不下dchar d : 4;//再开辟一个新的字节放d
};int main()
{struct S s { 0 };printf(%d , sizeof(s));//3return 0;
}3位段的跨平台问题
1int位段被当成有符号数还是无符号数是不确定的。 2位段中最大的数目不能确定16位机器下最大是16,32位机器下是32写成33在16位和32位的机器下都会报错 3:位段中的成员在内存中是从左向右分配还是从右向左分配不确定。 4当一个结构包含2个位段第二个位段的成员比较大第一个位段剩下的空间无法容纳第二个位段时是舍弃剩余的位还是使用这是不确定的。 总之位段可以节省空间但有跨平台问题想要解决跨平台问题需要写不同的代码
4位段的应用
在网络协议中IP数据报的格式其中很多的属性只需几个bit位就能够描述在这里使用位段可以节省很多空间网络传输的数据报大小也会较小一些对网络的畅通是有帮助的。
5位段的使用注意事项
内存中一个字节有一个编号地址位段可能几个成员共用一个字节这样有些成员的起始位置并不是某个字节的起始位置那么这些位置是没有地址的。所以不能对位段的成员使用操作符这样就不能使用scanf直接给位段的成员输入值只能先输入到一个变量里然后赋值给位段的成员。
#include stdio.h
struct S
{char a : 3;char b : 5;int c : 7;
};
int main()
{struct S s { 0 };int c 0;//scanf(%d, s.c);scanf(%d, c);s.c c;printf(c%d , s.c);return 0;
}