编程技术是改变世界的力量。
本站
当前位置:网站首页 > 后端语言 > 正文

C语言之结构体基础(c语言结构体怎么用)

gowuye 2024-04-25 04:40 9 浏览 0 评论

什么是结构体

在C语言中,结构体是不同数据类型的元素的集合。该结构用于创建用户定义的数据类型。该结构也被称为“ C语言自定义类型”。换句话说,结构体是不同类型数据的集合。这种数据类型的名字是由用户自主定义的。通常结构体用于将不同数据类型的元素组合成一个组。结构体中定义的元素称为结构成员。在前面我们学习过基础的数据类型int float char 等,都只能用来表示基础的数据类型,那么要怎么来表示复杂的数据类型呢?比如下信息:

定义5个数组,然后通过数组下标一致性原则去描述上述表格数据是否可行? 当然没得问题,如下代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() 
{
  int ids[10] = { 0 };
  char names[10][10] = { 0 };
  char sexs[10][3] = { 0 };
  int ages[10] = { 0 };
  int scores[10] = { 0 };

  ids[0] = 100;
  strcpy(names[0], "欧阳疯");
  strcpy(sexs[0], "男");
  ages[0] = 18;
  scores[0] = 666;

  return 0;
}

看起来还不错,实际上很繁琐,在排序需要交换两者数据的时候极其繁琐,既然学生信息有很多,那么能不能定义一个学生类型呢?如果能,直接通过学生访问该学生的所有信息就很方便了!

C语言结构体创建

为了定义结构体,您必须使用 struct 语句。struct 语句定义了一个包含多个成员的新的数据类型,struct 语句的格式如下:

  • 结构体名自己起,struct 结构体名组成新的数据类型,C语言中struct不可缺少
  • 多个成员之间用分号分隔,C语言中不允许无数据成员的结构体定义
  • 末尾的分号不可缺少

那么对于上面的学生的信息,就可以用如下结构体表示学生结构体类型:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Student 
{
    int id;    //学号
    char name[10];  //姓名
    char sex;    //性别
    int age;    //年龄
    int score;    //总分
};
int main() 
{

  return 0;
}

结构体中所有数据成员组成一个整体,形成一个新的数据类型,而不同变量则是零散内存,毫无关联,如下图:

C语言结构体访问

结构体中的数据必须要通过结构体变量访问,访问方式有以下两种:

  • 普通结构体变量: 变量.成员
  • 结构体指针:指针->成员 或者 (*指针).成员

结构体变量的创建

结构体类型已经声明,如何使用结构体类型定义结构体变量呢?有以下方法(typedef别名创建后续再讲):

  • 先声明结构体类型再定义结构体变量
  • 在声明结构体类型的同时定义变量

如下测试代码:

#include <stdio.h>
#include <stdlib.h>
struct MM
{
  char name[20];
  int age;
  double score;
}mm;       
//在声明结构体类型的同时定义变量mm
int main() 
{
  //先声明结构体类型再定义结构体变量
  //struct MM: 类型
  //girl: 变量名
  struct MM girl;
  return 0;
}

结构体变量的初始化

在定义结构体变量的同时通过{}的方式为每一个成员变量进行赋初值,赋初值主要有以下几种方式:

  • 全部初始化
  • 部分初始化:未初始化部分自动初始化为0
  • 全部初始化为0
  • 初始化指定的成员(可以初始化任意成员,不必遵循定义顺序)
  • 用另一个结构体变量初始化

如下测试代码:

#include <stdio.h>
#include <stdlib.h>
struct MM
{
  char name[20];
  int age;
  double score;
};       
int main() 
{
  
  //全部初始化,顺序必须一致
  struct MM girl = {"girl",18,99.1};  
  //部分初始化:未初始化部分自动初始化为0
  struct MM mm = { "gril" };
  //全部初始化为0
  struct MM zero = { 0 };
  //初始化指定的成员(可以初始化任意成员,不必遵循定义顺序)
  struct MM beauty = { .age = 18,.name = "beauty" };
  //用另一个结构体变量初始化
  struct MM woman = girl;
  return 0;
}

结构体数组

一个结构体变量可以存放一个学生的一组信息,可是如果有 10 个学生呢?难道要定义 10 个结构体变量吗?难道上面的程序要复制和粘贴 10 次吗?很明显不可能,这时就要使用数组。结构体中也有数组,称为结构体数组。它与前面讲的数值型数组几乎是一模一样的,只不过需要注意的是,结构体数组的每一个元素都是一个结构体类型的变量,都包含结构体中所有的成员项。

struct Student stus[10]; 这就定义了一个结构体数组,共有 10 个元素,每个元素都是一个结构体变量,都包含所有的结构体成员。

示例程序| 从键盘输入 5 个学生的基本信息,如学号、姓名、年龄、性别,将年龄最大的学生的基本信息输出到屏幕

#include <stdio.h>
struct Student
{
    int id;
    char name[10];
    int age;
    char sex;
};
int main()
{
    struct Student stus[10];
    printf("input stu>:\n");
    for (int i = 0; i < 5; i++)
    {    
        scanf("%d %s %d %c", &stus[i].id, stus[i].name, &stus[i].age, &stus[i].sex);
    }
    struct Student maxStu = stus[0];
    for (int i = 0; i < 5; i++)
    {
        if (maxStu.age < stus[i].age)
        {
            maxStu = stus[i];
        }
    }
    printf("-------------Max---------------\n");
    printf("%d %s %d %c\n", maxStu.id, maxStu.name, maxStu.age, maxStu.sex);
    return 0;
}

程序测试结果如下:

当然对于这种表格数据操作有很多,例如排序,查找,文件保存等。详细参见结构体数组写管理系统。

结构体指针

当一个指针变量指向结构体时,我们就称它为结构体指针。C语言结构体指针的定义形式一般为:struct 结构体名 *变量名;

如下测试代码:

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
struct MM
{
  char name[20];
  int age;
  double score;
};       
int main() 
{
  struct MM girl = {"girl",18,99.1};  
  struct MM* pMM = NULL;
  //结构体指针指向结构体变量
  pMM = &girl;
  //指针用->访问
  printf("%s\t%d\t%.1lf\n", pMM->name, pMM->age, pMM->score);
  //*pMM等效girl
  printf("%s\t%d\t%.1lf\n", (*pMM).name, (*pMM).age, (*pMM).score);
  //结构体指针动态内存申请
  struct MM* pArray = (struct MM*)malloc(sizeof(struct MM)*3);
  assert(pArray);
  for (int i = 0; i < 3; i++) 
  {
    pArray[i] =(struct MM){ "张三",18,99.9 };
    printf("%s\t%d\t%.1lf\n", pArray[i].name, pArray[i].age, pArray[i].score);
  }
  return 0;
}

程序测试结果如下:

基本上普通指针能做的,结构体指针一样的。只是在访问数据的时候需要剥洋葱(通过->访问每个数据)。当然也可以当做函数参数和返回值。

C语言位段

C语言允许在一个结构体中以位为单位来指定其成员所占内存长度,这种以位为单位的成员称为位段。利用位段能够用较少的位数存储数据。基本语法如下:

struct 结构体名
{
      类型 位段名1 : 位段大小;
      类型 位段名2 : 位段大小;
      类型 位段名3 : 位段大小;
      类型 位段名4 : 位段大小;
      ...
};

C语言标准规定,只有有限的几种数据类型可以用于位段。

  • 所有整数类型
  • char类型
  • bool类型

如下测试代码:

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
struct BitField
{
    unsigned char a : 1;
    unsigned char b : 4;
    unsigned char c : 3;
};
int main()
{
    //初始化
    struct BitField bit = { 1,2,3 };
    //输出
    printf("first:%d %d %d\n", bit.a, bit.b, bit.c);
    //赋值
    //10    1位有效去掉最高位
    bit.a = 2;  
    //10100 4位有效去掉最高位
    bit.b = 20;  
    //1000  3位有效去掉最高位
    bit.c = 8;   
    //再次输出
    printf("last:%d %d %d\n", bit.a, bit.b, bit.c);
}

程序测试结果如下:

位段注意项:

  • 位段的内存分配:位段占的二进制位数不能超过该基本类型所能表示的最大位数,即位段不能跨字节存储,比如char是占1个字节,那么最多只能是8位;
  • 位域的存储:C语言标准并没有规定位域的具体存储方式,不同的编译器有不同的实现,但它们都尽量压缩存储空间。
  • 禁止对位段取地址:地址是字节(Byte)的编号,而不是位(Bit)的编号。
  • 无名位段:位域成员可以没有名称,只给出数据类型和位宽,无名位域一般用来作填充或者调整成员位置。因为没有名称,无名位域不能使用。

C语言结构体嵌套

在一个结构体内包含另一个结构体作为其成员,当然一般嵌套可以理解为一类数据的封装。访问的话逐步剥洋葱即可,定义方式有两种写法。

  • 以结构体变量当做数据成员方式嵌套
  • 直接把结构体定义在另一个结构体内

示例程序以结构体变量当做数据成员方式嵌套 | 给学生增加一个出生日期,包含年月日

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
struct Date
{
    short year;
    short month;
    short day;
};
struct Student
{
    int id;
    char name[10];
    struct Date birth;  //出生日期
};
int main()
{
    //数据完整的情况,{}可有可无
    struct Student baby = { 1001,"baby",{2008,3,17} };
    printf("%d\t%s\t%d-%d-%d\n", baby.id, baby.name, 
        baby.birth.year, baby.birth.month, baby.birth.day);
    return 0;
}

示例程序直接把结构体定义在另一个结构体内| 给学生增加一个出生日期,包含年月日

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
struct Student
{
    int id;
    char name[10];
    struct Date
    {
        short year;
        short month;
        short day;
    }birth;//出生日期
};
int main()
{
    //数据完整的情况,{}可有可无
    struct Student baby = { 1001,"baby",{2008,3,17} };
    printf("%d\t%s\t%d-%d-%d\n", baby.id, baby.name, 
        baby.birth.year, baby.birth.month, baby.birth.day);
    return 0;
}

C语言结构体内存对齐

什么是内存对齐

从理论上讲,对于任何变量的访问都可以从任何地址开始访问,但是事实上不是如此,实际上访问特定类型的变量只能在特定的地址访问,这就需要各个变量在空间上按一定的规则排列,而不是简单地顺序排列,这就是内存对齐。通俗点讲就是厕所建坑位需要合理排布对齐,不然可能会存在只有半个坑的情况。

为什么要内存对齐

  • 某些平台只能在特定的地址处访问特定类型的数据;
  • 提高存取数据的速度。比如有的平台每次都是从偶地址处读取数据,对于一个int型的变量,若从偶地址单元处存放,则只需一个读取周期即可读取该变量;但是若从奇地址单元处存放,则需要2个读取周期读取该变量。

当然我们会不会算内存对齐其实并不重要,因为对于一个结构体占用内存一个sizeof即可搞定,重要的是大家要知道如何设计代码可以让内存更小,毕竟在特殊开发场景,内存占用是非常值得关注的。例如,网络传输,嵌入式等。

内存对齐规则

C语言标准并没有规定内存对齐的细节,而是交给具体的编译器去实现,但是对齐的基本原则是一样的。

  • 结构体变量的首地址能够被其最宽基本类型成员的大小所整除;
  • 结构体每个成员相对于结构体首地址的偏移量都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字节;
  • 结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要编译器会在最末一个成员之后加上填充字节。

如下测试代码:

#include <stdio.h>
#include <stdlib.h>
//会不会算出结果不重要,重要是学会怎么写可以少内存即可
//按照从小到大即可 字符和整形写一块
struct Data1
{
  double score;    //8
  char name[3];       //补一位和int组成8位
  int age;
};
struct Data2
{
  char name[3];     //补 5位
  double score;     //8位
  int age;          //补4位
};

struct Data3
{
  char name[9];   //8+1  +3
  int num;        //4
  double score;   //8
  int age;        //4+3 5
  char tel[3];
};
struct Data4
{
  int age;
  char name[3];
  int num;
};
struct Data5
{
  char name[5];
  char num;
};
struct Data6
{
  char name[5];
  int* p;         //32位按照4个字节对齐,64位按照8位对齐
};
struct Data7
{
  struct Data4 data;   //12
  //char name[3];     //3+1
  double score;        //8
  int age;             //8
};
union Data8
{
  char name[20];
  double score;
};
int main()
{
  struct Data1* p = (struct Data1*)malloc(sizeof(struct Data1));
  printf("%zd\n", sizeof(struct Data1));   //16
  printf("%zd\n", sizeof(struct Data2));   //24
  //8 1w  8w 
  printf("%zd\n", sizeof(struct Data3));   //32
  printf("%zd\n", sizeof(struct Data4));   //12
  printf("%zd\n", sizeof(struct Data5));   //6
  printf("%zd\n", sizeof(struct Data6));   //16
  //C语言不允许空的结构体
  printf("%zd\n", sizeof(struct Data7));   //32
  printf("%zd\n", sizeof(union Data8));   //24
  return 0;
}

相关

如果阁下正好在学习C/C++,看文章比较无聊,不妨关注下关注下小编的视频教程,通俗易懂,深入浅出,一个视频只讲一个知识点。视频不深奥,不需要钻研,在公交、在地铁、在厕所都可以观看,随时随地涨姿势。

相关推荐

Nginx 响应提速10倍,你需要知道的缓存性能优化——FastCGI调优
Nginx 响应提速10倍,你需要知道的缓存性能优化——FastCGI调优

Nginx缓存优化是帮助大家提升网站性能的重要操作之一,proxy_cache主要用于反向代理时,对后端内容源服务器进行缓存;fastcgi_cache主要用于...

2024-05-20 14:44 gowuye

王者荣耀天魔缭乱和逐梦之音返场活动地址 3月22日开启返场活动
王者荣耀天魔缭乱和逐梦之音返场活动地址 3月22日开启返场活动

王者荣耀官方终于确定了天魔缭乱和逐梦之音的返场活动,这让不少小伙伴乐开了花,返场活动将会在3月22日开启,下面就带来王者荣耀天魔缭乱和逐梦之音返场活动地址!王者...

2024-05-20 14:44 gowuye

常见的嵌入式web服务器有哪些?

嵌入式WEB服务器常见的有:Lighttpd,Shttpd,Thttpd,Boa,Mini_httpd,Appweb,Goahead。Lighttpd地址:http://www.light...

简述几款常见的嵌入式web服务器
简述几款常见的嵌入式web服务器

嵌入式web服务器,是web服务器当中的一种,是基于嵌入式系统而实现的web服务器。指的是在嵌入式系统(通俗点就是单片机系统)上实现的一个web服务器,可以通过...

2024-05-20 14:44 gowuye

教你如何利用fastcgi_cache缓存加速WordPress

在使用nginx缓存之前,必须在nginx里面加载专门的模块,这个模块叫做ngx_cache_purge。添加ngx_cache_purge模块下载ngx_cache_purge模块ngx_cache...

扫描WordPress漏洞

检测已知漏洞WPScan是一款广泛使用的WordPress安全扫描工具,它的一项重要功能是检测已知漏洞。在这篇文章中,我们将深入探讨WPScan如何检测已知漏洞,并结合实际示例,帮助读者更好地理解和应...

消灭 Bug!推荐几个给力的开源 Bug 跟踪工具
消灭 Bug!推荐几个给力的开源 Bug 跟踪工具

在这个充满bug的世界里,最遥远的距离不是生与死,而是你亲手制造的bug就在你眼前,你却怎么都找不到它。因此本文准备了7款优秀的开源bug跟踪系...

2024-05-20 14:43 gowuye

生物信息分析入门全攻略

生物信息学是生命科学研究的重大前沿领域,未来将占据生命科学研究的半壁江山。已经有越来越多的小伙伴投入到生物信息的学习中,但是入门难、深入慢、摸不到方向等都成为持续学习的拦路虎。本文根据生物信息技术大牛...

elkb实践经验,再赠送一套复杂的配置文件
elkb实践经验,再赠送一套复杂的配置文件

原创:小姐姐味道(微信公众号ID:xjjdog),欢迎分享,转载请保留出处。宝剑锋从磨砺出,梅花香自苦寒来。诗人白居易,三月下江南,看到沿路开放的桃花,心潮澎湃...

2024-05-20 14:43 gowuye

超详细从0到1 搭建ELK监控
超详细从0到1 搭建ELK监控

监控分类?Metrics用于记录可聚合的数据。例如,1、队列的当前深度可被定义为一个度量值,在元素入队或出队时被更新;HTTP请求个数可被定义为一个计数器,...

2024-05-20 14:42 gowuye

嵌入式开发 之Web配置页面开发
嵌入式开发 之Web配置页面开发

1.PHP是最好的语言??开发动态页面首选的语言是PHP,村村不能在这里忽悠人,如果你的硬件性能允许切略懂PHP,看到这里就可以退出了。本文面向的受众是Linu...

2024-05-20 14:42 gowuye

Python开发一个网站目录扫描工具用来检测网站是否有漏洞?
Python开发一个网站目录扫描工具用来检测网站是否有漏洞?

开发一个网站目录扫描工具是用来检测网站是否有非法目录请求的一个常见需求之一,我们要通过这个扫描工具来找到通过某个域名可以访问到的网站路径,可能对于有些系统来讲,...

2024-05-20 14:42 gowuye

创建一个类似Youtube的Id——使用PHP/Python/JS/Java/SQL

id通常都是用数字,不巧的是只有10个数字来使用,所以如果你有很多的记录,id往往变得非常冗长。当然对于计算机来说无所谓,但我们更希望id尽可能短。所以我们如何能使id变短?我们可以利用字母让它们附加...

快速云:有助于移动应用安全开发的五条妙计
快速云:有助于移动应用安全开发的五条妙计

许多企业不断地向其开发团队提供培训。但是某些漏洞,如早在十多年前就发现的SQL注入,如今仍广泛存在于各种应用中。因而,安全培训永不过时。在开发移动应用时,开发者...

2024-05-20 14:41 gowuye

洛杉矶国际电影节最佳动画短片奖影片《G’DAY》正式全网上映
洛杉矶国际电影节最佳动画短片奖影片《G’DAY》正式全网上映

7月2日,由M&CSaatchi创作,由深受好评的澳大利亚导演迈克尔·格雷西执导的动画短片《G’day》,正式在全网上映。该影片因其出色的创意赢得了洛...

2024-05-20 14:41 gowuye

取消回复欢迎 发表评论: