目录
补充:c语言是编译型高级语言,c语言编写的源程序必须先进行编译和连接生成可执行程序后再执行,其步骤如下:编辑-编译-连接-运行。
3. 连接(Linking):将多个部分组合成一个完整的程序
第一节:认识c语言
-
一.走进计算机技术的世界
思考一下:1.你为什么学习计算机专业;2.你对计算机专业了解吗;3.你觉得计算机技术的发展给我们的生活带来了哪些巨大的改变;4.你觉得学了计算机相关知识你以后可以做什么;
-
计算机技术给生活带来的改变
随着科技的发展,计算机技术的不断突破,给我们的生活带来了巨大的改变。
-
自动驾驶 - 萝卜快跑
计算机技术在自动驾驶领域的应用,以“萝卜快跑”为例,展示它是如何通过计算机视觉、传感器融合、机器学习等技术实现车辆的自主导航和决策。
-
人工智能 - ChatGPT
计算机技术在人工智能领域的进展,以OpenAI开发的ChatGPT为例,讨论它如何通过深度学习和自然语言处理技术,实现与人类之间的流畅对话。
-
图像识别
计算机技术在图像识别方面的应用,如人脸识别、物体检测等,这些都是通过计算机视觉技术和神经网络算法实现的。
-
智能家居系统
通过计算机技术控制家中的各种设备,提高生活的便利性和舒适度。
-
健康监测
计算机技术在健康监测领域的应用,如智能手表和健康追踪器如何通过传感器收集数据,并利用算法分析我们的健康状况。
计算机技术给我们的生活带来了前所未有的改变,但是同时也给我们带来了巨大的挑战和危机感。在当今时代已经不是一个靠苦力就能生活的时代了,未来这些不需要技术性的工作都将被计算机所取代。所以我们要进行计算机技术的相关学习,成为科技的使用者,而不是被替代。
-
学习计算机技术能做什么
-
It行业
-
制造业
-
农业
-
金融行业
-
二.揭秘程序的神秘面纱
引入:我们已经了解了计算机给生活带来的改变,也清楚了学习计算机技术我们能做什么。我们发现这些技术是的实现,从专业的角度来讲涉及到计算机科学,工程学,物理学等。但是计算机技术是一个重要的基石。没有计算机技术所有的一切都将成为空谈。因此作为信息时代的我们,一定要好好学习计算机技术。
那么学习计算机技术一个最重要事情的是什么呢?当然是学习编程,编写程序。
你所看到的这些技术本质上就是由一个一个程序组成,下面将带大家进入编程世界。
-
什么是计算机程序和编程
-
计算机程序:也称为软件程序或程序代码,是一系列指令的集合,这些指令由计算机直接识别或通过某种翻译手段执行。这些指令告诉计算机如何执行特定的任务或解决特定的问题。
编程:又称编定程序,是指导计算机按照特定逻辑顺序执行任务的行为。它通过将人类思维转化为计算机可理解的指令,实现与计算机系统的交互。
编程语言:也被称为计算机语言,是用来定义计算机程序的形式语言。它是一种标准化的交流技巧,用于向计算机发出指令,使计算机能够执行特定的任务或解决特定的问题。编程语言是计算机与人类之间的桥梁,它将人类的思想转化为计算机能够理解的指令,从而实现各种功能。
生动形象的了解什么是计算机程序:
想象一下你手里有一本超级详细的菜谱,上面写着怎么做你最喜欢的巧克力蛋糕。这个菜谱上有很多步骤,比如“打两个鸡蛋”、“加入半杯糖”和“放入烤箱烤20分钟”等。这些步骤就是计算机程序中的指令。
程序员就像是写菜谱的人,他们用特殊的语言(编程语言)来编写这些指令,让计算机能够理解并执行。
计算机是厨师,计算机就像是那个超级聪明的厨师,它能够读取菜谱(程序)上的每一个指令,并且按照指令的顺序,一步一步地完成任务。就像厨师需要工具(如锅碗瓢盆)来做饭一样,计算机也需要一些硬件设备(如处理器、内存和硬盘)来运行程序。
执行程序:当你把菜谱(程序)交给厨师(计算机)时,厨师就会开始按照菜谱上的步骤来准备蛋糕。同样地,当你运行一个计算机程序时,计算机就会按照程序中的指令来执行任务。
如果菜谱上的某个步骤写错了(比如“加入两勺盐”写成了“加入两袋盐”),那么做出来的蛋糕可能就会很咸。同样地,如果程序中的某个指令写错了,那么程序可能就无法正确运行,或者得到错误的结果。
程序的多样性:就像菜谱有很多种,比如做蛋糕的、做披萨的、做面条的等,计算机程序也有很多种。有的程序可以帮助我们学习新知识(就像教育软件),有的程序可以让我们玩游戏(就像游戏软件),还有的程序可以帮助我们管理电脑上的文件和文件夹(就像操作系统)。
-
认识c语言
C语言是一种广泛使用的计算机编程语言,它诞生于1972年的贝尔实验室,由Dennis Ritchie设计。C语言以其简洁、高效、可移植性强等特点,在计算机科学及软件工程领域占据着重要地位。下面我将从C语言的特点、应用领域以及学习C语言的重要性三个方面来介绍C语言,希望同学们能够认识到学习C语言的重要性。
-
C语言的特点
1.简洁高效:
C语言是一种高级语言,但它与硬件紧密相关,可以直接访问计算机的硬件资源,如内存和处理器。这使得C语言在执行效率上非常高,特别是在处理大规模数据和高性能要求的应用中表现出色。
2.可移植性强:
C语言的设计初衷就是“write once, run anywhere”(一次编写,到处运行)。这意味着用C语言编写的程序可以在不同的计算机平台上运行,而无需进行大量的修改。这得益于C语言使用标准库函数和语法规则,提供了适应各种平台的特性和结构。
3.强大的控制能力:
C语言提供了丰富的控制结构,如循环、条件判断等,使得程序员能够精确地控制程序的执行流程。同时,C语言还支持指针操作,使得程序员能够直接访问和修改内存中的数据,这对于底层开发和系统编程尤为重要。
-
C语言的应用领域
1.系统级编程:
C语言在系统级编程中占据主导地位,如操作系统(如Unix和Linux)的内核开发、驱动程序开发等。这些领域需要高效、可移植且能够直接访问硬件资源的编程语言,而C语言正好满足这些需求。
2.嵌入式系统开发:
嵌入式系统是指嵌入到其他设备中的计算机系统,如智能手机、汽车控制系统等。这些系统的资源有限,对性能要求极高,因此常常使用C语言进行开发。
3.游戏开发:
虽然现代游戏开发更多地使用C++、Java等语言,但C语言在游戏引擎和底层开发中仍然扮演着重要角色。许多游戏引擎(如Unity的底层)都使用了C语言进行编写。
4.网络编程:
C语言在网络编程中也有广泛应用,如编写网络服务器和客户端程序、网络通信协议的实现等。
-
c语言代码的结构
//下面是一个c语言程序
#include<stdio.h> //头文件
int main() //主函数
{
printf("hello world");//printf()是输出函数,用来做输出的,后面会讲;scanf()是输入函数用来输入
return 0;
}
简单来说,一个C程序就是由若干头文件和函数组成。
- #include <stdio.h>就是一条预处理命令, 它的作用是通知C语言编译系统在对C程序进行正式编译之前需做一些预处理工作。
- 函数就是实现代码逻辑的一个小的单元。一个C程序有且只有一个主函数,即main函数。C程序就是执行主函数里的代码,也可以说这个主函数就是C语言中的唯一入口。而main前面的int就是主函数的类型.
- printf()是格式输出函数,这里就记住它的功能就是在屏幕上输出指定的信息
- return是函数的返回值,根据函数类型的不同,返回的值也是不同的。
-
c语言代码书写规范
- 一个说明或一个语句占一行,例如:包含头文件、一个可执行语句结束都需要换行。
- 函数体内的语句要有明显缩进,通常以按一下Tab键为一个缩进。
- 括号要成对写,如果需要删除的话也要成对删除。
- 当一句可执行语句结束的时候末尾需要有分号。
- 代码中所有符号均为英文半角符号
- 无关不需要执行的字符需要删除或者注释
补充:c语言是编译型高级语言,c语言编写的源程序必须先进行编译和连接生成可执行程序后再执行,其步骤如下:编辑-编译-连接-运行。
生动形象的方式来解释C语言程序从编写到运行的整个过程,就像是在烹饪一道美味的菜肴一样。
1. 编辑(Editing):编写代码
想象一下,你是一位大厨,站在干净整洁的厨房里,准备做一道特别的菜肴。这时,你首先需要有一个食谱,也就是你的“菜谱”。在C语言的世界里,这个“菜谱”就是你的代码,而编写代码的过程就像是你在纸上(或者电脑上,更现代一些)一字一句地写下这个食谱。你会用C语言的语法,就像是用特定的调料和烹饪技巧,来规划你的程序应该如何工作。
2. 编译(Compiling):将代码转换为机器语言
写好“菜谱”后,你并不能直接用它来烹饪,因为食材(也就是你的电脑)看不懂人类的语言。这时,你需要一个翻译,来把你的“菜谱”翻译成食材能懂的语言。在C语言的世界里,这个翻译叫做“编译器”。编译器会读取你的代码(菜谱),然后把它转换成一种叫做“机器语言”的东西,这是电脑能直接理解和执行的语言。这个过程就像是厨师根据食谱准备食材,把食材切成需要的形状,调配好各种调料,但还没有开始烹饪。
3. 连接(Linking):将多个部分组合成一个完整的程序
如果你的菜肴非常复杂,可能需要用到多个“菜谱”上的技巧,也就是说,你的程序可能由多个文件组成,每个文件都负责不同的功能。在编译之后,这些单独编译的文件就像是切好的食材和调好的调料,虽然准备好了,但还没有组合在一起。这时,就需要一个“连接器”来把它们组合成一个完整的菜肴(程序)。连接器会找到这些文件之间的关联,确保它们能够正确地配合工作,就像是把切好的蔬菜、肉类和调料混合在一起,准备下锅。
4. 运行(Running):执行程序
最后,所有的食材都已经准备好了,现在你可以开始烹饪了!在C语言的世界里,这意味着你可以运行你的程序了。电脑会按照机器语言的指令,一步一步地执行你的程序,就像是大厨按照食谱的步骤,一步步烹饪出美味的菜肴。如果一切顺利,你的程序就会按照你的预期工作,输出结果,就像是你终于品尝到了自己亲手烹饪的美味佳肴。
通过这个过程,我们可以看到,编写和运行一个C语言程序就像是烹饪一道菜肴,需要精心准备、细心操作,才能最终得到满意的结果。
-
如何学习c语言的建议
- 多看
学会主动查阅资料,观看学习视频;如:csdn,bilibili
- 多敲
一万行代码定律,想要一门编程语言入门你至少要敲一万行代码(可不是一万行hello world,要是有意义的。);推荐刷oj练习基本语法和数据结构(pta,杭电oj,leecode,洛谷等)。同时实际的项目练习也是不可少的,这里可以去github上面找点实际的项目进行练习。
- 多错
再一次次错误中查漏补缺,提升最快的方法就是不断去编写程序,在编写过程中肯定会遇到各种各样的错误,你对错误就行修改研究分析的时候是你提升最快的时候。
第二节:C语言的数据类型
-
一.什么是数据类型
概念:在C语言(以及许多其他编程语言)中,数据类型是用来告诉计算机如何解释存储在变量、函数参数或其他存储单元中的数据的。数据类型不仅指定了数据的种类(比如是整数、浮点数、字符还是其他复杂类型),还定义了这些数据的存储范围和方式。每种数据类型都有其特定的内存占用、表示范围以及可以进行的操作(运算集)。
理解:数据类型在编程语言中就像生活中的物品分类一样,是理解程序世界的基础构建块。想象一下,我们的世界充满了各种各样的物品,从书籍、水果到家具,每种物品都有其独特的性质和用途。同样地,在编程中,数据类型帮助我们区分和处理不同种类的信息,比如数字、文本、日期等。
-
二.数据类型的分类
-
整型(Integer)
字节与范围:
知识点补充:什么是“字节”?
字节是计算机存储数据的基本单位,通常由8个位(bit)组成。
想象一下,你有一个大大的盒子,这个盒子被分成了很多小格子,每个小格子都能放一个小玩具。在这个故事里,“盒子”就像是计算机的内存,而每个“小格子”就是我们要说的“字节”(Byte)。
一个字节(Byte)是计算机存储数据的最小单位,它就像是一个小格子,可以装下一个数字或者一个字符的“玩具”。但是,有时候我们需要存储的信息比较多,一个字节可能不够用,怎么办呢?这时候,我们可以把几个字节放在一起,组成一个更大的单位,就像是用几个小格子拼成一个更大的空间,这个更大的单位就可以叫做“字”(Word)。不过,要注意的是,在不同的计算机和编程语言中,“字”的大小可能不一样,有的可能是2个字节,有的可能是4个字节或者更多。
数据的存储是如何保存的?
现在,我们知道了字节和字是计算机存储数据的小格子和大空间。那么,数据到底是怎么保存在这些格子里的呢?
想象一下,你要把一本故事书的内容放进这些格子里。首先,你会把故事书里的每一个字、每一个标点符号都转换成一种特殊的代码,这些代码就像是一个个小小的数字或者符号。然后,你会按照顺序,把这些代码一个一个地放进格子里。这样,当你需要读这本书的时候,计算机就会按照顺序从格子里取出这些代码,再把它们转换回原来的字和标点符号,你就可以看到完整的故事了。
在C语言中,这个过程也是一样的。当你写了一个程序,程序里的每一个字符、每一个数字都会被转换成一种叫做“二进制”的代码。这些二进制代码就是由0和1组成的,它们就像是小小的开关,0表示关,1表示开。然后,这些二进制代码会被保存在计算机的内存里,也就是那些小格子(字节)里。当你运行程序的时候,计算机就会按照程序的指令,从内存里取出这些二进制代码,然后执行相应的操作。
int:这是最常用的整型,通常占用4个字节(32位)。它的范围大约是-2,147,483,648到2,147,483,647(对于有符号int),或者是0到4,294,967,295(对于无符号unsigned int)。
short:比int小,通常占用2个字节(16位)。它的范围大约是-32,768到32,767(有符号),或0到65,535(无符号)。
long:比int大,但在32位系统上通常也是4个字节,64位系统上通常是8个字节(64位)。64位系统上的有符号long范围大约是-9,223,372,036,854,775,808到9,223,372,036,854,775,807。
long long:为了表示更大的整数,引入了long long类型,在64位系统上通常是8个字节。其有符号范围大约是-9,223,372,036,854,775,808到9,223,372,036,854,775,807(注意,这与64位long的范围相同,但在一些系统中long可能不是64位的)。
为什么一个整形都要有这么多不同的类型?
正常情况下int类型可以满足大部分需求,比如存储一下学校学生人数35000人。此时用int没有任何问题,但是这时候用short就会超出范围从而出现错误:
#include<stdio.h> int main() { int num=35000;//学生人数 short num1=35000;//学生人数 printf("%d\n",num); if(num1!=35000){ printf("数字超出了short的范围"); } else{ printf("%d",num1); } return 0; }
那这是不是意味着short就没有用了?当然不少,对于一些小的数据,我们可以使用short来节约内存的占用,同时碰到那种硬件接口为16位的设备使用short也是不错的选择。
对于long long,long等类型,同理当然是int类型无法满足需求的时候使用。eg:高精度计算,大整数运算等。
下面这段代码可以看到各种数据类型在你的编译环境中所占的字节。 我们可以看到输出:
#include <stdio.h> #include <limits.h> // 包含整型大小限制的宏 int main() { printf("int(整形): %lu bytes, 范围: %d to %d\n", sizeof(int), INT_MIN, INT_MAX); printf("short(短整形): %lu bytes, 范围: %d to %d\n", sizeof(short), SHRT_MIN, SHRT_MAX); printf("long(长整型): %lu bytes, 范围: %ld to %ld\n", sizeof(long), LONG_MIN, LONG_MAX); printf("long long: %lu bytes, 范围: %lld to %lld\n", sizeof(long long), LLONG_MIN, LLONG_MAX); printf("char(字符类型): %lu bytes, 范围: %d to %d\n", sizeof(char), CHAR_MIN, CHAR_MAX); return 0; }
-
浮点型(Float/Double)
浮点型就像是一个有着不同精度刻度的量杯,可以表示小数。想象你有一个量杯,上面有很多刻度,每个刻度之间的间隔代表了浮点数的精度。不同的浮点类型,就像不同大小、不同刻度密度的量杯。
字节与范围:
float:通常占用4个字节(32位)。它的精度较低,可以表示的范围很广,但小数部分可能不够精确。大约能表示的范围是1.4E-45到3.4E+38(有符号),但请注意,这个范围不是直接由字节决定的,而是由IEEE 754标准定义的。
double:通常占用8个字节(64位)。它比float提供了更高的精度和更大的范围,大约是4.9E-324到1.8E+308(有符号)。(找个例子生动形象理解表示范围)
选择使用
float
还是double
类型主要取决于你需要的数值范围和精度。简单来说,double
类型提供了比float
更大的范围和更高的精度,但是相应地,它也占用了更多的内存空间。下面通过一些具体的例子来分析何时使用float
和何时使用double
。使用
float
的情况:
内存限制:当你的程序运行在内存非常受限的环境(如嵌入式系统)中时,使用
float
可以节省内存。虽然现代计算机的内存普遍较大,但在某些特定应用或设备中,内存仍然是一个需要考虑的因素。精度要求不高:如果你处理的数值不需要非常高的精度,使用
float
就足够了。比如,在某些图形处理或游戏开发中,对于物体的位置或速度等属性,使用float
已经足够精确。例子:假设你正在编写一个模拟简单物理运动(如小球弹跳)的程序,你可能只需要跟踪小球的位置和速度。由于这些值的变化范围不是特别大,且对精度的要求也不是特别高(比如,不需要精确到小数点后很多位),因此使用
float
来存储这些值就足够了。使用
double
的情况:
大范围或高精度需求:当你需要处理的数值范围非常大或者需要非常高的精度时,应该使用
double
。比如,进行金融计算、科学计算或工程计算时,常常需要高精度的浮点数运算。默认选择:在很多情况下,如果没有特别的内存限制或精度需求,选择
double
作为默认类型是一个好的选择,因为它提供了更大的范围和更高的精度,减少了因精度不足导致的错误。例子:假设你正在编写一个程序来计算复杂的数学函数(如正弦、余弦函数)的值,或者进行高精度的财务分析(如计算复利)。在这些情况下,由于需要处理的数值范围可能很大,且对精度的要求非常高,因此使用
double
类型更为合适。总结来说,选择
float
还是double
主要取决于你的具体需求,包括内存限制、数值范围和精度要求。在大多数情况下,如果没有特别的限制,推荐使用double
以确保更高的精度和更大的数值范围。
-
字符型(Char)
字符型就像是你的字母表或符号集中的一个格子,每个格子代表一个字符(如字母、数字、标点符号等)。当你想要表示文本信息时,就会用到字符型变量。
字节与范围:
char:在C语言中,char类型通常占用1个字节(8位)。它可以表示的字符范围取决于系统的字符编码方式(如ASCII或Unicode)。在ASCII编码中,char可以表示0到127之间的数字,对应于标准键盘上的字符和一些控制字符。在扩展的ASCII或Unicode编码中,char可能不足以表示所有字符,这时可能会使用wchar_t等更宽的类型。但请注意,wchar_t的确切大小也依赖于系统和编译器。
字符类型肯定是和字符有关啦,现在你想存储个字母‘a’可不能用整形,浮点型了:
char c='a';//定义一个变量储存字符a printf("%d",c);//输出的就是ASCII码的值,a的ascii码是97,所以第一个输出是97 printf("%c",c);//输出的正常字符 //%d就是输出的整形,所以后面的c转换成了字符a对应的ascii码值,而%c输出的是字符型所以输出的是c
-
三.为什么要有数据类型
-
内存管理
不同的数据类型占用不同的内存空间。通过明确数据类型,编译器可以更有效地分配和管理内存资源,避免浪费。
例子:家具与空间(这个例子可以来介绍为什么有数据类型)在布置房间时,你不会在卧室放一张大桌子而忽略了空间大小。这与编程中的数据类型和内存管理相似。不同类型的数据(如整数和浮点数)占用不同的内存空间。使用合适的数据类型就像合理利用空间,既高效又避免了资源浪费。
-
数据安全性
数据类型有助于保护数据的安全性。编译器会根据数据类型进行类型检查,确保数据在赋值、运算等过程中不会超出其表示范围或发生类型不匹配的错误。
例子:比如一个数字超出了int的范围,那么就会出错
-
提高代码可读性
数据类型为程序员提供了一种标准化的方式来描述数据,使得代码更加清晰、易于理解和维护。
例子:一目了然可以知道这个变量是什么类型的,更便于修改和维护
-
支持不同的数据操作和算法
不同的数据类型具有不同的属性和方法,可以支持不同的数据操作和算法。例如,字符串类型支持字符串连接、查找等操作;数组类型支持元素的访问和遍历等操作。
例子:不同的数据类型有不同的操作方法,四则运算,字符串(后边会讲)拼接等。
第三节:C语言中的变量和常量
-
一.常量(Constant)
概念:在程序运行的过程中其值不可改变的量称之为常量,常量分为直接常量和符号常量。
-
直接常量
概念:直接常量也成为“字面常量”,即日常所说的“常数”。直接常量可以分为不同的类型:整形常量,浮点型常量,字符常量,字符串常量。
1.整形常量
整形常量又称为整数,在c语言中整数可以使用十进制,八进制,十六进制的形式来书写。
1. 十进制
我们平时最常用的数字就是十进制了。想象一下你有十个手指头,当你数东西的时候,数到10个就不能再用一个手指头表示了,这时候你就需要再多一个手指头来表示“又一组十个”,也就是10。这就是十进制,它用0到9这十个数字来表示数,每当数到10的时候,就向前进一位,变成1,然后重新开始数。比如,23就是2个十和3个一组成的。
2. 二进制
二进制就像是一个只有开关的机器。在这个机器里,只有两种状态:开(我们用1表示)和关(我们用0表示)。所以,二进制只有0和1这两个数字。也就相当于逢2进1。比如1010在二进制中表示十进制数字10.
3. 八进制
八进制是用0到7这八个数字来表示数的。就像十进制一样,但是它是每数到8个就向前进一位。不过,我们平时不太常用八进制,但在一些计算机语言中,为了简化表示,会用八进制来表示一些数。八进制数的前面有时会加一个0来表示它是八进制的,比如07就是八进制的7,而010就是八进制的8(但在十进制里它只是10)。
4. 十六进制
十六进制比八进制还要“厉害”一点,它用了16个数字来表示数。但是,我们只有0到9这十个数字,怎么办呢?没关系,我们借用了字母A到F来表示剩下的六个数字。所以,十六进制是由0到9和A到F这16个“数字”组成的。每数到16个就向前进一位。十六进制也常用在计算机中,特别是在表示颜色、内存地址等地方。十六进制数的前面有时会加一个0x来表示它是十六进制的,比如0xA就是十六进制的10(但在十进制里它只是10,但在十六进制里它表示的是比9还大的一个数)
其实x进制这玩意就是逢x进1
数值(十进制) | 八进制表达 | 十六进制表达 |
5 | 5 | 5 |
9 | 10 | 9 |
22 | 26 | 1E |
2.浮点型常量
浮点型常量即实数,只能以十进制的形式表示,不能使用八进制或者十六进制,表示可以用小数形式,也可以用指数形式。
3.14 | -0.001 | 1.23e3 |
3.字符常量
在C语言中,字符常量是指用单引号括起来的单个字符。这些字符可以是英文字母(无论大小写)、数字、标点符号或者某些特殊字符(如换行符、制表符等)。字符常量在内存中占用的空间大小取决于编译器和目标系统的具体实现,但通常是1个字节(即8位),能够表示ASCII码表中的字符。
基本规则和示例如下:
单引号括起来:字符常量必须用单引号括起来,而不是双引号。双引号用于表示字符串常量,即一系列字符的组合。
示例:
'A' 是一个字符常量,表示大写字母A。
'5' 是一个字符常量,表示数字5的字符形式,而不是数值5。
'\n' 是一个特殊字符常量,表示换行符。
'\t' 是另一个特殊字符常量,表示制表符(Tab)。
转义序列:对于无法在键盘上直接输入的字符(如换行符、制表符等),或者对于某些有特殊用途的字符(如单引号自身、双引号等),可以使用转义序列来表示。转义序列以反斜杠\开始,后跟一个或多个字符,用来表示特定的字符或字符序列。
字符常量的数值表示:字符常量在内存中是以其ASCII码值(或其他字符编码系统的对应值)存储的。因此,字符常量也可以看作是一个小的整数。例如,字符'A'的ASCII码值是65,因此,在数值上下文中,'A'可以被当作65来处理。
字符常量的类型:在C语言中,字符常量被视为char类型的值。这意味着它们可以参与需要char类型参数的运算或赋值操作。
下面是一些常用的转义字符:
以换行符为例子看一下转义字符的效果:
#include<stdio.h> #include<string.h> #define PI 3.14159 int main() { printf("正常效果:\n"); printf("****************"); printf("hello,world"); printf("****************"); printf("\n\n");//为了区分两个效果,这边插入了换行符 printf("插入换行符的效果:\n"); printf("****************\n"); printf("hello,world\n"); printf("****************"); }
4.字符串常量
- 概念:
字符串常量是由一对双引号括起来的字符序列。
"hello world!"
- 字符串的长度:
字符串中字符的个数成为字符串的长度。
#include<stdio.h>
#include<string.h>
int main()
{
char str[] = "Hello World!";
printf("Hello World!的字符串长度为:%d",strlen(str));//输出字符串长度
}
//输出结果为:Hello World!的字符串长度为:12
//我们可以知道这个字符串的长度为12,因为他一共有12个字符 注意空格也是一个字符
- 字符串的储存:
在C语言中,字符串常量是用来表示一系列字符的序列,这些字符被存储在连续的内存位置中,并且以空字符('\0')作为结束标志。相当于分配的储存空间是字符串常量的长度加1个字节。
-
符号常量
可以用一个标识符代表一个常量,该常量成为符号常量。定义符号常量有两种方式:
1.使用编译预处理命令define
//比如我想用PI来代替浮点数3.14159可以写成如下:
#define PI 3.14159
//这样的话在整个程序中PI就代表浮点数3.14159了
2.使用常量说明符const
const int MAX_SIZE = 100;
//这代表用 MAX_SIZE代替了最大值100
//与define不同的是const常量通常用于函数参数中,表示函数不会修改传入的参数。
-
二.变量
-
概念
变量可以理解为存放数据的容器。它的功能是用来存放程序中需要处理的数据。一个变量必须有变量名,系统可以通过变量名访问变量。
理解:现在,想象你有一个可以装东西的盒子。这个盒子的大小(即它能装多少东西)在制造时就确定了,但你可以随时改变盒子里装的东西。这个盒子就是变量,而盒子里装的东西就是变量的值。变量允许你在程序运行时存储和修改数据。变量的名字就相当于盒子上贴的标签。
-
变量的命名
1.变量名
在C语言中,变量名是用来标识和引用程序中变量的名称。变量是存储数据的容器,其值在程序执行期间可以发生变化。而变量名则是这些容器的标识符,它让程序员能够方便地访问和操作这些容器中的数据。
在C语言的世界里,变量名就像是给房间里的每个盒子贴上的标签,让我们能够轻松地找到并区分里面存放的宝藏(即数据)。想象一下,你走进一个巨大的仓库,里面堆满了各式各样的盒子,每个盒子里都装着不同的东西——从闪亮的金币到珍贵的宝石,再到日常用品。如果没有标签,你可能需要花费大量时间去打开每一个盒子,看看里面是什么,这显然既费时又费力。
变量名,就是这些盒子上贴的标签。它们用简洁明了的文字告诉你,这个盒子里装的是什么类型的宝藏。比如,“goldPieces”这个变量名,让你一眼就能知道这个盒子里装的是金币;“userAge”则告诉你这个盒子里存储的是某个用户的年龄信息。
2.变量的命名规则
- 变量名又称为变量标识符,c语言规定标识符只能由字母,数字,下划线组成,且第一个字符必须由字母或者下划线组成。
- 不能使用c语言中的关键字作为变量名
- 需要选择有意义的变量名,最好是见名知意
- 遵循最小化长度和最大化信息量的原则,保证意思明确且尽量缩短其长度
- 变量名区分大小写
- 变量名中不允许有空格
int myAge; // 正确,以字母开头,包含字母和数字 float salary; // 正确,简单的单词 char _firstChar; // 正确,以下划线开头,虽然不推荐这样做作为一般规则 double MaxValue; // 正确,使用大写字母表示常量风格(尽管C语言本身不区分变量和常量命名) int student_id; // 正确,使用下划线分隔单词以增加可读性 int 1stYear; // 错误,以数字开头 float 2nd sem; // 错误,同样以数字开头 char int; // 错误,使用了保留关键字int double -balance; // 错误,不能以减号或其他特殊字符开头 float class; // 错误,虽然class不是C的保留关键字,但可能与其他语言中的保留关键字冲突,最好避免 int my_age 1; // 错误,变量名不能包含空格,且此例尝试在变量名后添加数字,语法错误
-
变量的定义
变量必须要遵循先定义后使用原则,以便编译系统为其分配相应的储存单元。变量定义的一般形式为:
类型标识符 变量名;
#include <stdio.h> int main() { // 定义一个整型变量 int age = 25; // 定义一个浮点型变量 float salary = 5000.0; // 定义一个字符型变量 char gender = 'M'; // 定义一个双精度浮点型变量 double height = 1.75; // 打印变量值 printf("Age: %d\n", age); printf("Salary: %.2f\n", salary); printf("Gender: %c\n", gender); printf("Height: %.2f\n", height); return 0; }
-
变量的赋值
将某一个数值赋给某个变量的过程称为赋值,所赋的值可以是数字,字母,字符串,表达式等。可以在定义变量的同时给变量赋一个初值,称为变量的初始化。
赋值用‘=’,这里的‘=’是赋值符号,而作为判断的等于则用‘==’来表示。
#include <stdio.h>
int main() {
// 声明变量并立即赋值
int age = 25;
char gender = 'M';
// 后续赋值
float salary;
salary = 5000.0;
// 更改已赋值的变量
age = 30;
// 打印变量值
printf("Age: %d\n", age);
printf("Salary: %.2f\n", salary);
printf("Gender: %c\n", gender);
return 0;
}
第四节:输入与输出
-
一.字符数据的输入与输出
在C语言中,getchar() 和 putchar() 是两个非常基础且常用的函数,分别用于从标准输入(通常是键盘)读取一个字符和向标准输出(通常是屏幕)输出一个字符。这两个函数都定义在 <stdio.h> 头文件中。
-
getchar() 函数
用于接收输入设备输入的一个字符
#include <stdio.h>
int main() {
char c;
printf("请输入一个字符: ");
c = getchar(); // 读取一个字符
putchar(c); // 输出该字符
return 0;
}
-
putchar() 函数
向输出设备输出一个字符
#include <stdio.h>
int main() {
char c = 'A';
putchar(c); // 输出字符 'A'
putchar('\n'); // 输出换行符,以改善输出格式
return 0;
}
-
二.格式化输入与输出
-
格式化输入函数scanf()
scanf()
是 C 语言标准库中的一个函数,用于从标准输入(通常是键盘)读取并格式化输入。它根据指定的格式字符串来解析输入的数据,并将其存储在对应的变量中。
1.格式说明符
格式说明符 说明 %d 读取一个十进制整数。 %i 读取一个整数(可以是十进制、八进制或十六进制)。 %f 读取一个浮点数(单精度) %lf 读取一个双精度浮点数(注意使用 %lf
而不是%f
来读取double
类型)。%s 读取一个字符串(不包含空格) %c 读取一个字符。 %x 读取一个十六进制整数。 %o 读取一个八进制整数。 %u 读取一个无符号整数。 2.修饰符
以下是一些例子:
修饰符 说明 L 用于指示跟随的 d
、i
、o
、u
、x
、X
对应于long
类型的数据;对于f
,它表示double
类型(注意这里的用法有些特殊,因为%f
实际上是用于float
的,而%lf
用于double
)。h 表示 short
类型的整数。* 表示该输入项将被读取但不会被存储(即跳过)。 #include <stdio.h> int main() { // 声明不同类型的变量 int num; // 用于存储整数的变量 double dbl; // 用于存储双精度浮点数的变量 char str[50]; // 用于存储字符串的字符数组,大小设置为50以防止溢出 short sht; // 用于存储短整数的变量 long long llng; // 用于存储长长整数的变量 // 提示用户输入不同类型的数据 printf("请输入一个整数、一个双精度浮点数、一个字符串(不要包含空格)、一个短整数和一个长长整数,每输入一项后按Enter键:\n"); // 使用scanf读取整数 scanf("%d", &num); printf("读取的整数为:%d\n", num); // 使用scanf读取双精度浮点数 scanf("%lf", &dbl); printf("读取的双精度浮点数为:%.2f\n", dbl); // 使用scanf读取字符串,限制长度为49(为'\0'留一个字符的空间) scanf("%49s", str); // 注意:%s默认读取直到遇到空白字符,这里限制了最大读取长度 printf("读取的字符串为:%s\n", str); // 使用scanf读取短整数 scanf("%hd", &sht); printf("读取的短整数为:%d\n", sht); // 使用scanf读取长长整数 scanf("%lld", &llng); printf("读取的长长整数为:%lld\n", llng); // 注意:在实际应用中,应检查scanf的返回值以确保所有项都成功读取 // 演示忽略输入项的使用(假设我们想忽略一个整数) int temp; printf("请输入一个将被忽略的整数和一个额外的整数:\n"); scanf("%*d %d", &temp); // 第一个整数被忽略,第二个整数被读取到temp printf("读取的额外整数为:%d\n", temp); // 示例结束 return 0; }
注意:
使用scanf()时,需要确保提供的变量地址与格式说明符匹配。
scanf()在读取字符串时,会在遇到空格、制表符或换行符时停止读取,因此它不能用来读取包含空格的字符串。
scanf()函数在遇到非法输入时可能会出错,此时应检查其返回值以判断读取是否成功。
空白字符(如空格、制表符、换行符)在scanf()的格式字符串中会被忽略,但在输入数据中会被用来分隔不同的输入项。
使用
scanf()
时要小心缓冲区溢出问题,特别是当使用%s
读取字符串时,因为默认情况下scanf()
不会检查目标缓冲区的大小。当输入格式与期望的格式不匹配时,
scanf()
会失败并返回读取的项数(不包括任何因匹配失败而被跳过的项)。读取字符时,
scanf()
会跳过任何空白字符(空格、制表符、换行符等),直到遇到第一个非空白字符。如果你需要读取空白字符(如空格),你可能需要使用getchar()
或其他函数。
-
格式化输出函数printf()
printf()函数是C语言标准输入输出库(stdio.h)中的一个重要函数,用于向标准输出(通常是终端或屏幕)格式化输出数据。它能够处理各种类型的数据,包括整数、浮点数、字符、字符串等,并允许通过格式化字符串来控制输出的格式。
1.格式说明符
格式说明符 说明 %d
或%i
输出十进制整数。 %u 输出无符号整数。 %f 输出浮点数(默认为双精度,单精度需使用 %f
但可能因编译器而异,通常推荐使用%e
、%g
或%f
与double
类型的%lf
区分)。%lf 输出双精度浮点数。 %c 输出单个字符。 %s 输出字符串。 %x
或%X
输出十六进制整数( %x
使用小写字母,%X
使用大写字母)%o 输出八进制整数。 %% 输出 %
字符本身。2.修饰符
修饰符 说明 数字(如 %5d
)指定最小字段宽度。如果数值的位数少于指定的宽度,则默认使用空格填充(除非指定了其他填充字符)。 . 后跟数字(如 %.2f
):指定精度(对于浮点数)或最大字符数(对于字符串)。-
(如%-5d
)左对齐输出(默认情况下为右对齐)。 0
(如%05d
)使用 0
而不是空格作为填充字符(仅当指定了最小字段宽度时有效)。#include <stdio.h> int main() { int num = 123; double pi = 3.14159; char ch = 'A'; char str[] = "Hello, World!"; printf("十进制整数:%d\n", num); printf("双精度浮点数(保留两位小数):%.2lf\n", pi); printf("单个字符:%c\n", ch); printf("字符串:%s\n", str); printf("左对齐的整数,宽度为10:%-10d\n", num); printf("十六进制整数(大写):%X\n", num); printf("使用0填充的八进制整数,宽度为5:%05o\n", num); return 0; }
第五节:运算符与表达式
-
一.算术表达式与赋值表达式
C语言中,算术表达式通过算术运算符将运算量(也称操作数)连接起来,这些运算符包括:
-
基本算数运算符
#include <stdio.h>
int main() {
// 定义两个整数变量
int intA = 10, intB = 3;
// 使用加法运算符
int sum = intA + intB;
printf("加法结果: %d + %d = %d\n", intA, intB, sum);
// 使用减法运算符
int difference = intA - intB;
printf("减法结果: %d - %d = %d\n", intA, intB, difference);
// 使用乘法运算符
int product = intA * intB;
printf("乘法结果: %d * %d = %d\n", intA, intB, product);
// 使用除法运算符(整数除法)
int quotient = intA / intB;
printf("整数除法结果: %d / %d = %d\n", intA, intB, quotient);
// 使用取模运算符
int remainder = intA % intB;
printf("取模结果: %d %% %d = %d\n", intA, intB, remainder);
// 演示浮点数除法(至少一个操作数为浮点数)
double doubleA = 9.0, doubleB = 2.0;
double floatQuotient = doubleA / doubleB;
printf("浮点数除法结果: %.2f / %.2f = %.2f\n", doubleA, doubleB, floatQuotient);
return 0;
}
-
自增自减运算符
-
运算符的优先级
算术运算符的优先级从高到低依次为:()(圆括号)、*、/、%、+、-。当优先级相同时,运算符的结合性决定运算顺序。C语言中的算术运算符(除了单目运算符和赋值运算符外)都是从左向右结合的,即左结合性。
#include <stdio.h>
int main() {
// 定义变量
int a = 5, b = 10;
// 演示自增运算符(前缀和后缀)
printf("a原始值: %d\n", a);
a++; // 后缀自增,先返回当前值,再增加
printf("a后缀自增后: %d\n", a);
++b; // 前缀自增,先增加,再返回新值
printf("b前缀自增后: %d\n", b);
// 演示自减运算符(前缀和后缀)
int c = 10;
printf("c原始值: %d\n", c);
c--; // 后缀自减,先返回当前值,再减少
printf("c后缀自减后: %d\n", c);
--c; // 前缀自减,先减少,再返回新值
printf("c前缀自减后: %d\n", c);
// 注意:自增和自减运算符也可以用于浮点数,但通常用于整数以表示计数的变化
return 0;
}
-
算术表达式
用算数运算符或小括号将运算对象连接起来,符合c语言语法规则的式子称为c语言的算数表达式。运算对象包括常量,变量,函数。表达式在使用的过程中要注意书写形式。
eg:a+b ,(a+b-c)%2
-
二.赋值运算符与赋值表达式
-
简单赋值
赋值符号“=”就是赋值运算符,他的作用是将一个数据赋值给一个变量。
如x=5,是把常数5赋值给变量x。
int x; x = 5; // 赋值运算符将常数5赋值给变量x
由赋值运算符将一个变量和一个表达式连接起来的式子称为“赋值表达式”,他的形式一般为<变量>=<表达式>。
Eg:y=3+7
int y; y = 3 + 7; // 赋值表达式,3+7的结果赋值给变量y
赋值表达式加了;就是一个赋值语句。
赋值语句允许给多个变量连续赋值,但是定义变量时,不允许连续给多个变量赋初始值。
A=b=c=1;合法。Int A=b=c=1不合法。
int a, b, c; a = b = c = 1; // 链式赋值,但注意这里c已经被初始化为1(假设在声明时未显式初始化),然后b被赋值为c的值(即1),最后a也被赋值为b的值(即1)。 // 注意:虽然这种写法在技术上可行,但在复杂代码中可能降低可读性,应谨慎使用。 // int A = b = c = 1; // 这在C语言中是不合法的,因为不能在没有先声明和初始化b和c的情况下就使用它们来初始化A。 // 正确的做法应该分别声明和初始化: int A = 1, b = 1, c = 1; // 分别给A、b、c赋初始值1 int c = 1; int b = c; int A = b; // 现在A、b、c都被赋值为1
-
复合赋值
在赋值运算符=之前加上其他运算符,就构成了符合赋值运算符:
A+=b;
注意:
赋值运算符的左边必须是变量名,被赋值变量的值就是赋值表达式的值,但赋值表达式左边的变量与右边的表达式的数据类型不同时,需要进行数据类型转换。转换的原则是:右边表达式的值被转换成左边变量的数据类型,然后再赋值给变量。
-
三.关系运算符与关系表达式
-
关系运算符
关系运算符用于比较两个值的大小或是否相等,并返回一个布尔值(在C语言中,实际返回的是整型值,0表示假,非0表示真)。
c语言的数据类型虽然没有明确的布尔类型,但是布尔值本质上就是表示真(true)或假(false)的逻辑值,而在C语言中,我们可以约定用整数
1
来表示真(true),用整数0
来表示假(false)。#include<stdio.h> int main() { int a=1; int b=2; int c=3; printf("%d", a==b);//输出结果为0(假),因为1不等于2 printf("%d",a!=b);//输出结果为1(真) printf("%d",a<b);//输出结果为1(真) return 0; }
-
关系表达式
由关系运算符连接起来的表达式,其结果是一个布尔值(在C语言中为整型)。例如,a > b 是一个关系表达式,如果a大于b,则表达式的结果为真(非0),否则为假(0)。
在C语言中,关系运算符的优先级是确定表达式中不同部分执行顺序的重要因素。关系运算符主要用于比较两个操作数的大小或是否相等,并返回一个布尔值(在C语言中,实际上是整型值,0代表假,非0代表真)。关于关系运算符的优先级,可以总结如下:
关系运算符的优先级
关系运算符的优先级相对较低,但高于赋值运算符。在C语言的运算符优先级体系中,关系运算符通常位于算术运算符和逻辑运算符之间。具体来说,关系运算符的优先级如下(从高到低):
- 大于(>)
- 大于等于(>=)
- 小于(<)
- 小于等于(<=)
- 等于(==)
- 不等于(!=)
优先级说明
- 高优先级:
<
、<=
、>
、>=
这些运算符用于比较两个操作数的大小关系,它们的优先级高于==
和!=
。- 低优先级:
==
和!=
用于比较两个操作数是否相等或不等,它们的优先级相对较低。
-
四.逻辑运算符与逻辑表达式
逻辑运算符用于组合或修改布尔值(或可以解释为布尔值的表达式)。它们允许我们根据多个条件来构造更复杂的逻辑判断。
#include<stdio.h>
int main()
{
int a=1;
int b=2;
int c=3;
printf("%d",(a>b)&&(b<c));//输出结果为0因为需要同时满足(a>b)和(b<c)才为真
printf("%d",(a>b)||(b<c));//输出结果为1,因为两个条件只需要满足一个即可
return 0;
}
在C语言中,逻辑非操作符是 !
(也称为取反操作符或否定操作符)。它用于对其后的表达式的结果进行逻辑取反。如果表达式的结果为真(即非零值),逻辑非操作符会将其转换为假(即0);如果表达式的结果为假(即0),逻辑非操作符会将其转换为真(即1)。
#include <stdio.h>
int main() {
int a = 0;
int b = 5;
// 示例1:对整数进行逻辑非操作
if (!a) {
printf("a 是假(0),取反后为真。\n");
} else {
printf("a 是真(非0),但在这个例子中不会执行到这里。\n");
}
// 示例2:对比较表达式的结果进行逻辑非操作
if (!(a == b)) {
printf("a 不等于 b,取反前的比较结果为真,取反后为假,但在这个上下文中,条件为真,因此执行。\n");
} else {
printf("a 等于 b,但在这个例子中不会执行到这里。\n");
}
// 示例3:使用逻辑非操作符简化条件判断
int c = 0;
if (!c) { // 等同于 if (c == 0)
printf("c 是假(0)。\n");
}
return 0;
}
这些运算符和表达式是编写条件语句(如if语句、while循环等)时的基础,它们使得程序能够根据不同的条件执行不同的代码块。
补充:在C语言中,逻辑运算符的优先级决定了在复杂表达式中各个逻辑运算执行的先后顺序。逻辑运算符的优先级由高到低依次是:
逻辑非(!):这是最高优先级的逻辑运算符,用于对其后的表达式的结果进行逻辑取反。
逻辑与(&&):优先级次于逻辑非,用于连接两个表达式,仅当两个表达式的结果都为真时,整个表达式的结果才为真。
逻辑或(||):优先级最低,用于连接两个表达式,只要两个表达式中有一个的结果为真,整个表达式的结果就为真。
-
五.条件运算符与条件表达式
-
条件运算符
条件运算符是由“?”和“:”组成,用于连接3个运算对象的运算符,是c语言的唯一三目运算符。条件运算符的优先级高于赋值运算符和逗号运算符,而低于其他运算符。其结合性为自右向左。
-
条件表达式
用条件运算符将运算对象连接起来的式子称为条件表达式。一般格式为
条件表达式 ? 表达式1 : 表达式2;
- 如果
条件表达式
的结果为真(非零),则整个条件运算符的结果是表达式1
的结果。- 如果
条件表达式
的结果为假(零),则整个条件运算符的结果是表达式2
的结果。
例如:max = (a > b) ? a : b;
这是用来求a,b中的最大值,如果a>b为真则max的最大值为a,否则为b
#include <stdio.h> int main() { int a = 10; int b = 20; int max; // 使用条件运算符找出a和b中的较大值 max = (a > b) ? a : b; //因为a为10,b为20,a小于b,所以(a > b) 为假,最后的值为b的值20 printf("较大的数是: %d\n", max); // 另一个例子,演示条件运算符的优先级 int result = 10; result = (result > 5) ? result * 2 : result + 1; // 注意这里的运算优先级 //因为result > 5为真,所以最后的值为20 printf("result的值是: %d\n", result); return 0; }
-
六.逗号运算符和逗号表达式
-
逗号运算符
c语言中提供了一种特殊的运算符---逗号运算符(又称为顺序求值运算符),用于将两个表达式连接起来,他是所有运算符中优先级最低的。
eg:3+5,6+8
-
逗号表达式
用逗号运算符将各种类型的表达式连接起来的式子称为“逗号表达式”,逗号表达式的一般形式为:
表达式1,表达式2,表达式3
计算过程是依次计算,并且将最后一个表达式的值作为整个式子的值。
#include <stdio.h> int main() { int a = 1, b = 2, c; // 使用逗号表达式,先计算a++的值为2,计算b = a * 2的值为4,a+b的值为6 所以最后c的值为6 c = (a++, b = a * 2, a + b); // 输出结果 printf("a = %d, b = %d, c = %d\n", a, b, c); return 0; }
-
七.其他运算符
-
sizeof运算符
Sizeof为求字节数量运算符,是c语言中唯一的关键字运算符,可以计算某一种类型的数据所占储存单元的字节个数
#include <stdio.h>
int main() {
// 计算基本数据类型的字节数
printf("char 的大小: %zu 字节\n", sizeof(char));
printf("int 的大小: %zu 字节\n", sizeof(int));
printf("float 的大小: %zu 字节\n", sizeof(float));
printf("double 的大小: %zu 字节\n", sizeof(double));
// 计算数组的大小
int arr[10];
printf("arr 数组的大小: %zu 字节\n", sizeof(arr));
printf("arr 数组中单个元素的大小: %zu 字节\n", sizeof(arr[0]));
return 0;
}
-
小括号运算符
优先级高的运算符,有小括号就先盖括号里的.。
第六节:数据类型转换
一.自动数据类型转换(隐式转换)
自动数据类型转换是在不需要程序员显式指定的情况下,由编译器自动进行的类型转换。这种转换通常发生在以下几种情况:
1.赋值时的类型转换:
当将一种数据类型的值赋给另一种数据类型的变量时,如果这两种类型兼容且目标类型能够表示源类型的值,则会发生自动类型转换。例如,将int类型的值赋给double类型的变量时,int类型的值会自动转换为double类型。
#include <stdio.h>
int main() {
int a = 5;
double b;
// 将int类型的值赋给double类型的变量
b = a;
printf("a = %d, b = %f\n", a, b);
// 输出:a = 5, b = 5.000000
// 这里,int类型的a的值被自动转换成了double类型的值赋给了b
return 0;
}
2.算术运算中的类型转换:
在进行算术运算时,如果操作数的类型不同,编译器会先将它们转换为同一类型,然后再进行运算。转换的规则通常是将较低精度的类型转换为较高精度的类型,如int、unsigned、long、float、double等类型之间的转换顺序是递增的。
#include <stdio.h>
int main() {
int x = 5;
float y = 2.5;
// int和float类型进行算术运算
double z = x + y;
// 注意:这里虽然z是double类型,但x和y在运算前会被隐式转换为double类型
printf("z = %f\n", z);
// 输出:z = 7.500000
// x被转换为5.0(double类型),然后与y相加,结果也是double类型
return 0;
}
3.整形提升:
在进行整形运算时,如果操作数的类型是char或short,则它们会被提升为int类型(如果int能够表示它们的值)或unsigned int类型(如果char或short是无符号的且unsigned int能够表示它们的值)后再进行运算。这是为了利用CPU的整型运算器的最大效率。
#include <stdio.h>
int main() {
char c = 'A'; // ASCII码为65
short s = 10000;
// 整形提升示例:char与int运算
int ic = c + 10; // c被提升为int类型
// 另一个示例:short与int运算
int is = s + 1; // s被提升为int类型
printf("ic = %d, is = %d\n", ic, is);
// 输出:ic = 75, is = 10001
// 'A'的ASCII码是65,加10后为75;short类型的s被提升为int类型后再加1
return 0;
}
二.强制数据类型转换(显式转换)
强制数据类型转换是程序员通过特定的语法显式地将一种数据类型的值转换为另一种数据类型的值。这种转换通常用于需要改变数据类型但又没有自动转换发生的场合。
- 强制类型转换的语法:
强制类型转换的语法格式为(类型名)(表达式),其中类型名是目标数据类型的名称,表达式是需要被转换的值的表达式。
示例:
将int类型的变量a转换为float类型:float f = (float)a;
将float类型的表达式x + y的结果转换为int类型:int result = (int)(x + y);
- 注意事项:
强制类型转换可能会导致数据精度的丢失或溢出。例如,将double类型的大数值转换为int类型时,小数部分会被截断。
强制类型转换的优先级高于算术运算符的优先级,因此在涉及复杂表达式时,需要使用括号明确指定转换的范围。
强制类型转换只影响转换操作本身,不会改变原变量的数据类型。
综上所述,C语言中的数据类型转换是编程中常见的操作,它允许程序员灵活地处理不同类型的数据。通过掌握自动数据类型转换和强制数据类型转换的规则和用法,可以有效地提高程序的效率和可读性。
第七节:顺序结构程序设计
在C语言中,顺序结构是最基本的程序结构之一,也是最简单的结构。顺序结构指的是程序按照代码的顺序,从上到下依次执行,直到遇到跳转语句(如if、for、while等)或程序结束为止。顺序结构是任何复杂程序的基础,因为它代表了程序执行的最直接、最自然的方式。
-
一.顺序结构的特点
-
顺序性
代码按照书写的顺序执行,每条语句的执行都依赖于前一条语句的执行结果(如果有的话)。
-
简单性
结构清晰,易于理解和实现。
-
基础性
任何复杂的程序结构(如选择结构、循环结构)都建立在顺序结构的基础上。
-
二.顺序结构的基本组成
顺序结构主要由以下几部分组成:
声明语句:用于声明程序中使用的变量、常量、类型等。
赋值语句:用于给变量赋值。
输入语句:如scanf函数,用于从用户或其他输入源接收数据。
处理语句:进行各种计算、处理数据等操作的语句。
输出语句:如printf函数,用于将结果输出到屏幕或其他输出设备。
控制语句(在顺序结构中主要是顺序执行,但在整个程序中可能包含):用于 控制程序的流程,如条件判断(if)、循环(for、while、do-while)等。
下面是一些具体的例子:
1.简单的顺序结构
#include <stdio.h> int main() { int age; // 声明语句 age = 25; // 赋值语句 printf("我的年龄是:%d岁\n", age); // 输出语句 return 0; }
2. 包含输入的顺序结构
#include <stdio.h> int main() { int number; printf("请输入一个整数:"); scanf("%d", &number); // 输入语句 printf("您输入的整数是:%d\n", number); // 输出语句 return 0; }
3.包含处理和输出的顺序结构
#include <stdio.h> int main() { int a = 5, b = 10; // 声明并赋值 int sum; sum = a + b; // 处理语句 printf("a和b的和是:%d\n", sum); // 输出语句 return 0; }
4. 顺序结构中的控制语句(这里主要是还顺序执行)
#include <stdio.h> int main() { int score = 85; if (score >= 60) { // 控制语句(条件判断) printf("恭喜,您及格了!\n"); } // 注意:此处的if语句虽然是一个控制语句,但在本例中它是顺序执行的一部分, // 因为没有else分支或更复杂的跳转来中断顺序执行流。 return 0; }
第八节:选择(分支)结构程序设计
在C语言(以及大多数编程语言中),选择结构是程序设计中不可或缺的一部分。它允许程序根据不同的条件执行不同的代码块。这种能力对于创建交互式程序、处理复杂逻辑和进行决策制定至关重要。下面将详细介绍C语言中的两种主要选择结构:if语句和switch语句。
-
一.选择结构的编程思想
选择结构的核心思想是“如果满足某个条件,则执行一段代码;否则,执行另一段代码(或什么也不做)”。这种逻辑使得程序能够根据输入、环境状态或其他因素做出决策。
-
二.if语句
if语句是C语言中最基本的选择结构。它允许程序测试一个条件,并根据该条件的真假执行不同的代码块。
-
单分支(if语句)
执行流程: 先计算表达式的值,如果表达式的值为真(T),则执行其后的语句;如果表达式的值为假(F)则不执行该语句。其中,表达式必须是关系表达式或逻辑表达式,语句可以为简单语句或复合语句。
语法格式:
if (表达式)
语句;
#include <stdio.h> int main() { int number = 10; if (number > 5) { printf("数字大于5。\n"); } // 如果number的值大于5,则输出:数字大于5。 return 0; }
-
双分支(if ...else语句)
执行流程:先计算表达式的值,如果表达式的值为非0(即为真),则执行语句1,否则执行语句2。总之,该格式中的“语句1”和“语句2”总会有一个得到执行。
语法格式:
if (表达式)
语句1;
else
语句2;
#include <stdio.h> int main() { int score = 75; if (score >= 60) { printf("恭喜你,及格了!\n"); } else { printf("很遗憾,你没有及格。\n"); } // 根据score的值,输出对应的消息。 如果成绩大于60则输出及格,否则不及格 return 0; }
-
多分支(if... else...else)
#include <stdio.h>
int main() {
int grade = 85;
if (grade >= 90) {
printf("优秀\n");
} else if (grade >= 80) {
printf("良好\n");
} else if (grade >= 60) {
printf("及格\n");
} else {
printf("不及格\n");
}
// 根据grade的值,输出对应的成绩等级。这里设定了多个条件(分支)
return 0;
}
-
嵌套语句
语法格式:
if (表达式1)
if (表达式2)
语句1;
else语句2;
else
语句3;
#include <stdio.h> int main() { int a = 10, b = 20; if (a > 5) { if (b > 15) { printf("a大于5且b大于15。\n"); } else { printf("a大于5但b不大于15。\n"); } } else { printf("a不大于5。\n"); } // 根据a和b的值,输出对应的条件判断结果。 先判断a是否大于5如果大于再对b进行是否大于15的判断,否则输出a不大于5 return 0; }
-
三.switch语句
switch语句是另一种选择结构,它允许一个变量(或表达式)被检查并与多个case标签进行比较。如果找到匹配,则执行相应的代码块。
注意事项:
每个case块的末尾通常有一个break语句,用于跳出switch结构。如果没有break,程序会继续执行下一个case的代码,这称为“穿透”。
default块是可选的,它提供了当没有任何case匹配时的默认行为。
例子1:基本switch语句:
#include <stdio.h> int main() { int day = 3; // 假设是周三 switch (day) { case 1: printf("星期一\n"); break; case 2: printf("星期二\n"); break; case 3: printf("星期三\n"); break; case 4: printf("星期四\n"); break; case 5: printf("星期五\n"); break; case 6: printf("星期六\n"); break; case 7: printf("星期日\n"); break; default: printf("无效的天数\n"); } return 0; }
例子 2: 穿透(没有break语句)
#include <stdio.h> int main() { int score = 85; switch (score / 10) { // 使用整除来获取十分位 case 10: case 9: printf("优秀\n"); // 故意省略break来展示穿透 case 8: printf("良好\n"); break; case 7: printf("中等\n"); break; case 6: printf("及格\n"); break; default: printf("不及格\n"); } return 0; }
在这个例子中,由于
score / 10
的值为8(整数除法),所以首先会打印"优秀"(尽管逻辑上这可能不准确,但这里用于演示穿透),因为没有break
,所以会继续执行并打印"良好"。例子 3: 使用default块
#include <stdio.h> int main() { char grade = 'F'; // 假设是一个成绩等级 switch (grade) { case 'A': printf("优秀\n"); break; case 'B': case 'C': printf("及格\n"); break; case 'D': printf("勉强及格\n"); break; default: printf("不及格\n"); } // 因为grade是'F',所以会执行default块 return 0; }
在这个例子中,
grade
变量的值为'F',它不匹配任何case
标签,因此会执行default
块,打印"不及格"
第九节:循环结构程序设计
循环结构是程序设计中的基本结构之一,它允许程序重复执行某段代码,直到满足特定条件为止。循环结构极大地提高了代码的重用性和程序的效率,特别是在处理大量重复任务时。
-
一.循环结构的编程思想
循环结构的编程思想基于“重复执行”的原则,即当某个条件成立时,反复执行一段代码。这种思想在处理数组遍历、重复计算、数据输入输出等场景中尤为常见。
-
二.实现循环的三种语句
-
while 循环
只要条件表达式的结果为真(非0),就重复执行循环体中的语句。当条件表达式的结果为假(0)时,退出循环
基本框架:
表达式1;
while(表达式2)
{
语句;
表达式3;
}
while后面的语句称为循环体;循环体执行的次数由循环条件表达式(表达式2)来确定;
当循环条件表达式为真值时就执行循环体;
while(1)//是一个死循环
相当于:如果满足表达式2,就会一直执行语句;表达式3.直到不满足条件。
例子:
1.计算1-10的和
#include <stdio.h> //计算从1-10的和 int main() { int i = 1; int sum = 0; while (i <= 10) { sum += i; i++; } printf("1到10的和是: %d\n", sum); return 0; } //在这个例子中,while循环会一直执行,直到变量i的值大于10。 //在每次循环中,i的值会被加到sum上,然后i的值增加1。最终,sum包含了从1到10的所有整数的和。
2.死循环:
#include <stdio.h> int main() { while (1) { printf("这是一个死循环,按Ctrl+C退出。\n"); // 注意:在实际应用中,死循环通常不是好的编程实践,除非有特定的用途(如等待事件)。 } return 0; // 这行代码实际上永远不会被执行。 }
这个例子展示了如何使用
while(1)
来创建一个死循环,它会无限次地执行循环体内的代码,直到被外部因素(如用户中断)终止。
-
do while 循环
do-while循环与while循环相似,但do-while循环至少执行一次循环体,因为循环体的执行是在检查条件之前进行的。其一般形式为:
表达式1;
do
{
语句;
}
while(表达式2);
先执行一次指定的循环体语句,再判别表达式是否为真,若为真返回重新执行循环体语句。
与while循环的区别:
while循环先判断再执行;
do…while循环先执行再判断;
只要第一次进入循环的条件就不满足,那么这两种循环的结果可能不同;
例子:
1.在这个例子中,
do-while
循环至少会执行一次,因为循环体的执行是在检查条件之前进行的。即使i
的初始值不满足循环条件(即i < 5
),循环体也会执行一次。然后,条件被检查,如果为真,则循环继续;如果为假,则循环结束。因此,这个循环会打印出i
的值从0到4。#include <stdio.h> int main() { int i = 0; do { printf("i的值是: %d\n", i); i++; } while (i < 5); return 0; }
2.在这个例子中,尽管初始时
i
的值不满足循环条件(i < 5
为假),但do-while
循环体仍然会执行一次,打印出i
的初始值5。然后,由于条件不满足,循环结束,并打印出“循环结束。”。这展示了do-while
循环与while
循环在初始条件不满足时的行为差异。#include <stdio.h> int main() { int i = 5; do { printf("i的值是: %d\n", i); i++; } while (i < 5); printf("循环结束。\n"); return 0; }
-
for 循环
for循环是一种更灵活的循环结构,它可以在一个语句中初始化循环变量、设置循环条件、以及更新循环变量。其一般形式为:
初始化表达式在循环开始前执行一次,然后检查条件表达式,如果为真,则执行循环体,之后执行更新表达式,并再次检查条件表达式,重复此过程直到条件表达式为假。
#include <stdio.h>
int main() {
int sum = 0;
for (int i = 1; i <= 10; i++) {
sum += i;
}
printf("1到10的和是: %d\n", sum);
return 0;
}
在这个例子中,for
循环的初始化表达式是int i = 1;
,条件表达式是i <= 10;
,更新表达式是i++
。循环体是sum += i;
,它计算了从1到10的整数和。
-
三.与循环有关的控制语句
-
break 语句
用于完全终止循环的执行,无论是哪种循环。break语句后面的语句(即使是循环体中的)都不会被执行。
例子 1: 在while循环中使用break语句
#include <stdio.h> int main() { int i = 0; while (1) { // 创建一个无限循环 i++; printf("i的值是: %d\n", i); if (i == 5) { break; // 当i等于5时,终止循环 } } printf("循环结束。\n"); return 0; }
在这个例子中,
while
循环本来是一个无限循环,但由于break
语句的存在,当i
的值等于5时,循环被终止,并打印出“循环结束。”。例子 2: 在for循环中使用break语句
#include <stdio.h> int main() { for (int i = 0; i < 10; i++) { if (i == 5) { break; // 当i等于5时,终止循环 } printf("i的值是: %d\n", i); } printf("循环结束。\n"); return 0; }
在这个例子中,
for
循环在i
小于10的条件下执行。然而,当i
等于5时,break
语句导致循环被提前终止
-
continue 语句
用于跳过循环体中剩余的代码,并立即开始下一次循环的迭代。如果循环中没有任何剩余的迭代(例如在for循环中已执行了更新表达式),则继续执行循环之后的代码。
例子 1: 在while循环中使用continue语句
#include <stdio.h> int main() { int i = 0; while (i < 10) { i++; if (i % 2 == 0) { continue; // 如果i是偶数,跳过当前循环的剩余部分 } printf("i的值是奇数: %d\n", i); } return 0; }
在这个例子中,
while
循环遍历从0到9的整数。当i
是偶数时,continue
语句导致循环体中的剩余部分(即打印语句)被跳过,直接进行下一次迭代。因此,只有奇数会被打印出来。例子 2: 在for循环中使用continue语句
#include <stdio.h> int main() { for (int i = 0; i < 10; i++) { if (i % 3 == 0) { continue; // 如果i是3的倍数,跳过当前循环的剩余部分 } printf("i的值不是3的倍数: %d\n", i); } return 0; }
在这个例子中,
for
循环遍历从0到9的整数。如果i
是3的倍数,则continue
语句导致循环体中的打印语句被跳过,不会执行。因此,只有不是3的倍数的整数会被打印出来。
-
四.循环的嵌套
循环的嵌套指的是在一个循环体内部再定义一个或多个循环。嵌套循环可以处理更复杂的数据结构,如二维数组。嵌套循环的层数理论上没有限制,但实际应用中应注意避免过深的嵌套,以免影响代码的可读性和维护性。
使用嵌套循环来打印一个图案,比如一个星号(*)组成的正方形。
#include <stdio.h> int main() { int n = 5; // 假设我们要打印一个5x5的正方形 // 外层循环控制行数 for (int i = 0; i < n; i++) { // 内层循环控制每行的星号数量 // 注意:为了打印正方形,每行的星号数量应该与行数相同 for (int j = 0; j < n; j++) { printf("* "); // 打印一个星号后跟一个空格 } // 每打印完一行后换行 printf("\n"); } return 0; }
在这个例子中,我们定义了一个变量
n
来表示正方形的边长(也就是我们要打印的行数和每行的星号数量)。外层循环负责控制行数,而内层循环则负责在每行中打印相应数量的星号。通过嵌套循环,我们能够按照预定的模式(在这个例子中是正方形)来打印字符。
第十节:数组
-
一.什么是数组
在计算机科学中,数组是一种基础且强大的数据结构,用于在计算机内存中连续存储相同类型的数据。数组中的每个元素可以通过索引(或下标)来访问,索引通常是从0开始的。数组使得数据管理和操作变得高效和直接。
在C语言的世界里,数组就像是一个装满同类型小盒子的大箱子,每个小盒子都有一个独特的编号(称为索引或下标),通过这个编号,我们可以准确地找到并操作箱子里的每一个小盒子。这个“大箱子”就是数组,而里面的小盒子则代表数组中的元素。
-
二.为什么要有数组
想象一下,如果你需要存储100个学生的分数,你会怎么做?如果没有数组,你可能需要定义100个单独的变量来分别存储每个学生的分数,这样不仅代码冗长难读,而且管理起来也非常不便。但有了数组,你就可以用一个简单的名字(比如scores)来引用这个“装满分数的大箱子”,并通过索引来访问或修改任意一个学生的分数,大大简化了代码的复杂度和提高了效率。
没有数组的情况
如果没有数组,我们确实需要定义100个单独的变量来存储每个学生的分数。假设学生的编号从1到100,我们可能会写出类似以下的代码(这里只展示部分以简化):
int score1 = 85; int score2 = 92; int score3 = 78; // ... 以此类推,直到 score100 // 假设我们需要访问第50个学生的分数 // 我们必须直接引用 score50 这个变量 printf("第50个学生的分数是: %d\n", score50); // 如果需要修改这个分数,也需要直接操作 score50 score50 = 88;
如上所见,这种方法非常不灵活且难以维护,特别是当需要处理的元素数量增加时。
使用数组的情况
有了数组,我们可以大大简化上述过程。我们定义一个名为
scores
的数组,用于存储100个学生的分数,然后通过索引来访问或修改这些分数。以下是如何实现的示例:#include <stdio.h> int main() { // 定义一个可以存储100个整数的数组 int scores[100]; // 假设我们初始化一些分数(这里只展示部分) scores[0] = 85; scores[1] = 92; scores[2] = 78; // ... 以此类推,直到 scores[99] // 访问第50个学生的分数 printf("第50个学生的分数是: %d\n", scores[49]); // 注意数组索引从0开始 // 修改第50个学生的分数 scores[49] = 88; return 0; }
-
三.一维数组
-
定义
一维数组是最简单的数组形式,它像是一条直线,其中的每个元素都可以通过唯一的索引来访问。一维数组是后面只有一个下标的数组。定义方式:
类型说明符 数组名[常量表达式] eg:int a[10]
上面定义了一个一维数组,其中a为数组名,此数组有10个元素,每个元素的类型都是整形。
#include <stdio.h> int main() { int scores[10]; // 定义了一个整型一维数组scores,它有10个元素 return 0; }
-
一维数组的引用
数组要先定义后使用,只能逐个引用数组元素,而不能一次引用整个数组
一维数组的引用格式为:数组名[下标]
如果数组长度为len,数组下标的取值范围为0~len-1,其中0为下线,len-1为上限。
#include <stdio.h> int main() { int scores[10] = {0}; // 初始化所有元素为0 scores[0] = 90; // 引用数组的第一个元素并赋值 printf("第一个学生的分数是: %d\n", scores[0]); // 访问并打印第一个元素 return 0; }
-
一维数组的初始化
一维数组的初始化就是给一维数组赋初值,其形式为:
数据类型 数组名[数组长度]={值1,值2,...,值5}
完全赋值int a[5]={1,2,3,4,5}
#include <stdio.h> int main() { int scores[5] = {1, 2, 3, 4, 5}; // 初始化5个元素 return 0; }
部分赋值int a[5]={1,2,3}
#include <stdio.h> int main() { int scores[5] = {1, 2, 3}; // 初始化前三个元素,其余自动初始化为0 return 0; }
不指定长度赋值int a[]={1,2,3,4,5}
#include <stdio.h> int main() { int scores[] = {1, 2, 3, 4, 5}; // 编译器自动根据元素数量确定数组长度 return 0; }
不赋初值 int a[]
#include <stdio.h> int main() { int scores[5]; // 数组元素未被初始化,其值是不确定的 return 0; }
操作:可以遍历数组以读取或修改其元素,使用循环结构(如for循环)是常见的做法。
#include <stdio.h> int main() { int scores[5] = {1, 2, 3, 4, 5}; for(int i = 0; i < 5; i++) { printf("第%d个学生的分数是: %d\n", i+1, scores[i]); } return 0; }
应用:用于存储一系列相关的数据,如学生的成绩、一系列温度读数等。
-
四.二维数组
定义:二维数组可以看作是一个表格,由行和列组成,每个元素都通过两个索引(行索引和列索引)来访问。
#include <stdio.h>
int main() {
int matrix[2][3]; // 定义了一个2行3列的二维数组
return 0;
}
操作:使用嵌套循环遍历二维数组,外层循环遍历行,内层循环遍历列。
操作(遍历):
#include <stdio.h> int main() { int matrix[2][3] = {{1, 2, 3}, {4, 5, 6}}; for(int i = 0; i < 2; i++) { // 外层循环遍历行 for(int j = 0; j < 3; j++) { // 内层循环遍历列 printf("matrix[%d][%d] = %d\n", i, j, matrix[i][j]); } } return 0; }
应用:用于表示矩阵、图像数据、棋盘游戏棋盘等。
按行分段赋值:int a[2][3]={{1,2,3},{4,5,6}};
#include <stdio.h> int main() { int matrix[2][3] = {{1, 2, 3}, {4, 5, 6}}; // 初始化二维数组 return 0; }
连续赋值:int a[2][3]={1,2,3,4,5,6};
#include <stdio.h> int main() { int matrix[2][3] = {1, 2, 3, 4, 5, 6}; // 初始化二维数组,按行连续赋值 return 0; }
部分赋值:int a[2][3]={{1},{2},{3}};
#include <stdio.h> int main() { int matrix[2][3] = {{1}, {2}}; // 只初始化了第一行的第一个元素和第二行的第一个元素,其余自动初始化为0 return 0; }
省略维数赋值 int a[][3]={1,2,3,4,5,6,7,8,9};
#include <stdio.h> int main() { int matrix[][3] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; // 第一维大小由初始化列表确定,为3 return 0; }
-
五.字符数组与字符串
字符数组:字符数组用于存储一系列字符。与一维数组类似,但存储的元素是字符类型(如char)。(本质上就是储存的数据类型从数字变成了字符)
定义:
#include <stdio.h> int main() { char greeting[10]; // 定义一个字符数组,用于存储最多9个字符的字符串(第10个位置留给'\0') return 0; }
初始化:
#include <stdio.h> int main() { char greeting[10] = "Hello"; // 初始化字符数组,自动在末尾添加'\0' // 实际上,"Hello" 后面的 '\0' 也会存储在 greeting 数组的第六个位置 return 0; }
引用:
#include <stdio.h> int main() { char greeting[10] = "Hello"; printf("%c\n", greeting[0]); // 引用并打印第一个字符 'H' return 0; }
字符串:在C语言中,字符串是以空字符(\0)结尾的字符数组。这意味着字符串的最后一个字符是空字符,用于标识字符串的结束。
字符串数组的定义,引用,初始化的方式与一维数组和二维数组基本相同,唯一不同的是,字符数组的数组元素类型为字符型。
定义和初始化:
#include <stdio.h> int main() { char str[] = "Hello, World!"; // 定义并初始化一个字符串,编译器自动添加'\0' return 0; }
引用
字符串通常通过指针或数组名来引用整个字符串,但也可以像字符数组一样逐个访问字符。
#include <stdio.h> int main() { char str[] = "Hello, World!"; printf("整个字符串: %s\n", str); // 引用整个字符串 printf("第一个字符: %c\n", str[0]); // 引用第一个字符 return 0; }
字符串输出输入函数(puts,gets):
puts
函数用于向标准输出设备(通常是屏幕)输出一个字符串,并在字符串末尾自动添加一个换行符(\n
)。其原型定义在<stdio.h>
头文件中。#include <stdio.h> int main() { char str[] = "Hello, World!"; puts(str); // 输出: Hello, World! // 注意:puts 自动在字符串末尾添加换行符 return 0; }
gets
函数用于从标准输入设备(通常是键盘)读取一行文本,直到遇到换行符(\n
),但它不会将换行符存储在字符串中,而是将其替换为字符串的终止符(\0
)。由于它不会检查目标缓冲区的大小,因此很容易发生缓冲区溢出,导致安全问题(推荐使用fgets函数)。#include <stdio.h> int main() { char str[100]; printf("Enter a string: "); if (fgets(str, sizeof(str), stdin) != NULL) { // fgets 会将换行符(如果有的话)也读入到字符串中 // 可以通过替换掉换行符来避免在输出时显示它 str[strcspn(str, "\n")] = 0; // 移除换行符 puts(str); } return 0; }
字符串处理函数:
操作:可以使用字符串处理函数(如strlen、strcpy、strcat等)来操作字符串。
#include <stdio.h> #include <string.h> // 引入字符串处理函数库 int main() { char str1[] = "Hello, "; char str2[] = "World!"; char combined[50]; // 分配足够的空间以存储两个字符串和结束符'\0' // 使用字符串处理函数 strcpy(combined, str1); // 将str1复制到combined strcat(combined, str2); // 将str2追加到combined printf("Combined string: %s\n", combined); // 打印合并后的字符串 // 使用strlen获取字符串长度 printf("Length of combined string: %zu\n", strlen(combined)); return 0; }
在这个例子中,
strcpy
函数用于复制字符串,strcat
函数用于连接(追加)两个字符串,而strlen
函数则用于获取字符串的长度(不包括结尾的空字符\0
)。
字符串在C语言中广泛用于存储和处理文本数据。例如,它们可以用于:
- 存储用户输入的命令或数据。
- 作为文件名来打开或保存文件。
- 传递消息或错误代码给程序的其他部分。
- 在网络编程中发送和接收数据。
- 存储和显示文本信息,如游戏中的角色对话或状态信息。
第十一节:用户自定义函数
-
一.模块化程序设计的思想
模块化程序设计的核心思想是将复杂的问题分解为多个简单、易于理解和管理的子问题。每个子问题由独立的模块(函数)来实现,这些模块之间通过明确的接口(如函数参数和返回值)进行交互。这样,不仅提高了代码的可读性和可维护性,还促进了代码的重用,减少了冗余。
Eg:一个计算器程序可以分解为加法、减法、乘法和除法等模块,每个模块对应一个函数,函数之间通过传递操作数和接收结果来交互。
-
二.函数的定义和调用
函数定义
在C语言中,函数是一段可以重复使用的代码块,用于完成特定的任务。函数定义包括返回类型、函数名、参数列表(可能为空)和函数体。
函数调用
函数调用是执行函数代码的过程。在调用时,需要指定函数名和传递给函数的参数(如果函数有参数的话)。
函数定义与调用:
#include <stdio.h> // 函数定义 int add(int a, int b) { return a + b; } int main() { // 函数调用 int sum = add(5, 3); printf("Sum: %d\n", sum); return 0; }
-
三.函数间的参数传递
尽管C语言主要通过值传递来传递参数,但指针的引入允许我们实现引用传递的效果,即直接操作函数外部的原始变量。
值传递:
#include <stdio.h> void swap(int a, int b) { int temp = a; a = b; b = temp; // 注意:这里的修改不会影响到外部变量 } int main() { int x = 5, y = 10; swap(x, y); // x 和 y 的值不会改变 printf("x: %d, y: %d\n", x, y); return 0; }
引用传递,通过指针(没学指针呢可以不看):
#include <stdio.h> void swap(int *a, int *b) { int temp = *a; *a = *b; *b = temp; } int main() { int x = 5, y = 10; swap(&x, &y); // 传递的是地址,因此 x 和 y 的值会改变 printf("x: %d, y: %d\n", x, y); // 输出: x: 10, y: 5 return 0; }
-
四.函数的嵌套调用
函数的嵌套调用是指在一个函数内部调用另一个函数。这种调用方式可以进一步增加代码的模块化和重用性。
#include <stdio.h>
int multiply(int a, int b) {
return a * b;
}
int power(int base, int exponent) {
if (exponent == 0) return 1;
return multiply(base, power(base, exponent - 1)); // 嵌套调用
}
int main() {
int result = power(2, 3); // 调用 power 函数,它内部调用了 multiply 函数
printf("2^3 = %d\n", result);
return 0;
}
五.函数的递归调用
递归调用是函数自己调用自己的过程。递归必须有一个明确的终止条件,以避免无限递归导致的栈溢出。
#include <stdio.h>
int factorial(int n) {
if (n == 0) return 1; // 终止条件
return n * factorial(n - 1); // 递归调用
}
int main() {
int result = factorial(5);
printf("5! = %d\n", result);
return 0;
}
六.变量的作用域
变量的作用域指的是变量在程序中可以被访问的区域。在C语言中,变量的作用域由其在代码中的位置决定。了解变量的作用域对于编写清晰、可维护的代码至关重要。
局部变量
定义:在函数内部定义的变量,其作用域仅限于定义它的函数体内部。一旦函数执行完毕,局部变量的值将被销毁,并且无法再被访问(除非函数通过某种方式返回了局部变量的值,但这通常是通过返回值或指针参数实现的,而不是直接访问局部变量本身)。
特点:局部变量只能在定义它的函数内部被访问和修改。
例子:
#include <stdio.h> void printNumber() { int localNum = 10; // 局部变量,仅在printNumber函数内部可访问 printf("Local number: %d\n", localNum); } int main() { // 尝试访问localNum会导致编译错误,因为它在main函数中不可见 // printf("Local number in main: %d\n", localNum); // 错误:'localNum'未定义 printNumber(); // 调用函数,函数内部可以访问localNum return 0; }
全局变量
定义:在函数外部定义的变量,其作用域从定义点开始到文件结束。全局变量可以在程序的所有函数中被访问和修改,只要这些函数在全局变量定义之后被声明。
特点:全局变量虽然提供了跨函数的数据共享能力,但过度使用会导致程序难以理解和维护,因为全局变量的值可以在程序的任何地方被修改。
例子:
#include <stdio.h> int globalNum = 20; // 全局变量,在整个文件范围内可访问 void modifyGlobal() { globalNum = 30; // 修改全局变量的值 } void printGlobal() { printf("Global number: %d\n", globalNum); } int main() { printGlobal(); // 输出:Global number: 20 modifyGlobal(); // 修改全局变量的值 printGlobal(); // 输出:Global number: 30 return 0; }
在这个例子中,
globalNum
是一个全局变量,它在整个文件中都是可见的,包括在main
函数、modifyGlobal
函数和printGlobal
函数中。通过modifyGlobal
函数,我们可以修改全局变量的值,并通过printGlobal
函数验证这个修改。然而,也需要注意到全局变量的使用应当谨慎,以避免引入难以追踪的副作用和降低程序的模块化和可读性。
七.变量的生存期
变量的生存期指的是变量在内存中存在的时间。
自动变量(局部变量)
自动变量,也称为局部变量,其生存期仅限于定义它的函数或代码块内部。当函数被调用时,这些变量被创建在栈上;当函数执行完毕时,它们会被自动销毁。
#include <stdio.h>
void exampleFunction() {
// 这是一个自动变量(局部变量)
int localVar = 10;
printf("在函数内:localVar = %d\n", localVar);
// 当这个函数执行完毕后,localVar 的值将不再存在(被销毁)
}
int main() {
// 这里无法访问 localVar,因为它只在 exampleFunction 中定义
exampleFunction();
// 尝试访问 localVar 会导致编译错误
// printf("在函数外:localVar = %d\n", localVar); // 错误:localVar 未定义
return 0;
}
静态变量
静态变量在程序的整个执行期间都存在,但其作用域仍然受限于定义它的代码块(对于局部变量)或文件(对于全局静态变量)。
静态局部变量示例:
#include <stdio.h>
void exampleFunction() {
// 这是一个静态局部变量
static int staticLocalVar = 0;
staticLocalVar++;
printf("staticLocalVar = %d\n", staticLocalVar);
// 尽管函数被多次调用,staticLocalVar 的值会保持并递增
}
int main() {
exampleFunction(); // 输出:staticLocalVar = 1
exampleFunction(); // 输出:staticLocalVar = 2
return 0;
}
静态全局变量示例:
静态全局变量仅在其定义的文件中可见。
// file1.c
#include <stdio.h>
static int staticGlobalVar = 42; // 仅在 file1.c 中可见
void printStaticGlobal() {
printf("staticGlobalVar = %d\n", staticGlobalVar);
}
// file2.c
// 这里无法访问 staticGlobalVar,因为它是 static 的且定义在 file1.c 中
// main 函数可能在另一个文件中,或者这里为了演示
// int main() { ... }
全局变量
全局变量在程序启动时创建,在程序结束时销毁。它们在程序的任何地方都可以被访问(除非被其他文件通过关键字如 static
限制)。
#include <stdio.h>
// 这是一个全局变量
int globalVar = 20;
void anotherFunction() {
// 这里可以访问 globalVar
printf("在 anotherFunction 中:globalVar = %d\n", globalVar);
}
int main() {
// 在 main 函数中也可以访问 globalVar
printf("在 main 中:globalVar = %d\n", globalVar);
anotherFunction(); // 调用另一个函数,它也能访问 globalVar
return 0;
}
ps:写的比较匆忙,有错误的或者写的不完善,遗漏的请大家指出