首页 > 其他分享 >C语言——自定义类型(结构体+枚举+联合)

C语言——自定义类型(结构体+枚举+联合)

时间:2022-10-23 22:01:28浏览次数:54  
标签:struct 自定义 int C语言 char 枚举 printf 对齐 结构

结构体

基础知识

结构是一些值的集合,这些值被称为成员变量;

结构体可以存储不同类型的数据项,而数组中是存储相同类型数据项

声明

struct tag {//struct是关键字,tag是结构体标签名 
member-list//成员变量的列
member-list
...
} variable-list ;
//variable-list 结构 变量 ,定义在结构的末尾,最后一个分号之前,可以指定一个或多个结构变量
//结构末尾是要带分号的


#include<stdio.h>
struct book{
int price;
char name[12];
char id[20];
}b4,b5,b6;//b4,b5,b6是全局变量
int main()
{
struct book b1;
struct book b2;
struct book b3;
return 0;
}

特殊的声明

匿名结构体类型

  • 声明时省略了结构体标签
  • 这种匿名结构体类型只能用上一次
struct
{
int a;
char b;
float c;
double d;
} s;

struct
{
int a;
char b;
float c;
double d;
} *ps;

int main()
{
ps = &s; //错误
return 0;
}

即使成员一样,但编译器照样会把上面两个声明看作是不同的类型,下面的结构体指针就不能存上面的地址

结构体的自引用

  • 结构体中不是包含同类型的结构体变量,而是包含同类型结构体的指针
  • 一般情况下,tag、member-list、variable-list 这3部分至少要出现2部分
  • 结构体的成员可以包含其他结构体,也可以包含指向自己结构体类型的指针;但不能自己包含自己,也不能包含同类型的结构体变量
错误:
struct Node{
int date;
struct Node next;
};

正确:
struct Node{
int date;
struct Node* next;
};
struct N
{
int price;
struct N n;
};

注意:结构体自引用,通常这种指针的应用是为了实现一些更高级的数据结构如链表和树等。

结构体变量的定义(创建)和初始化

//定义(创建)

struct point
{
int x;
int price;
}s1 ; // 声明类型的 同时 创建 变量s1

struct point p1;//创建结构体变量p1

int main()
{
struct point s2;//创建局部变量s2
return 0;
}
//初始化:定义变量的同时赋值

//1
struct point
{
int x;
char c;
}s1 ;
int main()
{
struct point s2 = {32,‘d’};//初始化
return 0;
}

//2
#include<stdio.h>
struct S
{
int x;
char c;
}s1 ;

struct point
{
int x;
struct S s;
char c;
}s1 ;
int main()
{
struct point sa = { 20, {30,'e'} , 'x' };
printf("%d %d %c %c\n",sa.x ,sa.s.x ,sa.s.c ,sa.c);
return 0;
}

注意:访问结构体成员时 用  .  和  ->  

  . 是针对结构体变量的

-> 是针对结构体指针的


结构体内存对齐

结构体对齐规则:

  1. 第一个成员放在结构体变量在内存中存储位置的0偏移处开始
  2. 其他成员都要对齐放到各自对齐数(成员的大小 和 默认对齐数的 较小值)整数倍的地址处。(VS中默认对齐数为 8)
  3. 结构体的总大小是结构体的所有成员的对齐数中最大的那个对齐数的整数倍。(当不满为整数倍时直接提升到整数倍,这可能会浪费一些空间)
  4. 嵌套的结构体先对齐到 自己的最大对齐数 的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

注意:VS中默认对其数为8,Linux中没有默认对齐数的概念

例子1:计算结构体的大小

#include<stdio.h>
struct S{
char c1;
int i;
double d;
};
int main()
{
struct S s1 = {0};
printf("%d\n",sizeof(s1));
return 0;
}

C语言——自定义类型(结构体+枚举+联合)_位段

成员对齐数依次为1,4,8;    最大对齐数为8

C语言——自定义类型(结构体+枚举+联合)_#include_02

例子2:

#include<stdio.h>
struct S{
char c1;
char c2;
int b;
};
int main()
{
struct S s1 = {0};
printf("%d\n",sizeof(s1));
return 0;
}
//占8,且为4的倍数
//结果:8

C语言——自定义类型(结构体+枚举+联合)_位段_03

例3:

struct S{
double d;
char c;
int i;
};
//16

C语言——自定义类型(结构体+枚举+联合)_嵌套_04

当结构体进行嵌套结构体时:

  • 嵌套的结构体先对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
#include<stdio.h>
struct B{
char c1;
int b;
};
struct S{
char c1;
struct B b1;
int b;
};
int main()
{
struct B b1 = {0};
struct S s1 = {0};
printf("%d\n",sizeof(s1));
return 0;
}
//32
//嵌套的结构体先对齐到8,整体大小是16,也是8的整倍数

C语言——自定义类型(结构体+枚举+联合)_嵌套_05

修改默认对齐数

  • 可以用  #pragma pack()  进行默认对齐数的修改  
#include<stdio.h>
#pragma pack(2)//默认对齐数改为2
struct S{
char c1;
int b;
char c2;
};
#pragma pack() //2在中间起效,后面表示默认对齐数修改结束,改回8了

int main()
{
struct S s1 = {0};
printf("%d\n",sizeof(s1));
return 0;
}
//结果:8

结论:当你觉得对齐方式不合适的时候,可以自己更改

结构体传参


#include <stdio.h>
struct S
{
int data[1000];
int num;
};

struct S s = {{1, 2, 3, 4}, 1000};
//结构体传参
void print1(struct S s)
{
printf("%d\n", s.num);//用 .
}

//结构体地址传参
void print2(struct S* ps)
{
printf("%d\n", ps->num);//用 ->
}
int main()
{
print1(s); //传结构体
print2(&s); //传地址
return 0;
}

在print1和print2中 首选print2函数,因为:

函数传参的时候,参数是需要压栈的,会产生时间和空间上的系统开销。如果传递一个结构体对象时,结构体过大,参数压栈的系统开销就会很大,从而导致性能的下降。

结论:结构体传参的时候,传结构体的地址

位段

位段的声明和结构体是类似的,但有两个不同

  • 位段的成员必须是char, int , unsigned int 或 signed int
  • 位段的成员名后面有 一个冒号和一个数
struct A{
int a:1;//a 成员占1个bit位
int _b:2;//_b 成员占2个bit位
int c:7;//c 成员占7个bit位
};

位段的内存分配

  • 位段的空间上是按照需要 以4个字节 或者1个字节(char)的方式来开辟的
#include<stdio.h>
struct A{
//int型是4个字节大小,即32bit
int a:1;//a 成员占1个bit位
int _b:28;//_b 成员占28个bit位
//前面加起来为29,未达32,但后一个加上后大于了32;所以得重新再开启一个int型空间
//4个字节----32bit为
int c:7;//c 成员占7个bit位
};
int main()
{
printf("%d\n",sizeof(A));
return 0;
}
//结果:8

总结:与结构体相比,位段在可以达到同样的效果上又可以更好地节省空间,但存在跨平台上的问题。


枚举

枚举就是进行有限次地一一列举,将可能的取值一一列举

定义格式:

enum 枚举名 
{
枚举元素1,
枚举元素2,//这里是用逗号
……
};
//{}中的是枚举常量

这些可能取值都是有值的,未赋值时默认从0开始,依次递增1

举例:

#include<stdio.h>
enum Day //星期
{
Mon,
Tues,
Wed,
Thur,
Fri,
Sat,
Sun
};
int main()
{
printf("%d\n",Wed);
return 0;
}
//结果:2

枚举优点

  1. 增加代码的可读性和可维护性
  2. 与 #define 定义的标识符相比,枚举有类型检查,更加严谨
  1. 有效防止命名污染(封装)
  2. 便于调试
  3. 使用方便,一次可以定义多个常量


联合(共用)体

联合类型的定义

这种类型定义的变量也包含一系列的成员,特点是这些成员共用同一块空间

格式:

union [union tag]
{
member definition;
member definition;
...
member definition;
} [one or more union variables];

联合体特点

  • 联合体的成员是共用同一块内存空间的。
  • 一个联合体变量的大小 至少 最大成员的大小
#include <stdio.h>
union Un
{
char c;//1
int i;//4
};
int main()
{
union Un u;
printf("%p\n", &u);
printf("%p\n", &(u.c));
printf("%p\n", &(u.i));

printf("%d\n", sizeof(u));//计算联合体变量的大小

return 0;
}
//3个地址是相同的
//4

联合体初始化

  • 在同一时间,只能使用一个成员:因为共用一块空间,改动一个也会将其他值损坏
#include <stdio.h>
union Un
{
char c;//1
int i;//4
};
int main()
{
union Un u;
//union Un u = {10};//这样会给所有成员初始化相同的值10

u.i = 1000;
u.c = 100;
printf("%d\n",u.i);
printf("%d\n",u.c);
return 0;
}

C语言——自定义类型(结构体+枚举+联合)_位段_06

这次我们在同一时间只使用一个变量

C语言——自定义类型(结构体+枚举+联合)_#include_07

联合体大小的计算

  • 联合体的大小 至少是 最大成员的大小
  • 当最大成员的大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍
//1
#include<stdio.h>
union Un
{
char c[5];
char c1;
};
int main()
{
union Un u;
printf("%d\n",sizeof(u));
return 0;
}
//结果:5


//2
#include<stdio.h>
union Un
{
short a[3];//2 6
int b;//4 4
};
int main()
{
union Un u;
printf("%d\n",sizeof(u));
return 0;
}
//结果:8




标签:struct,自定义,int,C语言,char,枚举,printf,对齐,结构
From: https://blog.51cto.com/u_15722538/5787619

相关文章

  • C语言问题定位总结
    二分搜索法gitbisect命令,可以定位问题引入的第一个commit,如下图:用法举例:1、下载模拟代码:[email protected]:bradleyboy/bisectercise.git$cdbisecterci......
  • 嵌入式-C语言基础:指针
    指针就是地址,变量的值可以通过两种方式访问,一个是通过变量名,一个是通过地址访问。从而引出一个问题,即什么是指针变量?整型(字符)变量就是存放整形(字符)的变量,指针变量就是存放......
  • C语言_2
    video3~指的是按位取反。此时“位”值的还是二进制位;++k和k++的区别,前置++是先++再使用,后置++是先使用在++;强制类型转换:在想要转换类型的前面加上括号然后给与相应的类型名......
  • C语言笔记基础知识
    ......
  • C语言的练习题
    有1,2,3,4四个数字,那能组成多少个互不相同且无重复数字的三位数?都是多少?分析:三位数可表示为:个位:g,十位:s,百位:b.可以有多少组合:用for语句的嵌套#include<stdio.h>intmain(......
  • C语言学习--指针--指针和内存单元
    指针和内存单元指针:地址内存单元: 计算机中内存最小的存储单元。    指针的定义和使用inta=10;int*p=&a;*表示指针变量*p=250;指针的解引用,间......
  • 实验2 C语言控制语句应用编程
    1#include<stdio.h>2#include<stdlib.h>3#include<time.h>45intmain()6{7srand(time(0));89intdate=rand()......
  • 自定义transform的重塑
    #cartoontransformdefct(names,*args):'''names:alistofmobjectsargsr......
  • 解决vs code C语言编译路径丢失问题的一些思考
    -问题说明: 本人算是刚接触c语言的萌新,配置vscode编译环境时费了不少波折。今天我删除掉了一个包含mingw的文件(这个文件是本人下载过的两个编译器其中的一个,因为无法确认......
  • 实验2 C语言控制语句应用编程
     任务一:#include<stdio.h>#include<stdlib.h>#include<time.h>#defineN5intmain(){intnumber;inti;srand(time(0));for(i=0;i<N;++i){nu......