C 结构体

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

定义结构

必须使用 struct 语句。struct 语句定义了一个包含多个成员的新的数据类型,struct 语句的格式如下
struct tag {
member-list
member-list
member-list …
} variable-list ;

tag 是结构体标签
member-list 是标准的变量定义,比如 int i; 或者 float f,或者其他有效的变量定义
variable-list 结构变量,定义在结构的末尾,最后一个分号之前,您可以指定一个或多个结构变量
声明 Book 结构的方式:
struct Books{
char title[50];
char author[50];
char subject[100];
int book_id;
} book;

** 在一般情况下, tag , member-list ,variable-list 这3部分 至少要出现2个 :::

结构体变量的初始化

  1. #include <stdio.h>
  2. struct Book
  3. {
  4. char title[50];
  5. char author[50];
  6. char subject[100];
  7. int id;
  8. } book = {"C 语言", "RUNOOB", "编程语言", 123456};
  9. int main()
  10. {
  11. printf("title : %s\nauthor: %s\nsubject: %s\nbook_id: %d\n", book.title, book.author, book.subject, book.book_id);
  12. }
  13. title : C 语言
  14. author: RUNOOB
  15. subject: 编程语言
  16. book_id: 12345

访问结构成员

:::info 为了访问结构的成员,我们使用成员访问运算符(.)
成员访问运算符是结构变量名称和我们要访问的结构成员之间的一个句号
您可以使用 struct 关键字来定义结构类型的变量。下面的实例演示了结构的用法 :::

  1. #include <stdio.h>;
  2. #include <string.h>;
  3. struct Books
  4. {
  5. char title[50];
  6. char author[50];
  7. char subject[100];
  8. int book_id;
  9. };
  10. int main()
  11. {
  12. //有点像创建2个新结构
  13. struct Books Book1; /* 声明 Book1,类型为 Books */
  14. struct Books Book2; /* 声明 Book2,类型为 Books */
  15. //赋值值到指定的目标
  16. strcpy(Book1.title, "C");
  17. //or 或者 直接赋值应该也可
  18. Book1.title = "C Programming";
  19. strcpy( Book1.author, "Nuha Ali");
  20. strcpy( Book1.subject, "C Programming Tutorial");
  21. Book1.book_id = 6495407;
  22. /* Book2 详述 */
  23. strcpy( Book2.title, "Telecom Billing");
  24. strcpy( Book2.author, "Zara Ali");
  25. strcpy( Book2.subject, "Telecom Billing Tutorial");
  26. Book2.book_id = 6495700;
  27. /* 输出 Book1 信息 */
  28. printf( "Book 1 title : %s\n", Book1.title);
  29. printf( "Book 1 author : %s\n", Book1.author);
  30. printf( "Book 1 subject : %s\n", Book1.subject);
  31. printf( "Book 1 book_id : %d\n", Book1.book_id);
  32. /* 输出 Book2 信息 */
  33. printf( "Book 2 title : %s\n", Book2.title);
  34. printf( "Book 2 author : %s\n", Book2.author);
  35. printf( "Book 2 subject : %s\n", Book2.subject);
  36. printf( "Book 2 book_id : %d\n", Book2.book_id);
  37. /* 输出 Book1 信息 */
  38. printBook( Book1 );
  39. /* 输出 Book2 信息 */
  40. printBook( Book2 );
  41. /* 通过传 Book1 的地址来输出 Book1 信息 */
  42. printBook( &Book1 );
  43. /* 通过传 Book2 的地址来输出 Book2 信息 */
  44. printBook( &Book2 );
  45. return 0;
  46. }
  47. // 结构作为函数参数 、封装book 结构体
  48. void printBook (struct Books book)
  49. {
  50. printf( "Book title : %s\n", book.title);
  51. printf( "Book author : %s\n", book.author);
  52. printf( "Book subject : %s\n", book.subject);
  53. printf( "Book book_id : %d\n", book.book_id);
  54. }
  55. // 定义 指针函数 来输出
  56. void printBook1( struct Books *book)
  57. {
  58. printf( "Book title : %s\n", book.title);
  59. printf( "Book author : %s\n", book.author);
  60. printf( "Book subject : %s\n", book.subject);
  61. printf( "Book book_id : %d\n", book.book_id);
  62. }

C 共用体

:::info 共用体是一种特殊的数据类型,允许您在相同的内存位置存储不同的数据类型。您可以定义一个带有多成员的共用体,但是任何时候只能有一个成员带有值。共用体提供了一种使用相同的内存位置的有效方式

共用体是一种特殊的数据类型,允许您在相同的内存位置存储不同的数据类型。您可以定义一个带有多成员的共用体,但是任何时候只能有一个成员带有值。共用体提供了一种使用相同的内存位置的有效方式 :::

  1. #include <stdio.h>
  2. #include <string.h>
  3. union Data
  4. {
  5. int i;
  6. float f;
  7. char str[20];
  8. };
  9. int main( )
  10. {
  11. union Data data;
  12. printf( "Memory size occupied by data : %d\n", sizeof(data));
  13. data.i = 10;
  14. data.f = 220.5;
  15. strcpy( data.str, "C Programming");
  16. printf( "data.i : %d\n", data.i);
  17. printf( "data.f : %f\n", data.f);
  18. printf( "data.str : %s\n", data.str)
  19. /*
  20. data.i : 1917853763
  21. data.f : 4122360580327794860452759994368.000000
  22. data.str : C Programming
  23. 我们可以看到共用体的 i 和 f 成员的值有损坏,
  24. 因为最后赋给变量的值占用了内存位置,这也是 str 成员能够完好输出的原因。
  25. */
  26. data.i = 10;
  27. printf( "data.i : %d\n", data.i);
  28. data.f = 220.5;
  29. printf( "data.f : %f\n", data.f);
  30. strcpy( data.str, "C Programming");
  31. printf( "data.str : %s\n", data.str);
  32. /*
  33. data.i : 10
  34. data.f : 220.500000
  35. data.str : C Programming
  36. 在这里,所有的成员都能完好输出,因为同一时间只用到一个成员。
  37. */
  38. return 0;
  39. }

:::info 结构体小结
1、结构体的出现是为了把一些不同类型的数据作为一个整体来引用

共用体定义:
它的定义方式和结构体相似,只是把关键字改为union。
2,.共用体的内存长度分析:
与结构体不同的是,公用体所占的内存长度是它的最长的成员的长度。

3.对于公用体的注意点小结:
1.每一瞬间只有一个成员起作用。
2.共用体变量中起作用的成员是最后一次放入的数据成员。这点要格外注意。
3.公用体变量的地址和它的各个数据成员的地址是一样的。
4.不能对共用体变量名赋值,注意的是,不能在定义功能体变量时对它初始化。 :::

C 位域

如果程序的结构中包含多个开关量, 只有 TRUE/FALSE 变量

  1. #include <stdio.h>
  2. #include <string.h>
  3. /* 定义简单的结构 */
  4. struct
  5. {
  6. unsigned int widthValidated;
  7. unsigned int heightValidated;
  8. } status1;
  9. /* 定义位域结构 */
  10. struct
  11. {
  12. unsigned int widthValidated : 1;
  13. unsigned int heightValidated : 1;
  14. } status2;
  15. int main()
  16. {
  17. printf( "Memory size occupied by status1 : %d\n", sizeof(status1));
  18. printf( "Memory size occupied by status2 : %d\n", sizeof(status2));
  19. return 0;
  20. }
  21. Memory size occupied by status1 : 8
  22. Memory size occupied by status2 : 4

位域声明

:::info 有些信息在存储时,并不需要占用一个完整的字节,而只需占几个或一个二进制位
例如在存放一个开关量时,只有 0 和 1 两种状态,用 1 位二进位即可。为了节省存储空间,并使处理简便,
C 语言又提供了一种数据结构,称为”位域”或”位段”

所谓”位域”是把一个字节中的二进位划分为几个不同的区域,并说明每个区域的位数。每个域有一个域名,允许在程序中按域名进行操作。这样就可以把几个不同的对象用一个字节的二进制位域来表示。
典型的实例:

  • 用 1 位二进位存放一个开关量时,只有 0 和 1 两种状态。
  • 读取外部文件格式——可以读取非标准的文件格式。例如:9 位的整数。 :::

:::info

位域的定义和位域变量的说明

struct 位域结构名 {
位域列表
}; :::

下面是有关位域中变量元素的描述:

元素 描述
type 只能为 int(整型),unsigned int(无符号整型),signed int(有符号整型) 三种类型,决定了如何解释位域的值。
member_name 位域的名称。
width 位域中位的数量。宽度必须小于或等于指定类型的位宽度。

带有预定义宽度的变量被称为位域。位域可以存储多于 1 位的数,例如,需要一个变量来存储从 0 到 7 的值,
您可以定义一个宽度为 3 位的位域

  1. struct
  2. {
  3. unsigned int age : 3;
  4. } Age;
  5. age 变量将只使用 3 位来存储这个值,如果您试图使用超过 3 位,则无法完成。
  6. struct bs{
  7. int a:8;
  8. int b:2;
  9. int c:6;
  10. }data;
  11. data bs 变量,共占两个字节。其中位域 a 8 位,位域 b 2 位,位域 c 6 位。
  12. struct packed_struct {
  13. unsigned int f1:1;
  14. unsigned int f2:1;
  15. unsigned int f3:1;
  16. unsigned int f4:1;
  17. unsigned int type:4;
  18. unsigned int my_int:9;
  19. } pack;
  20. 在这里,packed_struct 包含了 6 个成员:
  21. 四个 1 位的标识符 f1..f4
  22. 一个 4 位的 type 和一个 9 位的 my_int

C typedef

:::info C 语言提供了 typedef 关键字,您可以使用它来为类型取一个新的名字。
下面的实例为单字节数字定义了一个术语 BYTE

typedef unsigned char BYTE;

在这个类型定义之后,标识符 BYTE 可作为类型 unsigned char 的缩写,例如:

BYTE b1, b2;

按照惯例,定义时会大写字母,以便提醒用户类型名称是一个象征性的缩写,但您也可以使用小写字母,
typedef unsigned char byte;

:::

  1. #include <stdio.h>
  2. #include <string.h>
  3. typedef struct Books
  4. {
  5. char title[50];
  6. char author[50];
  7. char subject[100];
  8. int book_id;
  9. } Book;
  10. int main( )
  11. {
  12. Book book;
  13. strcpy( book.title, "C 教程");
  14. strcpy( book.author, "Runoob");
  15. strcpy( book.subject, "编程语言");
  16. book.book_id = 12345;
  17. printf( "书标题 : %s\n", book.title);
  18. printf( "书作者 : %s\n", book.author);
  19. printf( "书类目 : %s\n", book.subject);
  20. printf( "书 ID : %d\n", book.book_id);
  21. return 0;
  22. }
  23. 书标题 : C 教程
  24. 书作者 : Runoob
  25. 书类目 : 编程语言
  26. ID : 12345

:::info

typedef vs #define

define 是 C指令 ,用于为各种数据类型定义别名, 与typedef 类似,但是它们有以下几点不同:

  • typedef 仅限于为类型定义符号名称,
  • #define 不仅可以为类型定义别名,也能为数值定义别名,比如您可以定义 1 为 ONE。
  • typedef 是由编译器执行解释的,#define 语句是由预编译器进行处理的。

:::

  1. #include <stdio.h>
  2. #define TRUE 1
  3. #define FALSE 0
  4. typedef struct Books
  5. {
  6. char title[50];
  7. char author[50];
  8. char subject[100];
  9. } Book;
  10. int main( )
  11. {
  12. Book book1, Book2;
  13. printf( "TRUE 的值: %d\n", TRUE);
  14. printf( "FALSE 的值: %d\n", FALSE);
  15. return 0;
  16. }

C 输入 & 输出

:::info 当我们提到输入时,这意味着要向程序填充一些数据。输入可以是以文件的形式或从命令行中进行
C 语言提供了一系列内置的函数来读取给定的输入,并根据需要填充到程序中。
当我们提到输出时,这意味着要在屏幕上、打印机上或任意文件中显示一些数据。
C 语言提供了一系列内置的函数来输出数据到计算机屏幕上和保存数据到文本文件或二进制文件中。

标准文件

C 语言把所有的设备都当作文件。所以设备(比如显示器)被处理的方式与文件相同。
以下三个文件会在程序执行时自动打开,以便访问键盘和屏幕。 ::: | 标准文件 | 文件指针 | 设备 | | —- | —- | —- | | 标准输入 | stdin | 键盘 | | 标准输出 | stdout | 屏幕 | | 标准错误 | stderr | 您的屏幕 |

文件指针是访问文件的方式,本节将讲解如何从屏幕读取值以及如何把结果输出到屏幕上。
C 语言中的 I/O (输入/输出) 通常使用 printf() 和 scanf() 两个函数。
scanf() 函数用于从标准输入(键盘)读取并格式化,
printf() 函数发送格式化输出到标准输出(屏幕)。

C 内存管理

C 中的动态内存管理。
C 语言为内存的分配和管理提供了几个函数。这些函数可以在 头文件中找到。

序号 函数和描述
1 void *calloc(int num, int size);
在内存中动态地分配 num 个长度为 size 的连续空间,并将每一个字节都初始化为 0。所以它的结果是分配了 num*size 个字节长度的内存空间,并且每个字节的值都是0。
2 void free(void *address);
该函数释放 address 所指向的内存块,释放的是动态分配的内存空间。
3 void *malloc(int num);
在堆区分配一块指定大小的内存空间,用来存放数据。这块内存空间在函数执行完成后不会被初始化,它们的值是未知的。
4 void realloc(void address, int newsize);
该函数重新分配内存,把内存扩展到 newsize

注意:void 类型表示未确定类型的指针。C、C++ 规定 void 类型可以通过类型转换强制转换为任何其它类型的指针。

动态分配内存

编程时,如果预先知道数组的大小,那么定义数组时就比较容易。
例如,一个存储人名的数组,它最多容纳 100 个字符,
所以可以定义数组,如下所示
char name[100];

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. int main()
  5. {
  6. char name[100];
  7. char *description;
  8. strcpy(name, "Zara Ali");
  9. /* 动态分配内存 */
  10. description = (char *)malloc( 200 * sizeof(char) );
  11. if( description == NULL )
  12. {
  13. fprintf(stderr, "Error - unable to allocate required memory\n");
  14. }
  15. else
  16. {
  17. strcpy( description, "Zara ali a DPS student in class 10th");
  18. }
  19. printf("Name = %s\n", name );
  20. printf("Description: %s\n", description );
  21. }