第十章 结构体与共用体
【重点1】什么是结构体类型?
结构体是一种数据类型,它的特点是可以包含不同类型的数据。想象一下,如果你要描述一本书,你需要书名、作者、页数和价格,而这四项数据分别属于字符串型、整型和浮点型。在此情况下,你就需要一个可以存储各种数据类型的容器,这就是结构体。结构体的定义如下:struct 结构体名 {数据类型1 成员名1;数据类型2 成员名2;……}; 在程序中,我们可以使用typedef关键字,为结构体类型取一个新的名字。
代码示例:
struct Book {
char title[50];
char author[50];
int pages;
float price;
};
typedef struct Book Book;
以上代码定义了一个名为"Book"的结构体类型,它有四个成员:title, author, pages和price。然后使用typedef关键字,为struct Book取了一个新的名字"Book"。
【重点2】如何定义结构体变量?
我们可以使用刚才定义的结构体类型,来定义结构体变量。结构体变量其实就是结构体类型的实例(像你和我一样,都是人类的实例)。一个结构体变量所占用的内存空间等于其所有成员所占空间的总和。
代码示例:
Book book1, book2;
以上代码定义了两个名为"book1"和"book2"的Book类型变量。
【重点3】如何引用结构体成员?
结构体成员可以通过以下方式引用:1)使用点运算符(.),如结构体变量名.成员名。2)如果成员通过指针访问,则应使用箭头运算符(->),如指针变量名->成员名。这两个运算符分别用于访问结构体变量和指向结构体变量的指针所引用的成员。
代码示例:
Book book1;
Book *ptr = &book1;
book1.title = "The Joy of C"; // 使用点运算符访问结构体成员
ptr->title = "The Joy of C"; // 使用箭头运算符访问结构体成员
以上代码首先定义了一个Book类型的变量"book1",然后定义一个指向"book1"的指针"ptr"。最后两行分别使用点运算符和箭头运算符访问book1的title成员。
【重点4】什么是链表?
比起继续使用数组,我们有时会选择使用链表这一更复杂的数据结构。链表由许多节点组成,每个节点都包含一个值和一个指向下一个节点的指针。持之以恒,你会发现这是一个非常强大的工具。
【重点5】共用体有什么用?
共用体和结构体有些类似,都是可以存储不同类型数据的容器。但共用体不同的是,它的所有成员共享同一块内存空间。这意味着你只能在同一时刻,使用其中一个成员。
代码示例:
union Data {
int i;
float f;
char str[20];
};
以上代码定义了一个名为"Data"的共用体类型,它有三个成员:i, f, str。但你应该记住,这三个成员会共享同一块内存。
第十一章 文件
【重点1】什么是文件类型指针?
当我们要操作文件时,通常会使用一个特殊的指针类型:文件指针。这个指针指向的不是内存中的位置,而是磁盘上的文件。文件指针的定义格式为:FILE *指针变量名。
代码示例:
FILE *fp;
以上代码定义了一个名为"fp"的文件指针。
【重点2】文本文件与二进制文件的区别是什么?
文件有两种存储格式:文本格式和二进制格式。文本格式就如你看到的这个文档,都是字符,而二进制格式则以二进制形式存放数据。对于一个数字,如果它被保存为文本格式,文件里存储的是数字的ASCII码值;而以二进制格式保存,文件里存储的是数字的二进制值。
【重点3】怎么打开文件?
要读写文件,首先需要打开文件。你可以使用fopen函数打开文件,其格式为FILE *fp = fopen("文件路径", "模式");。其中,文件路径指定了文件在电脑上的位置,模式指明了你打算如何使用这个文件:读模式(r),写模式(w),附加模式(a)或者二进制模式(b)。
代码示例:
FILE *fp = fopen("C:\\test.txt", "r");
以上代码打开名为"C:\test.txt"的文件以供读取。
【重点4】如何操作文件?
C语言有一些内建的函数来处理文件。例如feof函数用于检查文件是否结束,fseek函数用于移动文件位置,fgets函数和fputs函数可从文件读去和写入字符串,等等。这些函数对于处理文件来说十分有用。
代码示例:
FILE *fp = fopen("C:\\test.txt", "r");
char ch;
while((ch = fgetc(fp)) != EOF) { // 读取文件,直到碰到文件结束符
putchar(ch); // 打印字符
}
fclose(fp); // 关闭文件
以上代码读取"C:\test.txt"的内容,并把它打印到屏幕上。
第十二章 深入讨论
【考点1】编译预处理
编译预处理简单来说就是在编译正式开始前,进行的一些处理工作。#开头的行都是一种指示,告诉编译器需要执行什么任务。但是你并不需要加分号结束,也不会影响你的程序运行时间。例如,宏替换,其实就是在代码中找到对应的宏,然后用你定义的内容替换它。举个例子,说你有一个宏定义#define f(x) (x)(x),然后在你的代码中有一个表达式f(2+2),那么在预处理阶段,就会被替换为(2+2)(2+2)。
然后如果你在一个源文件比如f2.c中有#include "f1.c",那其实就相当于把f1.c的内容原封不动地“黏贴”到了f2.c中。所以就把f1.c和f2.c合并成了一个完整的C程序。
例子:
#define SQUARE(x) (x)*(x)
int main()
{
int a = 4;
printf("The square of %d is %d.\n", a, SQUARE(a));
return 0;
}
注释说明: 这段程序中定义了一个宏SQUARE(x),然后在main函数中使用这个宏计算了变量a的平方。
【考点2】标识符作用域
标识符的作用域是限定这个标识符能在哪些地方被用到的规则。局部变量,就是在某个函数或者语句块内部定义的变量。局部变量只能在定义它的函数内部使用。
全局变量是定义在函数外面,整个程序的任何地方都可以使用的变量。
例子:
#include<stdio.h>
int global_var = 10; // 全局变量,整个程序都可以使用
int main() {
int local_var = 5; // 局部变量,只能在main函数中使用
printf("Global variable: %d\n", global_var);
printf("Local variable: %d\n", local_var);
return 0;
}
注释说明: 这段程序中定义了一个全局变量global_var和一个main函数的局部变量local_var。
【考点3】动态存储分配
动态存储分配就是在程序运行时,而不是在编译时,划分内存空间。动态存储分配可以帮助我们创建恰好需要的大小的内存空间,而不会浪费内存。
例子:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int *ptr;
ptr = (int *) malloc(sizeof(int));
if (ptr == NULL) {
printf("Memory not allocated.\n");
} else {
*ptr = 10; // 在动态分配的内存存储值
printf("Value of ptr = %d\n", *ptr);
free(ptr); //及时释放内存
}
return 0;
}
注释说明:这段程序演示了如何使用malloc函数动态分配内存空间,并确保在完成后适当地释放内存,以防止内存泄漏。