【C语言】关于我回头学的那些结构体等(三)

这篇具有很好参考价值的文章主要介绍了【C语言】关于我回头学的那些结构体等(三)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

我的第一门语言就是C,但是学艺不精,中途跑去学了C#和Java后,感觉到了C的重要性,毕竟是最接近底层的语言,又跑回来学C。

毕竟前两门的控制语句,变量什么的都是类似的,回到C后只需要学习一些特定C的语法,比如宏,预编译指令等等,这些对我来说都是陌生的词汇。

所以边学边记录一下以前的知识。



一、字符串

在 C 语言中,字符串实际上是使用空字符 \0 结尾的一维字符数组。因此,\0 是用于标记字符串的结束。

空字符(Null character)又称结束符,缩写 NUL,是一个数值为 0 的控制字符,\0 是转义字符,意思是告诉编译器,这不是字符 0,而是空字符。

char site[7] = {‘R’, ‘U’, ‘N’, ‘O’, ‘O’, ‘B’, ‘\0’};


二、结构体

C 数组允许定义可存储相同类型数据项的变量,结构是 C 编程中另一种用户自定义的可用的数据类型,它允许您存储不同类型的数据项。

struct Books
{
   char  title[50];
   char  author[50];
   char  subject[100];
   int   book_id;
} book;

结构体的成员可以包含其他结构体,也可以包含指向自己结构体类型的指针,而通常这种指针的应用是为了实现一些更高级的数据结构如链表和树等。

//此结构体的声明包含了其他的结构体
struct COMPLEX
{
    char string[100];
    struct SIMPLE a;
};
 
//此结构体的声明包含了指向自己类型的指针
struct NODE
{
    char string[100];
    struct NODE *next_node;
};

如果两个结构体互相包含,则需要对其中一个结构体进行不完整声明

struct B;    //对结构体B进行不完整声明
 
//结构体A中包含指向结构体B的指针
struct A
{
    struct B *partner;
    //other members;
};
 
//结构体B中包含指向结构体A的指针,在A声明完后,B也随之进行声明
struct B
{
    struct A *partner;
    //other members;
};

1)结构体初始化

#include <stdio.h>
 
struct Books
{
   char  title[50];
   char  author[50];
   char  subject[100];
   int   book_id;
} book = {"C 语言", "RUNOOB", "编程语言", 123456};
 
int main()
{
    printf("title : %s\nauthor: %s\nsubject: %s\nbook_id: %d\n", book.title, book.author, book.subject, book.book_id);
}

2)结构体的访问

#include <stdio.h>
#include <string.h>
 
struct Books
{
   char  title[50];
   char  author[50];
   char  subject[100];
   int   book_id;
};
 
int main( )
{
   struct Books Book1;        /* 声明 Book1,类型为 Books */
   struct Books Book2;        /* 声明 Book2,类型为 Books */
 
   /* Book1 详述 */
   strcpy( Book1.title, "C Programming");
   strcpy( Book1.author, "Nuha Ali"); 
   strcpy( Book1.subject, "C Programming Tutorial");
   Book1.book_id = 6495407;
 
   /* Book2 详述 */
   strcpy( Book2.title, "Telecom Billing");
   strcpy( Book2.author, "Zara Ali");
   strcpy( Book2.subject, "Telecom Billing Tutorial");
   Book2.book_id = 6495700;
 
   /* 输出 Book1 信息 */
   printf( "Book 1 title : %s\n", Book1.title);
   printf( "Book 1 author : %s\n", Book1.author);
   printf( "Book 1 subject : %s\n", Book1.subject);
   printf( "Book 1 book_id : %d\n", Book1.book_id);
 
   /* 输出 Book2 信息 */
   printf( "Book 2 title : %s\n", Book2.title);
   printf( "Book 2 author : %s\n", Book2.author);
   printf( "Book 2 subject : %s\n", Book2.subject);
   printf( "Book 2 book_id : %d\n", Book2.book_id);
 
   return 0;
}

3)作为形参

#include <stdio.h>
#include <string.h>
 
struct Books
{
   char  title[50];
   char  author[50];
   char  subject[100];
   int   book_id;
};
 
/* 函数声明 */
void printBook( struct Books book );
int main( )
{
   struct Books Book1;        /* 声明 Book1,类型为 Books */
   struct Books Book2;        /* 声明 Book2,类型为 Books */
 
   /* Book1 详述 */
   strcpy( Book1.title, "C Programming");
   strcpy( Book1.author, "Nuha Ali"); 
   strcpy( Book1.subject, "C Programming Tutorial");
   Book1.book_id = 6495407;
 
   /* Book2 详述 */
   strcpy( Book2.title, "Telecom Billing");
   strcpy( Book2.author, "Zara Ali");
   strcpy( Book2.subject, "Telecom Billing Tutorial");
   Book2.book_id = 6495700;
 
   /* 输出 Book1 信息 */
   printBook( Book1 );
 
   /* 输出 Book2 信息 */
   printBook( Book2 );
 
   return 0;
}
void printBook( struct Books book )
{
   printf( "Book title : %s\n", book.title);
   printf( "Book author : %s\n", book.author);
   printf( "Book subject : %s\n", book.subject);
   printf( "Book book_id : %d\n", book.book_id);
}

4)指向结构体的指针

您可以定义指向结构的指针,方式与定义指向其他类型变量的指针相似,如下所示:

struct Books *struct_pointer;

让指向该结构体的指针,去访问结构内部的成员,您必须使用 -> 运算符,如下所示:

struct_pointer->title;

#include <stdio.h>
#include <string.h>
 
struct Books
{
   char  title[50];
   char  author[50];
   char  subject[100];
   int   book_id;
};
 
/* 函数声明 */
void printBook( struct Books *book );
int main( )
{
   struct Books Book1;        /* 声明 Book1,类型为 Books */
   struct Books Book2;        /* 声明 Book2,类型为 Books */
 
   /* Book1 详述 */
   strcpy( Book1.title, "C Programming");
   strcpy( Book1.author, "Nuha Ali"); 
   strcpy( Book1.subject, "C Programming Tutorial");
   Book1.book_id = 6495407;
 
   /* Book2 详述 */
   strcpy( Book2.title, "Telecom Billing");
   strcpy( Book2.author, "Zara Ali");
   strcpy( Book2.subject, "Telecom Billing Tutorial");
   Book2.book_id = 6495700;
 
   /* 通过传 Book1 的地址来输出 Book1 信息 */
   printBook( &Book1 );
 
   /* 通过传 Book2 的地址来输出 Book2 信息 */
   printBook( &Book2 );
 
   return 0;
}
void printBook( struct Books *book )
{
   printf( "Book title : %s\n", book->title);
   printf( "Book author : %s\n", book->author);
   printf( "Book subject : %s\n", book->subject);
   printf( "Book book_id : %d\n", book->book_id);
}

5)结构体占用空间内存大小


01.struct tagApple 
02.{  
03.     char   A;  
04.     int    B;  
05.     short  C;  
06.}apple;

我们都知道,char类型占用1个字节,int型占用4个字节,short类型占用2个字节,long占用4个,double占用8个;

那么我们可能会犯一个错误就是直接1+4+2=7,该结构体占用7个字节。这是错的。

以下我们简单分析下:

计算结构体大小时需要考虑其内存布局,结构体在内存中存放是按单元存放的,每个单元多大取决于结构体中最大基本类型的大小。
对格式一:
【C语言】关于我回头学的那些结构体等(三)
以int型占用4个来作为倍数,因为A占用一个字节后,B放不下,所以开辟新的单元,然后开辟新的单元放C,所以格式一占用的字节数为:3*4=12;


三、共用体

共用体的格式是和结构体是类似的。是一种特殊的数据类型,允许您在相同的内存位置存储不同的数据类型。

您可以定义一个带有多成员的共用体,但是任何时候只能有一个成员带有值。共用体提供了一种使用相同的内存位置的有效方式。

union Data
{
   int i;
   float f;
   char  str[20];
} data;

1)共用体的内存大小

下面的实例将显示上面的共用体占用的总内存大小:

#include <stdio.h>
#include <string.h>
 
union Data
{
   int i;
   float f;
   char  str[20];
};
 
int main( )
{
   union Data data;        
 
   printf( "Memory size occupied by data : %d\n", sizeof(data));
 
   return 0;
}

打印出来结果是20


2)访问成员变量

我们在前面说过可以定义一个带有多成员的共用体,但是任何时候只能有一个成员带有值

下面这个例子就来看看访问成员变量以及值的问题。

#include <stdio.h>
#include <string.h>
 
union Data
{
   int i;
   float f;
   char  str[20];
};
 
int main( )
{
   union Data data;        
 
   data.i = 10;
   data.f = 220.5;
   strcpy( data.str, "C Programming");
 
   printf( "data.i : %d\n", data.i);
   printf( "data.f : %f\n", data.f);
   printf( "data.str : %s\n", data.str);
 
   return 0;
}

运行后结果如下:
【C语言】关于我回头学的那些结构体等(三)

在这里,我们可以看到共用体的 i 和 f 成员的值有损坏,因为最后赋给变量的值占用了内存位置,这也是 str 成员能够完好输出的原因。


那我们注释掉strcpy( data.str, “C Programming”);会怎么样呢??

【C语言】关于我回头学的那些结构体等(三)
此时此刻结果就是f的值出来了
【C语言】关于我回头学的那些结构体等(三)


那我们继续注释掉f

【C语言】关于我回头学的那些结构体等(三)

结果不言而喻,肯定是i输出正常,于是就证明了同个时间只能有一个成员带有值,毕竟这三个变量是占一个地址的,也是有原因的。

【C语言】关于我回头学的那些结构体等(三)


3)如何同时访问共用体成员

#include <stdio.h>
#include <string.h>
 
union Data
{
   int i;
   float f;
   char  str[20];
};
 
int main( )
{
   union Data data;        
 
   data.i = 10;
   printf( "data.i : %d\n", data.i);
   
   data.f = 220.5;
   printf( "data.f : %f\n", data.f);
   
   strcpy( data.str, "C Programming");
   printf( "data.str : %s\n", data.str);
 
   return 0;
}

在这里,所有的成员都能完好输出,因为同一时间只用到一个成员。


四、结构体的定制版——位域

位域位域,位就是操作二进制的。

1)采用传统结构体

struct
{
  unsigned int A;
  unsigned int B;
} status;

在结构体中,我们知道怎么计算一个结构体占用空间内存大小。此时两个int成员变量,每个各占了4个字节,2*4=8。

Bit Bit Bit Bit Bit Bit Bit Bit
A A A A A A A A
B B B B B B B B

这样虽然占满了空间,但是我们不用到这么多bit啊,蹲坑不拉屎相当于浪费内存,因为我们只需要一个Bit。

2)采用位域

位域是 C 语言中一种使用表示成员变量的二进制位的方式,可以有效地节省内存空间。

在定义结构体时,使用冒号 : 来定义某个二进制位的字段,如下所示:

struct MyStructure {
   unsigned int age : 5;    // 占用 5 个二进制位
   unsigned int gender : 1; // 占用一个二进制位
   // 其他成员变量
};

在上面的例子中,agegender 是结构体 MyStructure 的两个成员变量。冒号后面的数字表示这个成员变量占据的二进制位数(Bit),8个二进制位组成一个字节。

例如,age 变量占用了 5 个二进制位,它可表示的数值范围为 0 ~ 31。

那么此时上面的结构体占用了多少内存空间呢?

此时此刻他们占用了这个结构体占用了 1 个字节(8 比特)的内存空间。

如图所示,下面是一个字节:

Bit Bit Bit Bit Bit Bit Bit Bit
age age age age age gender

其中,agegender 都是使用了位域(bit-field)来定义的变量。

age 使用了 5 个二进制位来表示,因此它的取值范围是 0-31。gender 只需要一个 Bit 来表示,因此它只有两种取值方式:0 或者 1。

由于 C 语言中最小的可寻址内存单位是一个字节(即 8 Bit),这里占用了整整一个字节的内存空间。

在这个结构体中,虽然只有两个位域变量,但是实际上由于内存对齐等原因可能会产生填充,于是会填充到age后面去,不会再重新创建一个字节去存放gender成员变量。

再举个栗子:请问下面代码占用了多大的内存空间?

struct
{
  unsigned int A: 1;
  unsigned int B: 1;
} status;

这个结构体还是只占用了 1 个字节(8 Bit)的内存空间。

其中,A 和 B 都是使用了位域(Bit-field)来定义的变量,它们都只需要占用 1 Bit 的存储空间,因为它们只有两种状态(0 或 1),所以每个变量都可以用一个 Bit 表示。

由于 C 语言中最小的可寻址内存单位是一个字节(即 8 Bit),因此就算这个结构体中只有两个位域变量,也需要占用至少一个字节的内存空间。在这个结构体中,status 只包含了两个位域成员,因此它所有成员共占用了 2 Bit 的空间(1 Bit * 2),但是实际上由于内存对齐等原因可能会产生填充。

如图所示,下面是一个字节:

Bit Bit Bit Bit Bit Bit Bit Bit
A B

看出来使用位域有什么好处了吧,是能大大节省我们的内存空间,这对于性能的提升等等都有不小的好处。

3)位域溢出

当位域只有3个Bit时,我们用4个BIt会出现什么情况呢?

#include <stdio.h>
#include <string.h>
 
struct
{
  unsigned int age : 3;
} Age;
 
int main( )
{
   Age.age = 4;
   printf( "Sizeof( Age ) : %d\n", sizeof(Age) );
   printf( "Age.age : %d\n", Age.age );
 
   Age.age = 7;
   printf( "Age.age : %d\n", Age.age );
 
   Age.age = 8; // 二进制表示为 1000 有四位,超出
   printf( "Age.age : %d\n", Age.age );
 
   return 0;
}

Sizeof( Age ) : 4
Age.age : 4
Age.age : 7
Age.age : 0

4)位域的注意事项

而使用位域时首先需要注意以下几点:

  • 不同编译器对于位域的实现可能有所区别,因此需要特别小心。
  • 在同一个字节中不能定义重叠的位域。
  • 如果结构体中包含多个位域,则它们必须按照从高到低或从低到高的顺序排列。
  • 对于有符号数值类型,在使用负数时需要格外小心。

使用位域时还需要考虑:

  • 具体的编译器实现和硬件架构等因素对内存布局的影响。

  • 不同编译器对于位域大小和对齐方式的处理可能会有所不同,如果不同的编译器对位域大小和对齐方式处理不一致,可能会导致代码可移植性问题。

  • 在使用位域时应该避免同时定义多个不同类型和大小的位域,否则可能会导致对齐和填充问题。

总之,位域是一种很方便但也很复杂的内存管理方式,在使用时需要特别小心。文章来源地址https://www.toymoban.com/news/detail-409938.html

到了这里,关于【C语言】关于我回头学的那些结构体等(三)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • #VCS# 关于VCS 编译选项:+vcs+initreg+random的理解(3)回头是岸

    前段时间,有时间整理了一下关于+vcs+initreg+random 编译和仿真选项的心得,草草写了两篇笔记。自觉得对该选项已经掌握了,后来实际应用中再次触及到了该知识点,不想又卡壳了。今天,继续追加一篇,希望对大家能有所帮助!不正之处,请指正。 对于上述规则rule#2和 ru

    2024年01月20日
    浏览(30)
  • 【数据结构】前言概况 - 树

    🚩 纸上得来终觉浅, 绝知此事要躬行。 🌟主页:June-Frost 🚀专栏:数据结构 🔥该文章针对树形结构作出前言,以保证可以对树初步认知。  线性结构是一种相对简单的数据结构,元素之间按照一定的顺序排列,每个元素最多有两个接口:前驱和后继。这种结构相对直观

    2024年02月07日
    浏览(56)
  • 数据结构前言

    数据结构是计算机存储、组织数据的方式。数据结构是指相互之间存在一种或多种特定关系的数据元素的集合。 上面是百度百科的定义, 通俗的来讲数据结构就是数据元素集合与数据元素集合或者数据元素与数据元素之间的组成形式。 举个简单明了的例子: 就像一个图书馆

    2024年02月09日
    浏览(37)
  • 【算法与数据结构】--前言

    欢迎来到《算法与数据结构》专栏!这个专栏将引领您进入计算机科学领域中最重要、最精彩的领域之一:算法与数据结构。不管您是一名初学者,还是已经拥有一定编程经验的开发者,都可以从这里找到有益的知识和实践。 在计算机科学的世界里,算法和数据结构是至关重

    2024年02月07日
    浏览(235)
  • 【c语言】详解c语言#预处理期过程 | 宏定义前言

    c语言系列专栏: c语言之路重点知识整合   创作不易,本篇文章如果帮助到了你,还请点赞支持一下♡𖥦)!!  主页专栏有更多知识,如有疑问欢迎大家指正讨论,共同进步! 给大家跳段街舞感谢支持!ጿ ኈ ቼ ዽ ጿ ኈ ቼ ዽ ጿ ኈ ቼ ዽ ጿ ኈ ቼ ዽ ጿ ኈ ቼ 代码编译到执

    2024年02月01日
    浏览(36)
  • [C语言实现]数据结构之《关于我转生成队列这档事》

    🥰作者: FlashRider 🌏专栏: 数据结构 🍖知识概要:详解 队列 的概念、顺序队列和链式队列的优点和缺点,以及代码实现。 目录 什么是队列? 选择什么结构来实现队列? 链式队列的实现 队列的结构体实现 队列需要的函数声明 队列的函数实现 测试代码 队列其实就是一种操

    2024年02月09日
    浏览(21)
  • 关于BGP安全那些事儿

    文| 宙斯盾DDoS防护团队 Rocky 导语 美国时间10月4日中午,Facebook公司网络出现重大故障,故障持续了6个小时后才恢复。官方给出的故障原因,简单来说是一次误操作引发了连锁反应。 (复杂点就是:在例行网络维护中,发送的一条命令无意中关闭了其全球骨干网的所有BGP连

    2023年04月08日
    浏览(37)
  • 关于测试的那些事02

    目录 测试工具 1.禅道 2.selenium IDE 3.UI自动化优点 4.webdriver的原理 (1)定位元素(必须全局唯一): (2)操作测试对象 (3)等待 (4)信息打印   (5)对浏览器的操作   (6)键盘事件 (7)定位一组元素 (8)多层框架定位 (9)层级定位 (10)下拉框中某个元素的定位三种方

    2023年04月08日
    浏览(22)
  • 关于小程序认证那些事

    回答: 是的,24年开始,小程序每年都要认证一次,个人主体每次认证缴费30元,企业主体认证一次300块 回答: 微信认证跟备案是独立的,我刚遇到了认证通过的名字,在备案的时候因为名字被驳回了。 回答: 不需要,认证完成后,每年有二次的改名机会。

    2024年03月26日
    浏览(30)
  • 关于数藏的那些事

    相信大家最近都有注意到数字藏品这个横空出世的流行浪潮,很多人应该都是在市面上在那个见过各种这样的数藏作品,但却不是很了解其中真正的前因后果。数藏到底是什么?为什么会突然在市场上火爆?火爆的背后我们应该注意什么? 下面由我来为大家解答一下上面提出

    2024年02月15日
    浏览(25)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包