|这个作业属于哪个课程|2024-2025-1-计算机基础与程序设计|
|这个作业要求在哪里|2024-2025-1计算机基础与程序设计第一周作业|
|这个作业的目标|<复习知识,巩固基础>|
|作业正文|https://www.cnblogs.com/HonJo/p/18593475|
一、教材学习内容
(一)共用体
C语言中的共用体(Union)是一种特殊的数据类型,允许在相同的内存位置存储不同的数据类型。这意味着一个共用体变量可以存储多种不同类型的数据,但是一次只能存储其中的一种。共用体的主要特点是节省内存空间,因为它只占用最大的成员所需的空间。
以下是共用体的基本用法和特点:
-
定义共用体类型:
在C语言中,可以通过typedef
关键字定义一个新的共用体类型。例如:typedef union { int i; float f; char str[20]; } Data;
这里定义了一个名为
Data
的共用体类型,它可以存储一个整数(int
)、一个浮点数(float
)或一个字符数组(char
)。 -
声明共用体变量:
使用定义好的共用体类型来声明变量:Data data;
-
访问共用体成员:
可以通过点(.
)操作符来访问共用体的成员:data.i = 10; // 存储一个整数 data.f = 220.5; // 存储一个浮点数 strcpy(data.str, "C Programming"); // 存储一个字符串
-
共用体成员的内存共享:
共用体的所有成员共享同一块内存空间,因此修改一个成员会影响其他成员的值。例如:data.i = 10; // 此时内存中存储的是整数10 data.f = 220.5; // 此时内存中存储的是浮点数220.5,整数10被覆盖
-
共用体的大小:
共用体的大小等于其最大成员的大小。在上面的例子中,Data
的大小将取决于int
、float
和char[20]
中最大的那个。 -
使用共用体的注意事项:
- 共用体变量在内存中没有固定的偏移量,因此不能作为函数参数传递。
- 共用体成员的访问方式与结构体成员的访问方式相同,但是共用体的所有成员共享内存。
- 共用体可以包含指针,但是必须小心使用,因为它们指向的是共用体的内存位置。
共用体在需要节省内存或者需要将同一块内存用于不同目的时非常有用,比如在嵌入式编程中。但是,由于其成员共享内存的特性,使用时必须小心,以避免数据覆盖和不一致的问题。
(二)单向链表
C语言中的单向链表是一种动态数据结构,由一系列节点组成,每个节点包含数据部分和指向下一个节点的指针。单向链表的特点是节点的链接方向是单向的,即每个节点只指向下一个节点,而不知道前一个节点。这种结构在插入和删除操作中非常灵活,但不支持反向遍历。
以下是单向链表的基本组成和操作:
1. 节点定义
单向链表的每个节点通常包含两个部分:数据域和指针域。数据域用于存储数据,指针域用于存储指向下一个节点的指针。
typedef struct Node {
int data; // 数据域,用于存储数据
struct Node* next; // 指针域,指向下一个节点
} Node;
2. 创建节点
创建一个新的节点,通常需要为数据域赋值,并初始化指针域。
Node* createNode(int data) {
Node* newNode = (Node*)malloc(sizeof(Node)); // 分配内存
if (newNode == NULL) {
// 处理内存分配失败
exit(1);
}
newNode->data = data; // 设置数据
newNode->next = NULL; // 初始化指针域为NULL
return newNode;
}
3. 初始化链表
初始化一个空链表,通常设置头节点为NULL
。
Node* initList() {
return NULL; // 空链表的头节点为NULL
}
4. 插入节点
在单向链表中插入节点通常有三种情况:在链表头部插入、在链表尾部插入和在链表中间插入。
-
在头部插入:
void insertAtHead(Node** head, int data) { Node* newNode = createNode(data); newNode->next = *head; *head = newNode; }
-
在尾部插入:
void insertAtTail(Node** head, int data) { Node* newNode = createNode(data); if (*head == NULL) { *head = newNode; } else { Node* temp = *head; while (temp->next != NULL) { temp = temp->next; } temp->next = newNode; } }
-
在中间插入(例如,插入到值为
prevData
的节点之后):void insertAfter(Node* prevNode, int data) { if (prevNode == NULL) { printf("Previous node cannot be NULL\n"); return; } Node* newNode = createNode(data); newNode->next = prevNode->next; prevNode->next = newNode; }
5. 删除节点
删除节点通常需要找到该节点的前一个节点,并将其next
指针指向要删除节点的下一个节点。
void deleteNode(Node** head, int key) {
Node* temp = *head, *prev = NULL;
if (temp != NULL && temp->data == key) {
*head = temp->next;
free(temp);
return;
}
while (temp != NULL && temp->data != key) {
prev = temp;
temp = temp->next;
}
if (temp == NULL) return;
prev->next = temp->next;
free(temp);
}
6. 遍历链表
遍历单向链表以访问每个节点的数据。
void printList(Node* node) {
while (node != NULL) {
printf("%d ", node->data);
node = node->next;
}
printf("\n");
}
单向链表的优点是插入和删除操作灵活,不需要像数组那样移动元素。缺点是不支持反向遍历,且每个节点需要额外的内存空间来存储指针。单向链表在实现某些算法和数据结构时非常有用,如队列、栈(作为实现方式之一)等。
(三)文件
在C语言中,文件操作是一个重要的部分,允许程序读取和写入文件。C语言提供了一组标准的库函数,用于处理文件的打开、关闭、读取、写入和其他相关操作。这些函数定义在stdio.h
头文件中。
1. 文件指针
在C语言中,每个打开的文件都与一个文件指针(FILE*
类型)关联。文件指针是一个指向FILE
结构的指针,该结构包含了文件的状态信息。
2. 打开文件
使用fopen
函数打开文件,并返回一个文件指针。
FILE *fp;
// 以只读模式打开文件
fp = fopen("example.txt", "r");
// 以写入模式打开文件
fp = fopen("example.txt", "w");
// 以追加模式打开文件
fp = fopen("example.txt", "a");
// 以读/写模式打开文件
fp = fopen("example.txt", "r+");
模式字符串可以包含以下字符:
r
:只读模式打开文件。w
:只写模式打开文件,如果文件存在,则截断为零长度,如果文件不存在,则创建新文件。a
:追加模式打开文件,写操作会在文件末尾追加数据,如果文件不存在,则创建新文件。b
:二进制模式,可以与其他模式组合使用(如rb
、wb
)。+
:更新模式,允许读写。
3. 读取文件
使用fgetc
、fgets
、fread
等函数从文件中读取数据。
// 读取单个字符
char ch = fgetc(fp);
// 读取一行
char buffer[100];
fgets(buffer, sizeof(buffer), fp);
// 读取指定数量的元素
int items_read = fread(buffer, sizeof(char), 100, fp);
4. 写入文件
使用fputc
、fputs
、fwrite
等函数向文件写入数据。
// 写入单个字符
fputc('A', fp);
// 写入字符串
fputs("Hello, World!", fp);
// 写入指定数量的元素
int items_written = fwrite(buffer, sizeof(char), 100, fp);
5. 文件定位
使用rewind
、fseek
、ftell
等函数在文件中定位。
// 将文件指针重置到文件开头
rewind(fp);
// 移动文件指针到指定位置
fseek(fp, offset, whence);
// 获取当前文件指针的位置
long position = ftell(fp);
whence
参数可以是SEEK_SET
(文件开头)、SEEK_CUR
(当前位置)、SEEK_END
(文件末尾)。
6. 关闭文件
使用fclose
函数关闭文件,并释放资源。
fclose(fp);
7. 错误检查
使用ferror
和clearerr
函数检查和清除文件错误。
if (ferror(fp)) {
// 处理错误
}
clearerr(fp); // 清除错误标志
8. 文件结束检测
使用feof
函数检查是否到达文件末尾。
if (feof(fp)) {
// 到达文件末尾
}
9. 临时文件
使用tmpfile
创建临时文件,该文件在关闭时会自动删除。
FILE *temp = tmpfile();
10. 二进制文件操作
C语言中的文件操作函数也可以用于二进制文件,只需在模式字符串中添加b
字符。
文件操作是C语言中处理数据持久化的基本方式,掌握这些操作对于编写能够读写文件的程序至关重要。
二、教材学习中的问题
(一)结构体和共用体的区别
共用体(Union)和结构体(Structure)在C语言中都是复合数据类型,但它们之间存在几个关键的区别:
-
内存分配:
- 结构体:结构体的每个成员都占用自己的内存空间。因此,结构体的大小是其所有成员大小之和加上可能的填充(padding)。
- 共用体:共用体的所有成员共享同一块内存空间。共用体的大小等于其最大成员的大小,而不是所有成员大小之和。
-
成员访问:
- 结构体:结构体的成员可以独立访问,互不影响。
- 共用体:共用体的成员共享内存,因此访问一个成员会影响其他成员的值。在任何时候只能有效访问一个成员。
-
使用目的:
- 结构体:结构体用于需要存储多个不同类型数据项的情况,这些数据项之间没有共享内存的需求。
- 共用体:共用体用于节省内存,适用于需要在相同内存位置存储不同类型的数据,但一次只能使用其中一种类型。
-
初始化:
- 结构体:结构体可以被初始化为一个特定的值集合。
- 共用体:共用体只能初始化其第一个成员,或者在声明时指定某个特定成员的值。
-
数组和指针:
- 结构体:可以创建结构体数组和指向结构体的指针,这在处理大量结构体数据时非常有用。
- 共用体:虽然也可以创建共用体数组和指针,但由于内存共享的特性,使用时需要更加小心。
-
内存对齐:
- 结构体:编译器可能会在结构体中添加填充字节(padding)以满足内存对齐要求,这可能会导致结构体的实际占用空间大于成员大小之和。
- 共用体:由于所有成员共享内存,共用体不受内存对齐的影响,其大小仅取决于最大成员的大小。
-
函数参数:
- 结构体:结构体可以作为函数参数传递,通常通过值或引用。
- 共用体:由于共用体的内存共享特性,它通常不作为函数参数传递,因为参数传递可能会破坏内存中的数据。
-
可移植性:
- 结构体:结构体的布局(layout)在不同编译器或不同平台上是可移植的,只要成员的类型和顺序相同。
- 共用体:共用体的布局可能在不同的编译器或平台上有所不同,因为它们依赖于特定的内存布局规则。
总的来说,结构体和共用体在C语言中扮演着不同的角色,选择使用哪一个取决于具体的应用场景和需求。结构体提供了一种安全的方式来存储和访问不同类型的数据,而共用体则提供了一种节省内存的方式来存储不同的数据类型,但需要程序员小心管理内存访问。
三、基于AI的学习