• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
caimjblog
博客园    首页    新随笔    联系   管理    订阅  订阅
# 2024-2025-1 20241328《计算机基础与程序设计》第十四周学习总结

2024-2025-1 20241318 《计算机基础与程序设计》第十四周学习总结

作业信息

|作业课程|2024-2025-1-计算机基础与程序设计|
|作业要求|2024-2025-1计算机基础与程序设计第十四周作业|

教材学习内容总结

第13章 文件操作

1. 文件的基本概念

  • 文件是持久化存储数据的单位。
  • 文件分为文本文件和二进制文件。
  • 每个文件都有一个文件名和文件路径。

2. 文件的打开与关闭

  • 使用 fopen 函数打开文件。
FILE *fp = fopen("filename", "mode");
  • mode 可以是 "r"(读)、"w"(写)、"a"(追加)、"rb"(二进制读)、"wb"(二进制写)等。

  • 使用 fclose 函数关闭文件。

fclose(fp);

3. 文件的读写操作

  • 文本文件的读写

  • 使用 fgets 读取一行。

  • 使用 fprintf 写入格式化数据。

  • 使用 fscanf 读取格式化数据。

  • 二进制文件的读写

  • 使用 fread 读取数据块。

  • 使用 fwrite 写入数据块。

4. 文件指针的管理

  • 使用 ftell 函数获取文件当前指针位置。
  • 使用 fseek 函数设置文件指针的位置。
  • 使用 rewind 函数将文件指针移至文件开头。

5. 文件的错误处理

  • 检查文件是否成功打开,查看返回值是否为 NULL。
  • 使用 feof 检查文件结束。
  • 使用 ferror 检查读取/写入是否出错。

6. 文件操作的示例

#include

int main() {
FILE *fp;
char buffer[100];

// 打开文件
fp = fopen("example.txt", "r");
if (fp == NULL) {
perror("Error opening file");
return -1;
}

// 读取文件内容
while (fgets(buffer, sizeof(buffer), fp) != NULL) {
printf("%s", buffer);
}

// 关闭文件
fclose(fp);
return 0;
}

第14章 预处理器

1. 预处理器简介

  • 预处理器在编译前处理源代码。
  • 处理任务包括文件包含、宏定义、条件编译等。

2. 文件包含

  • 使用 #include 指令包含头文件。
  • 系统头文件:#include
  • 用户自定义头文件:#include "myheader.h"

3. 宏定义

  • 使用 #define 定义宏。
#define PI 3.14
#define SQUARE(x) ((x) * (x))

4. 条件编译

  • 使用 #ifdef、#ifndef、#if、#elif、#else 和 #endif 控制代码的编译。
#ifdef DEBUG
printf("Debug mode\n");
#endif

5. 预处理器指令的其他用法

  • #undef 取消宏定义。
  • #line 指定源代码行号。
  • #error 生成编译错误。

6. 示例代码

#include

#define PI 3.14
#define SQUARE(x) ((x) * (x))

int main() {
float radius = 5.0;

// 计算圆的面积
float area = PI * SQUARE(radius);
printf("Area of circle: %.2f\n", area);

return 0;
}

总结

  • 第13章讲述了C语言中文件的基本操作,包括如何打开、关闭、读取和写入文件。
  • 第14章介绍了C语言的预处理器,包括宏、条件编译和文件包含等预处理指令。

教材学习中的问题和解决过程(先问 AI)

问题1:fopen 打开文件失败的原因是什么?

引用文字:

fopen 函数用于打开一个文件。如果文件成功打开,返回一个文件指针;如果失败,返回 NULL。

问题:

为什么我的程序中调用 fopen 总是返回 NULL,文件确实存在,路径也没有问题?

查了资料:

查阅了一些文档和论坛,了解到 fopen 返回 NULL 可能有多个原因:

  • 文件路径错误或文件不存在
  • 文件权限问题,当前用户没有读取或写入权限
  • 文件系统限制(如文件数目过多)
  • 打开文件时使用的模式不正确(如只读模式试图打开一个只写文件)

根据我的实践:

我检查了路径是否正确,尝试用绝对路径打开文件,确保文件是存在的,并且没有任何权限问题。还确认了文件的模式是正确的,最终问题是文件被其他程序占用,导致我无法访问。

困惑:

尽管我已经检查了路径和权限,为什么一些情况下 fopen 还是会返回 NULL?能否通过程序自动诊断和输出更详细的错误信息?

问题1解决方案:

可以通过使用 perror 或 strerror(errno) 来获取更详细的错误信息,帮助诊断问题。

FILE *file = fopen("file.txt", "r");
if (file == NULL) {
    perror("Error opening file");
}

问题2:结构体指针和结构体变量的区别是什么?

引用文字:

结构体指针和结构体变量都是结构体类型的,但前者存储的是结构体对象的地址,后者直接存储结构体的内容。

问题:

为什么有时使用结构体指针比直接使用结构体变量更好?结构体指针在传递给函数时是不是比结构体变量更高效?
查了资料:
查阅资料后,了解到:

  • 结构体指针传递的是地址,避免了复制整个结构体的数据,从而提高效率,尤其是在结构体比较大的时候。
  • 结构体变量在函数调用时会复制整个结构体,可能导致性能下降,尤其是在结构体较大时。

根据我的实践:

在我处理大结构体(如一个包含大量字段的结构体)时,确实发现通过指针传递结构体给函数可以减少内存消耗和提高运行效率。使用结构体指针时,我还需要小心内存管理,确保指针有效,避免野指针。
困惑:
结构体指针确实有优势,但我不太明白如何在使用结构体指针时避免内存泄漏或野指针的问题?

问题2解决方案:

为了避免内存泄漏或野指针,可以通过初始化指针、确保在使用完指针后释放内存,以及使用 NULL 来避免指针悬空。

struct Person *ptr = malloc(sizeof(struct Person)); // 动态分配内存
if (ptr == NULL) {
   perror("Memory allocation failed");
}
free(ptr); // 释放内存
ptr = NULL; // 设置为NULL,防止野指针

基于AI的学习

你:我在学习指针的使用。

GPT(提问):那么,指针在C语言中究竟是什么呢?

你:指针是用来存储变量地址的。

GPT(提问):很好,指针存储的地址有什么用呢?

你:它可以用来直接访问内存中的数据。

GPT(提问):那你如何定义一个指针,给它赋值呢?

你:我使用 type *pointerName; 声明指针,然后用 pointerName = &variable; 赋值。

GPT(提问):那么,当你使用 *pointerName 时,你期待获取到什么?

你:我希望得到指针所指向的变量的值。

GPT(提问):这是一个很好的理解。那么,如果指针没有被初始化,你认为会发生什么?

你:我不知道。可能会崩溃吗?

GPT(提问):这是一个很有趣的思考。你能想象在程序中访问未初始化的指针会引发哪些后果吗?

你:这可能导致程序访问到不该访问的内存,产生错误。

GPT(提问):那么,如何避免这种情况发生?

你:我应该在定义指针时,将它初始化为 NULL。

GPT(提问):很好,你已经对指针有了一些理解。现在,请问你针对“指针的使用”这个知识点,理解了吗?

你:大概理解了,但具体细节我还有些不确定。

GPT(提问):那么,你认为哪些具体细节需要进一步的澄清和解答?

你:我对指针的算术运算不是很清楚,有点困惑。

GPT(提问):那对于指针的算术运算,你能讲讲你目前的理解吗?

你:我知道可以通过加减来移动指针,但具体是如何计算的,我不太明白。

GPT(提问):很好,你的理解已经迈出了第一步。你觉得指针算术运算与数据类型有什么关系?

你:我觉得可能与指针指向的数据类型的大小有关?

GPT(提问):那你能举个例子说明一下吗?

你:比如,对于一个 int 类型的指针,加1可能会移动4个字节。

GPT(提问):非常好!这样理解是精准的。你的思考过程很好,最后,请问你的回答结束了,是否希望我对你的回答进行评价总结?

你:是的,请总结一下。

GPT(总结):你对指针的使用有了基本的理解,包括指针的定义、用途和初始化。通过对指针算术运算的讨论,你正确地认识到数据类型的大小会影响指针的行为。建议你在实践中多进行指针的使用和运算,以加深理解。如果有任何具体疑问,随时提出!

代码调试中的问题和解决过程

  • 问题1:编译器提示“未定义的引用”错误
    问题1解决方案:
    这种错误通常是由于函数声明和定义不匹配,或者在链接时找不到目标文件。
    解决步骤:

    1. 确认函数的声明和定义是否一致,尤其是参数的类型、返回值等。
    2. 检查是否忘记链接某个目标文件(例如忘记链接某个 .c 文件编译后的 .o 文件)。
    3. 如果是多文件项目,确保在编译时正确链接了所有依赖的源文件和库。
  • 问题2:程序运行时崩溃,提示“段错误”(Segmentation Fault)
    问题2解决方案:
    段错误通常是由于访问了无效的内存区域(如空指针、数组越界等)导致的。
    解决步骤:

    1. 检查是否存在空指针访问。可以在访问指针前加上判空判断:
      if (ptr != NULL) {
          // 安全访问
      }
      
    2. 确保数组没有越界访问。检查数组的索引是否在有效范围内。
    3. 使用调试工具(如 gdb 或 valgrind)来定位出错的位置。
  • 问题3:函数返回值不符合预期
    问题3解决方案:
    如果函数返回值不符合预期,通常可能是以下原因:

    1. 变量初始化问题:确保所有的局部变量和结构体成员都已正确初始化。
    2. 返回值类型问题:检查函数的返回类型和实际返回值是否匹配。如果返回类型是 int,确保没有返回 float 或其他不兼容类型。
    3. 逻辑错误:检查函数内部的计算或条件判断是否符合预期。可以通过在关键位置添加 printf 输出调试信息。
      printf("x = %d, y = %d\n", x, y);
      
    4. 优化问题:有时编译器优化可能会改变代码的行为,尝试关闭优化选项(如 -O0)进行调试。
  • 问题4:数组或字符串输出不正确
    问题4解决方案:
    数组或字符串输出不正确通常是因为访问了未初始化的内存,或者输出时格式不对。
    解决步骤:

    1. 确保数组和字符串的大小足够,避免越界访问。
    2. 在字符串结束时加上 \0 来保证字符串的正确终止:
      char str[20];
      strcpy(str, "Hello");
      str[5] = '\0';  // 显式设置字符串结束符
      
    3. 检查输出格式是否正确。如果打印数组,可以使用循环遍历每个元素:
      for (int i = 0; i < length; i++) {
          printf("%d ", arr[i]);
      }
      
  • 问题5:变量值改变后调试时无法正确查看值
    问题5解决方案:
    调试时无法正确查看变量值,可能是由于编译器优化导致变量被优化掉了,或者调试信息丢失。
    解决步骤:

    1. 确保使用 -g 选项进行编译,以包含调试信息。
    2. 禁用编译器优化选项(如 -O0),这样可以防止优化掉未使用的变量。
    3. 在调试过程中,避免过多的内联函数,特别是在对某些变量的查看过程中,可能导致信息丢失。
  • 问题6:程序中使用动态内存分配后内存泄漏
    问题6解决方案:
    动态内存分配后,如果没有释放内存,会导致内存泄漏。
    解决步骤:

    1. 每次使用 malloc 或 calloc 等函数分配内存时,必须在不再使用该内存时调用 free 来释放它。
    2. 使用 valgrind 工具检测内存泄漏。它能帮助你找出未释放的内存位置。
      valgrind --leak-check=full ./my_program
      
    3. 确保没有在重复调用 free 时发生错误,因为这可能会导致程序崩溃。
  • 问题7:程序输出结果与预期不符
    问题7解决方案:
    程序输出与预期不符,可能是由于未考虑边界条件或有逻辑错误。
    解决步骤:

    1. 检查循环条件是否正确,确保没有进入死循环或遗漏重要的迭代。
    2. 使用 printf 打印关键变量的值,帮助检查程序运行时的状态。
    3. 检查函数参数和返回值,确保数据流在各个函数之间传递正确。

其他(感悟、思考等,可选)

学习《C语言程序设计》让我深刻体会到编程不仅仅是技术问题,更是思维方式的锻炼。从初学者的困惑到逐步掌握数据结构、指针、内存管理等核心概念,我逐渐意识到编程的逻辑性和严谨性。在调试代码的过程中,我学会了如何细致地排查问题,如何通过小小的改变去优化代码。这不仅提高了我的问题解决能力,也让我更加注重代码的可读性和效率。通过不断练习,我明白了编程是一个不断试错、总结和提高的过程,而这一过程本身也充满了挑战与乐趣。

学习进度条

代码行数(新增/累积) 博客量(新增/累积) 学习时间(新增/累积) 重要成长
目标 5000行 30篇 400小时
第一周 200/200 2/2 20/20
第二周 300/500 2/4 18/38
第三周 500/1000 3/7 22/60
第四周 300/1300 2/9 30/90
第五周 400/1700 1/10 20/110
第六周 350/2050 1/11 20/130
第七周 450/2500 1/12 20/150
第八周 300/2800 1/13 20/170
第九周 400/3200 1/14 20/190
第十周 300/3500 1/15 20/210
第十一周 200/3700 1/16 20/230
第十二周 200/3900 1/17 20/250
第十三周 100/4000 1/18 20/270
第十四周 100/4100 1/19 20/290

计划学习时间: 400小时

实际学习时间: 290小时

改进情况:

本周达成了每周20小时的学习计划,但由于一些时间安排不够紧凑,实际学习时间略低于目标时间。下周计划将进一步优化时间管理,提高学习效率,以确保按时完成学习目标。继续保持每周写一篇博客,记录学习过程中的收获与难点,便于总结与反思。

参考资料

  • 《计算机科学概论(第七版)》
posted on 2024-12-29 13:36  Eureka23662  阅读(37)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3