跳转到内容

C-EASY-1:C语言入门

计算机系统Banner

C语言方向提交总述:

  • 对于每一个题目,你需要提交包含:
    • 一份回答该小节问题的文字说明文件,要求使用Markdown语言进行排版
    • 题目中要求提供的可执行程序或代码文件
  • 每一题请提交对应的github的url链接。

在第一节C语言课程时,你的老师会要求你配置C语言环境。按照要求完成环境配置以后,你是否会有疑问,每一步的作用是什么?当编译环境配置好后,编译器中的三个配置文件的作用是什么?应该怎么调试?

现在我们一步一步来看:

  1. 什么是GCC,什么是MinGW?它的作用是什么?

  2. c_cpp_properties.json launch.json tasks.json这三个文件分别有什么作用?

  3. 为什么要在编译器内下载C语言的插件,插件的作用又是什么?

应该指出,在后续的代码编写过程中,最常更改的就是launch.json,比如是否要有外置控制台,或者使用内置终端。

那么launch.json中的配置都是什么意思?

{
// 使⽤ IntelliSense 了解相关属性。
// 悬停以查看现有属性的描述。
// 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0",
"configurations": [
{
"name": "gcc.exe - ⽣成和调试活动⽂件", // 该调试任务的名字,启动调试时会在待选列表中显⽰
"type": "cppdbg",
"request": "launch",
"program": "${fileDirname}\\${fileBasenameNoExtension}.exe", "args": [],
"stopAtEntry": false, //
"cwd": "${workspaceFolder}",
"environment": [],
"externalConsole": false, //
"MIMode": "gdb",
"miDebuggerPath": "C:\\mingw64\\bin\\gdb.exe", //
"setupCommands": [
{
"description": "为 gdb 启⽤整⻬打印",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
],
"preLaunchTask": "C/C++: gcc.exe build active file" // 调试前的预执⾏任务,这⾥的值是tasks.json⽂件中对应的编译任务,也就是调试前需要先编译
}
]
}

这是一个launch.json的文件,请你补全上面的注释,并尝试更改你自己的设置,然后用外置终端和内置终端分别打印“hello world”并上传你输出结果的截图。

C语言是大多数人学习的第一门计算机语言。我知道很多同学在上大学以前都没有接触过编程,接下来的招新题目都需要使用C语言进行,要求你至少要基本掌握C语言语法。所以我建议你,不要急于完成接下来我给出的题目,而是去找一个完整的网课或者一部详细的电子书,系统的学习并入门C语言,(这里我推荐黑马程序员和翁凯的课程)。我们的招新只有一轮,会一直持续到期中考试前两周才会截止,因此你完全可以拿出一两周的时间先学习C语言再进行更深入的招新题的学习。并且,我希望你在学习第一门语言的过程中,先不要打开AI的代码补全功能,这会很大的降低你的代码熟练度。希望你可以合理的使用AI。

我们刚刚完成了一个打印“hello world”的简单程序,可是如果我们想进行一定的更改(比如打印不同的内容),都要直接修改源代码。通过搜索引擎,你得知这样不仅太过繁琐,而且不利于我们对程序进行测试,在实际应用中也几乎不可行。所以你打算对程序进行一定的改写,使之可以实现动态交互。于是你打算进一步学习变量数组流程控制 - 循环结构流程控制 - 逻辑表达式等知识。

  1. 变量类型: 什么是变量的类型?它为什么重要?如果你想存放你的年龄(比如 18),应该选择哪种基本变量类型(如整数、浮点数)?如果你想存放单词 “apple”,使用一个char类型的字符变量能实现吗?尝试编程验证你的想法,并思考正确的存储方式应该是什么?
  2. 数组的起始与边界: 数组的第一个元素下标通常是从 0 还是 1 开始?访问数组元素时,下标超出其有效范围(例如,长度为 5 的数组访问 arr[5] 或 arr[-1])会导致什么问题?为什么数组越界是一个常见的、危险的错误?
  3. 流程控制 - 循环结构: 循环结构(如 for, while)是如何控制代码块重复执行的?请分别简述它们的基本结构。for 循环的初始化、条件判断和迭代部分各自的作用是什么?while 循环和 do…while 循环在首次执行条件判断上有何关键区别?尝试用两种不同的循环结构实现同一个任务(例如,计算 1 到 10 的和),体会它们的异同。
  4. 流程控制 - 逻辑表达式:在条件判断(如 if, else if)和循环条件中使用的逻辑表达式(如age > 18 && score >= 60)与算术表达式(如a + b * c)有何本质区别(尝试从运算对象和运算结果分析)?运算逻辑符&&,||,!的含义分别是什么?尝试编写包含多个条件的逻辑表达式并预测其输出结果。

你恍然大悟,尝试对程序进行了修改,以期可以实现如下功能:

  1. 设置合适的变量用于存储输入的姓名与年龄。

  2. 利用设置的变量,以原格式打印姓名与年龄。

  3. 在每一次的读取输入->打印内容结束后,记录进行过读取输入->打印内容操作的次数,打印提示询问用户是否继续输入。

  4. 若判断得到用户需要继续输入,则重复之前的读取输入->打印内容操作。

  5. 若判断得到用户不需要继续,则打印记录的进行的读取输入->打印内容操作次数,然后结束程序。

在程序设计中,设置函数的核心目的有两点:代码复用逻辑组织

代码复用是指通过抽象化特定功能模块为独立单元,实现算法逻辑的”一次定义,多次调用”,从而消除冗余代码并确保功能一致性。简单来说,就是把常用的操作打包成工具,随用随取。

逻辑组织是指通过函数边界建立抽象层次结构,将复杂系统分解为高内聚、低耦合的功能模块,实现认知负荷的有效管理。简单来说,就是把代码按功能分装进盒子(函数),便于后续查找、维护和修改。

关于什么是函数,我相信你已经有所了解了,在日后的编程中,我们通常不会在主函数中写大段的语句,而是在主函数中调用自己设定的函数,以实现各种功能。包括实际项目当中,也是如此。在这一题当中,你需要学会如何定义一个简单的函数减少代码复用。

下面我们设置一个情景,方便你更好地了解。

小明、小强、小林是某人三项(共有A、B、C三项)的业余爱好者,某次,三人一同参加了某人三项的趣味赛,成绩如下(满分100):

小明:77 85 91

小强:82 84 84

小林:72 83 100

请你通过平均分和方差的计算,综合考虑后,为三人综合成绩由高到低排序(成绩相等时,对顺序不做要求)。综合考虑的具体方法为:综合成绩 = 3 * 平均分 - 方差 / 3;

下面给出仅由一个main()函数完成的程序:

#include <stdio.h>
int main(){
int x1, x2, x3;
int y1, y2, y3;
int z1, z2, z3;
printf("请输入小明的三项成绩(顺序为A B C,以一个空格为间隔):");
scanf("%d %d %d", &x1, &x2, &x3);
printf("请输入小强的三项成绩(顺序为A B C,以一个空格为间隔):");
scanf("%d %d %d", &y1, &y2, &y3);
printf("请输入小林的三项成绩(顺序为A B C,以一个空格为间隔):");
scanf("%d %d %d", &z1, &z2, &z3);
int p1 = (x1 + x2 + x3) / 3;
int f1 = ((p1 - x1) * (p1 - x1) + (p1 - x2) * (p1 - x2) + (p1 - x3) * (p1 - x3)) / 3;
int zh1 = 3 * p1 - f1 / 3;
int p2 = (y1 + y2 + y3) / 3;
int f2 = ((p2 - y1) * (p2 - y1) + (p2 - y2) * (p2 - y2) + (p2 - y3) * (p2 - y3)) / 3;
int zh2 = 3 * p2 - f2 / 3;
int p3 = (z1 + z2 + z3) / 3;
int f3 = ((p3 - z1) * (p3 - z1) + (p3 - z2) * (p3 - z2) + (p3 - z3) * (p3 - z3)) / 3;
int zh3 = 3 * p3 - f3 / 3;
if (zh1 >= zh2 && zh2 >= zh3) {
printf("小明 > 小强 > 小林");
} else if (zh1 >= zh3 && zh3 >= zh2) {
printf("小明 > 小林 > 小强");
} else if (zh2 >= zh1 && zh1 >= zh3) {
printf("小强 > 小明 > 小林");
} else if (zh2 >= zh3 && zh3 >= zh1) {
printf("小强 > 小林 > 小明");
} else if (zh3 >= zh1 && zh1 >= zh2) {
printf("小林 > 小明 > 小强");
} else { // zh3 >= zh2 && zh2 >= zh1
printf("小林 > 小强 > 小明");
}
return 0;
}

相信你可以看出,在这其中,计算综合成绩(zh)时,代码的重复性非常高;于此同时,输入、计算、排序全过程都在一个函数中完成,不免使得程序看起来过于臃肿。下面,请你参考开始给出的将a + b封装成函数int add(int a, int b)的示例,尝试利用自定义函数将main()函数的下列内容精简

  1. 利用自定义函数,实现乘方的计算(用于方差的计算)。(代码复用)

  2. 利用自定义函数,将三人的综合成绩计算的过程进行封装。(代码复用)

  3. 将综合成绩的排序,利用自定义函数进行封装。(逻辑组织)

上传更改后的整个程序的代码。

相信通过这个例子, 你对自定义函数应该有了初步的、大致的了解。我们通常称函数定义中声明的参数为形参(如:int add(int a, int b)中的int aint b),函数调用时传入的参数为实参(如:main函数中的add(a,b)中的ab)。默认情况下,实参的值会复制给形参,这时会分为两种情况:值传递和引用传递。下面请你进一步学习其相关知识,最好可以了解一下这方面的易错点。然后,尝试回答下面的问题:

  1. 猜猜下列函数的效果(你可以自行运行,看看结果怎么样),尝试从值传递的角度入手,说说为什么会这样

    void swap(int a, int b){
    int temp = a;
    a = b;
    b = temp;
    }
    int main(){
    int a = 10;
    int b = 20;
    swap(a, b);
    }
  2. 怎么修改函数才能真正实现值交换的效果呢?请你初步了解指针。我们会在下一个题中系统学习指针和初步的数据结构。

提交点这里

出题人:出题人头像  taaaakoooo

QQ:1748798371

邮箱:1748798371@qq.com