初识c语言
1.什么是c语言
1.1为什么会有c语言的存在?
我们知道人和人之间交流可能会采用汉语,英语等等语言。那么我们在和计算机经行交流的时候应该采用什么语言呢?
起初,人们直接使用机器语言与计算机 “对话”,它由 0 和 1 组成的二进制代码构成,是计算机唯一能直接理解的语言。但对人类而言,机器语言晦涩难懂、编写困难,就像面对一本全是乱码的书籍,即便专业的程序员也难以直接用它高效地表达复杂逻辑。
为了改善这种情况,汇编语言应运而生。它用助记符代替部分二进制代码,比如用 “MOV” 表示数据传送,“ADD” 表示加法运算,一定程度上降低了编程难度。然而,汇编语言与特定硬件架构紧密绑定,移植性极差,就如同不同地区的方言,在甲地编写的汇编程序到乙地的计算机上可能完全无法运行,这严重限制了软件开发的效率和规模。
随着计算机应用场景的不断拓展,人们迫切需要一种既能高效操控计算机底层,又具有良好移植性和易用性的语言,C 语言就在这样的时代需求下诞生了。
1.2什么是c语言
C语言是一门通用计算机编程语言,广泛应用于底层开发(但不代表c语言不能用于上层开发)。那么什么是底层开发什么又是上层开发?(这里简单说一下因为这里涉及操作系统这里不过度展开。)
- 底层开发就像搭建房子的地基,主要和计算机硬件打交道,比如开发操作系统内核、硬件驱动程序、嵌入式系统程序。这需要了解硬件原理,用 C 语言等直接控制硬件,让硬件能正常工作,像给显卡写驱动,让电脑能显示画面。
- 上层开发则是在底层基础上盖房子,开发用户直接使用的软件,像手机 APP、网页、办公软件等
C语言的设计目标是提供一种能以简易的方式编译、处理低级存储器、产生少量的机器码以及不需要任何运行环境支持便能运行的编程语言。
C语言是一门面向过程的计算机编程语言,与C++,Java等面向对象的编程语言有所不同。其编译器主要有Clang、GCC、WIN-TC、SUBLIME、MSVC、Turbo C等。
2.第一个c语言程序
#include <stdio.h>
int main()
{
printf("hello world\n");
return 0;
}
相信第一次看见这串代码的时候肯定充满了各种问题?例如什么是main函数,什么是#include <stdio.h>,什么又是printf(“hello world\n”);。这里我会一行一行简单的进行解释。
- #include <stdio.h>
这一行是预处理指令。#include 的意思是 “包含”,它告诉编译器把 <stdio.h> 这个文件的内容添加到当前程序里。<stdio.h> 是一个头文件(“stdio” 是 “Standard Input Output” 的缩写,也就是标准输入输出),里面包含了很多和输入输出相关的功能信息,比如后面用到的 printf 函数的相关说明。有了它,程序才能使用这些输入输出功能,就像做菜前要先准备好菜谱,知道每个步骤怎么操作一样。 - int main()
这是定义了一个叫做 main 的函数,而且这个函数返回值的类型是 int(整数类型)。这里不用了解什么是函数知道这个概念即可。main 函数非常重要,它是整个 C 语言程序运行的入口,也就是说,程序从这里开始执行。就好比你去游乐园,main 函数就是游乐园的大门,不管游乐园里有多少好玩的项目,都得从这个大门进去才能开始体验。而且在一个 C 语言项目(工程)里,main 函数有且只能有一个,不能出现好几个 main 函数,不然程序都不知道该从哪里开始运行了。 - { 和 }
这两个大括号把 main 函数里要执行的代码 “包裹” 起来,告诉编译器,大括号里面的内容是 main 函数的具体执行部分。只要进入了 main 函数这个 “大门”,就按照大括号里的代码顺序依次执行。 - printf(“hello world\n”);
printf 是一个函数,它的功能是在屏幕上输出内容。(“hello world\n”) 是 printf 函数的参数,也就是告诉 printf 函数要输出什么。双引号里的 hello world 就是要显示在屏幕上的文字。\n 是一个特殊的符号,叫做转义字符,它的作用是让光标移到下一行开头,相当于我们在写字时按下回车键换行。所以这行代码执行后,屏幕上会显示 hello world,并且光标会移到下一行。 - return 0;
return 是 “返回” 的意思,因为 main 函数定义的返回值类型是 int(整数),所以这里要返回一个整数。return 0; 表示 main 函数执行结束后,返回一个值 0。在 C 语言中,通常用 return 0 表示程序正常结束,就像你完成一件事情后,向别人报告 “任务顺利完成” 一样。如果返回其他值,可能代表程序执行过程中出现了一些问题 。
如果还是有不理解的地方请不要慌张你就先把以下当作模板来经行记忆。 在熟练掌握c语言之前在写c语言程序的第一步先把这个模板写上去。再在模板内部写代码。
#include <stdio.h>
int main()
{
return 0;
}
3.数据类型
3.1为什么会有数据类型?
在现实生活中到处都充满了各种数据,例如数字、名字、身高、体重等等。这些数据种类繁多,属性各异,为了更好地管理和使用它们,我们会对其进行分类,比如把数字归为一类,用于计算;把名字归为一类,用于区分不同个体。在 C 语言中,同样需要对数据进行分类管理,这就是数据类型存在的意义。数据类型就像是给数据贴上的 “身份标签”,它规定了数据能取哪些值、怎么存进电脑,以及能用哪些操作处理这些数据,让计算机和程序员都能快速了解数据的特点。
C 语言的数据类型主要有三类。基本数据类型是最基础的,好比生活中最常见的数据分类,像char用于存储单个字符,就如同记录一个人的姓氏首字母;int用来存储整数,类似统计班级的学生人数;float能存储小数,就像记录商品的价格。
以下是常用的数据类型
char //字符数据类型
short //短整型
int //整形
long //长整型
long long //更长的整形
float //单精度浮点数(也就是有小数部分的数字)
double //双精度浮点数
看见这么多的类型你可能会有很多疑问例如 为什么一个整形会有4种分类?在解释这个问题之前我们先来了解一下每种类型的大小。
#include <stdio.h>
int main()
{
//在 C 语言中,printf函数是用来在屏幕上输出内容的,而%d是printf函数里的一个 “占位符”。
//当你看到%d时,它就像是一个空的小格子,告诉printf函数:“在这里要用整数的格式来打印数据哦!”
//比如你写printf("%d", 10);,逗号后面的10 就是要放到%d这个小格子里的数据,程序运行后,屏幕上就会显示出10
printf("%d\n", sizeof(char));
printf("%d\n", sizeof(short));
printf("%d\n", sizeof(int));
printf("%d\n", sizeof(long));
printf("%d\n", sizeof(long long));
printf("%d\n", sizeof(float));
printf("%d\n", sizeof(double));
printf("%d\n", sizeof(long double));
return 0;
}
运行结果为:
1 //这里的1代表是一个字节
2
4
4
8
4
8
-
那么什么是字节呢?为什么char类型占一个字节呢,int占四个字节呢等等?在解决这些问题之前我们先来了解计算机中的单位
在计算机的世界里,数据的存储和处理都需要用到各种计量单位,就像我们在生活中用米衡量长度、用千克衡量重量一样。其中,比特(bit) 是计算机中最小的信息计量单位,**它只有两种状态,用 0 和 1 来表示,**就好比开关的 “开” 和 “关”,或者灯泡的 “亮” 和 “灭”。单个比特能承载的信息量极少,几乎没什么实际用处。
为了能存储和处理更多有用的信息,人们把 8 个比特 组合在一起,形成了一个新的单位,叫做 字节(Byte,简称 B) 。字节是计算机中最基本的存储单位,它就像一个小盒子,可以存放一个英文字母、一个数字,或者一个简单的符号(比如 a、5、!)。我们平时说的计算机内存是多少 GB,硬盘容量是多少 TB,都是以字节为基础进行换算的。
随着数据量越来越大,字节这个单位就显得不够用了,于是人们又规定了更大的单位,它们之间的换算关系如下:1KB(Kilobyte,千字节) :1KB 等于 1024 个字节。这里的 “千” 和我们生活中说的 1000 有点不一样,在计算机里,因为采用二进制计数,所以 1KB 是 2 的 10 次方,也就是 1024 字节。它大概能存储 500 多个汉字。 1MB(Megabyte,兆字节) :1MB 等于 1024KB。一首普通音质的 MP3 音乐,大小大概在 3 - 5MB 左右。 1GB(Gigabyte,吉字节) :1GB 等于 1024MB。一部高清电影的大小通常在 1 - 2GB,一些大型的电脑游戏,安装包可能有几十 GB。 1TB(Terabyte,太字节) :1TB 等于 1024GB。现在的大容量硬盘,很多都能达到 1TB 甚至更高,足够存储大量的照片、视频和文件。 1PB(Petabyte,拍字节) :1PB 等于 1024TB,常用于数据中心存储海量的数据。 1EB(Exabyte,艾字节) :1EB 等于 1024PB,这个级别的数据量极其庞大,比如全球每年产生的数据总量,就能达到 EB 级别。
这些单位构成了计算机中完整的存储计量单位体系,通过它们,我们可以清晰地描述数据的大小,无论是衡量一个小文件,还是评估整个数据中心的存储容量,都离不开这些单位。了解计算机中的单位,能帮助我们更好地理解计算机如何存储和处理数据,也能在日常使用计算机时,更清楚地知道存储设备的容量和数据占用情况。
-
在了解计算机单位后我们就可以知道为什么为什么char类型占一个字节呢,int占四个字节呢?等等。
计算机里不同数据类型占的空间不一样,这都是提前设计好的。
char 类型用来存单个字母、数字或符号,比如 ‘a’、‘5’。计算机用 ASCII 码给这些字符编号,总共 128 个字符,用 8 个比特(也就是 1 个字节)就能把它们都表示完,所以 char 就占 1 个字节。
int 类型用来存整数,比如人数、年龄。4 个字节(32 个比特)能表示 -21 亿多到 21 亿多这么大的范围,日常生活和普通程序里用到的整数,基本都在这个范围,够用又不浪费空间,所以一般 int 就设定成占 4 个字节。
其他数据类型占的空间,也都是根据要存的数据特点和范围决定的,这样计算机就能又快又好地存数据、算数据啦。
-
在了解什么是数据类型后我们来看一下怎么使用这些数据类型
一般来写格式都是很统一的
数据类型 空格 这个数据的名字 = 数据char ch = 'w'; int weight = 120; double salary = 20.0;
4.变量和常量
4.1什么是常量和变量
生活中的有些值是**不变的**(比如:圆周率,性别,身份证号码,血型等等)
有些值是**可变的**(比如:年龄,体重,薪资)。不变的值,
C语言中用**常量**的概念来表示,变得值C语言中用**变量**来表示。
变量的使用
类型 名称 数据
int age = 150;
float weight = 45.5;
char ch = 'w';
定义变量的同时给它赋予一个值,这种行为叫做初始化。一般情况下,我们在定义变量时最好进行初始化。因为如果不初始化,变量中存储的值是不确定的随机值(由编译器分配)。虽然部分 C 语言编译器允许编译未初始化变量的代码,但也有很多编译器会给出警告,提醒存在风险。并且,在程序运行时使用未初始化的变量,可能会得到意想不到的结果,导致程序出错。
4.2变量的分类
- 局部变量
- 全局变量
在 C 语言中,全局变量和局部变量是根据变量作用范围划分的两种类型,它们在程序里发挥着不同作用。我会用简单的生活场景类比,结合示例代码,让你轻松理解二者区别。
全局变量就像是家里公共区域的物品,比如客厅的电视,家里所有人在任何房间都能使用它。在程序里,全局变量定义在所有函数(比如main函数)可以简单理解为定义在{}的外面,从定义的地方开始,到整个程序结束,任何函数都能访问和修改它。例如:
c
#include <stdio.h>
int global_num = 10; // 这就是全局变量,定义在函数外面
int main() {
printf("全局变量的值:%d\n", global_num); // 在main函数里使用全局变量
return 0;
}
这里的global_num就是全局变量,整个程序的任何函数都可以使用它,方便在不同函数之间共享数据。但要小心,如果很多地方都随意修改全局变量,可能会让程序变得混乱,就像家里每个人都随意挪动客厅电视,大家想看的时候就找不到了。
局部变量则像你房间里私人物品,只有在你房间(特定函数内部)才能使用,出了房间就用不了。在 C 语言里, 局部变量定义在函数内部或者语句块(用大括号{}括起来的部分)里,它的作用范围只在定义它的函数或语句块内。比如:
c
#include <stdio.h>
void function() {
int local_num = 5; // 这是局部变量,只在function函数里有用
printf("局部变量的值:%d\n", local_num);
}
int main() {
// printf("%d\n", local_num); 这样写是错误的,因为local_num在function函数外不能用
function();
return 0;
}
function函数里的local_num就是局部变量,只有在function函数里能使用,出了这个函数它就 “消失” 了。局部变量常用于函数内部临时存储数据,比如计算过程中的中间值,它能避免不同函数之间的数据干扰,就像你的私人物品放在自己房间,不会和别人的东西混在一起 。
注意当全局变量和局部变量的名字相同的情况下,局部优先
4.3变量的使用
这我们写一个简单的加法
#include <stdio.h>
int main()
{
int num1 = 0;
int num2 = 0;
int sum = 0;
printf("输入两个操作数:>");
scanf("%d %d", &num1, &num2);
sum = num1 + num2;
printf("sum = %d\n", sum);
return 0;
}
printf和scanf的使用
在 C 语言中,printf和scanf是两个非常实用的函数,分别用于数据输出和输入,就像计算机与我们 “对话” 的嘴巴和耳朵。接下来我结合示例,为你详细讲解它们的用法。
printf函数:printf的功能是将数据输出到屏幕上,让我们能看到程序处理的结果,就像计算机把信息 “说” 给我们听。
基础用法:使用时,要在双引号内写上想输出的内容,比如printf(“你好,C语言!”);,运行程序后,屏幕上就会显示 “你好,C 语言!”。
格式化输出:当需要输出变量的值时,就要用到格式化占位符。常见的占位符有%d(输出整数)、%f(输出浮点数)、%c(输出字符)等。例如:
c
#include <stdio.h>
int main() {
int num = 10;
float f_num = 3.14;
char ch = 'a';
printf("整数:%d,浮点数:%f,字符:%c\n", num, f_num, ch);
return 0;
}
在这段代码中,printf函数里双引号内的%d %f %c是占位符,
逗号后面的num f_num ch是对应的数据,
程序运行时,占位符会被实际数据替换,
屏幕将输出 “整数:10,浮点数:3.140000,字符:a” 。
scanf函数:scanf函数与printf相反,
它用于从键盘获取用户输入的数据,相当于计算机 “听” 我们说话并记录下来。
基础用法:scanf同样使用格式化占位符,并且要在变量名前加上&符号(表示取地址),告诉计算机把输入的数据存到哪里。例如:
c
#include <stdio.h>
int main() {
int input_num;
printf("请输入一个整数:");
scanf("%d", &input_num);
printf("你输入的整数是:%d\n", input_num);
return 0;
}
运行程序后,屏幕先显示 “请输入一个整数:”,这时从键盘输入一个数字,
比如5,按下回车键,scanf函数会把5存到input_num变量中,
接着程序通过printf输出 “你输入的整数是:5”。
使用printf和scanf函数时,要确保占位符和实际数据的类型匹配,
不然可能导致程序出错或得到意外结果。
同时,使用scanf函数时,要注意输入数据的格式要和要求一致,
比如要求输入整数,就不能输入字母,否则也会出现问题。
4.4 变量的作用域和生命周期
在 C 语言编程里,变量就像一个个小盒子,用来存放数据。而变量的作用域和生命周期,能帮助我们了解这些 “小盒子” 在程序里可以被使用的范围,以及它们存在的时间。
作用域指的是变量在程序中可以被访问和使用的区域。就好比不同的房间有不同的权限,有些物品只能在特定房间使用。在 C 语言中,变量的作用域主要分为全局作用域和局部作用域 。全局变量拥有全局作用域,它们定义在所有函数之外,整个程序的任何地方都能使用,就像家里客厅的公共物品,所有家庭成员在各个房间都能找到并使用。例如:
#include <stdio.h>
int global_var = 10; // **全局变量**,拥有**全局作用域**
void another_function() {
printf("在另一个函数中访问全局变量:%d\n", global_var);
}
int main() {
printf("在main函数中访问全局变量:%d\n", global_var);
another_function();
return 0;
}
**局部变量则具有局部作用域,**它们定义在函数内部或语句块(用大括号{}括起来的部分)里,只能在定义它们的函数或语句块内使用,出了这个范围就 “失效” 了,如同你房间里的私人物品,只有在你的房间内才能使用。比如在函数嵌套的场景下:
#include <stdio.h>
void outer_function() {
int outer_local_var = 20; // **外层函数的局部变量**,作用域仅限于outer_function函数
printf("外层函数局部变量:%d\n", outer_local_var);
{
int inner_local_var = 30; // **内层语句块的局部变量**,作用域仅限于这个大括号内
printf("内层语句块局部变量:%d\n", inner_local_var);
}
// printf("%d", inner_local_var); 这样写会出错,因为超出了inner_local_var的作用域
}
int main() {
outer_function();
return 0;
}
在上述代码中,outer_local_var是外层函数outer_function的局部变量,在函数内都能使用;而inner_local_var作为内层语句块的局部变量,只能在包含它的大括号内使用,一旦超出这个范围就无法访问。
在 C 语言中,全局变量的作用域和生命周期有着鲜明的特点,深刻影响着程序对数据的管理和使用。
作用域: 全局变量的作用域是全局作用域,它定义在所有函数(比如main函数)之外,从定义的位置开始,到整个程序结束,程序中的任何函数都能访问和使用这个变量 。这就好比城市里的公共广场,所有市民不管在哪个区域活动,都能来到这里。例如:
#include <stdio.h>
int global_count = 0; // 定义全局变量global_count
void add_count() {
global_count++; // 在add_count函数中访问并修改全局变量
}
int main() {
add_count();
printf("全局变量的值:%d\n", global_count); // 在main函数中使用全局变量
return 0;
}
上述代码里,global_count在add_count函数和main函数中都能被访问和操作,这就是全局作用域的体现。并且,即使在不同的源文件(多文件编程场景)中,通过合适的声明(使用extern关键字),也能访问到同一个全局变量。
生命周期描述的是变量从 “诞生” 到 “消亡” 的时间过程。
- 全局变量在程序开始执行时就创建了,直到程序结束才会被销毁,贯穿整个程序的运行过程,就像从房子建好就一直放在客厅的家具,只要房子还在,它就一直存在。
- 局部变量的生命周期和作用域紧密相关。当程序执行到定义局部变量的位置时,局部变量被创建;当包含该变量的函数执行结束,或者语句块执行完毕,局部变量就会被销毁,存储空间也会被释放。比如在函数中定义的局部变量,每次调用函数时它被创建,函数执行完就 “消失” 了,下次再调用函数,它又会重新创建,就像你每次去图书馆借书,借到书时书归你暂时使用(变量创建),还书后它就不再属于你(变量销毁) 。
4.5常量
常量顾名思义就是描述那些不变的量
C语言中的常量分为以下以下几种:
1. 字面常量
2. const 修饰的常变量
3. #define 定义的标识符常量
4. 枚举常量
1. 字面常量
特性:
1.直接在代码中写出的固定值,没有变量名,编译后直接嵌入到程序中。
2. 最常用的特性为不能被修改<-非常关键
举例:
// 常见的字面常量示例
int a = 10; // 整数常量 10
float b = 3.14f; // 浮点常量 3.14(f后缀表示float类型)
char c = 'A'; // 字符常量 'A'
const char* str = "Hello"; // 字符串常量 "Hello"
说明:字面常量是最基本的常量形式,如数字10、字符’A’、字符串"Hello"等,它们在代码中直接出现无法被修改。
2. const 修饰的常变量
特性:
- 用const关键字声明的变量,一旦初始化后值不能被修改。
- 本质上是变量,但具有常量的只读特性(不能被修改)。
- 内存中会为其分配空间,但编译器会阻止对其值的修改操作。
举例:
c
运行
const int MAX_SIZE = 100; // 常变量,初始化后不可修改
// MAX_SIZE = 200; // 错误:尝试修改const变量的值
void func() {
const float PI = 3.14159; // 局部常变量
// PI = 3.14; // 错误:不能修改const变量
}
说明:const变量必须在声明时初始化,之后任何修改其值的操作都会导致编译错误。
3. #define 定义的标识符常量
特性:
- 用预处理指令#define定义的符号常量,在编译前由预处理器进行文本替换。
- 没有类型信息,只是简单的文本替换,不占用内存空间。
- 作用域从定义处到文件结束或被#undef取消。
举例:
c
运行
#define PI 3.14159 // 定义标识符常量PI
#define STR "abcdef"
int main() {
float radius = 5.0;
float area = PI * radius * radius; // 预处理器会将PI替换为3.14159
printf("%d\n",PI);
printf("%s\n",STR);
return 0;
}
说明:#define定义的常量在编译前就被替换为对应的值,因此不会进行类型检查,可能导致意外错误(如宏函数的参数副作用)。
4. 枚举常量
这里只是感性的了解后面会学习
特性:
- 使用enum关键字定义的一组具名常量,默认从 0 开始递增。
- 枚举常量属于整型,可参与整数运算。
- 每个枚举常量在其作用域内唯一。
- 无法被修改
举例:
c
运行
// 定义星期的枚举类型
enum Weekday {
MONDAY, // 默认值为0
TUESDAY, // 1
WEDNESDAY, // 2
THURSDAY, // 3
FRIDAY = 5, // 显式赋值为5
SATURDAY, // 6(接续上一个值递增)
SUNDAY // 7
};
int main() {
//创建对象(类似数据类型变量定义)
enum Weekday today = WEDNESDAY;
if (today == FRIDAY) {
printf("今天是周五!\n");
}
// printf("%d", MONDAY); // 输出0
return 0;
}
说明:枚举常量提供了一种更直观的方式来定义一组相关的常量,常用于状态码、选项等场景。
四种常量的对比
类型 | 定义方式 | 类型检查 | 内存占用 | 作用域 |
---|---|---|---|---|
字面常量 | 直接书写值 | 无 | 嵌入代码 | 全局 |
const 常变量 | const 类型 变量名 = 值 | 有 | 有 | 局部或全局 |
#define 常量 | #define 标识符 值 | 无 | 无 | 文件内 |
枚举常量 | enum { 常量列表 } | 有 | 有 | 枚举作用域内 |
使用场景总结
这四种常量各有特点,使用场景也不同:
1. 字面常量适合简单、一次性使用的值。
2. const 常变量适合需要类型安全的常量,如数组大小。
3. #define 常量适合需要预编译替换的场景,如条件编译。
4. 枚举常量适合表示一组相关的整数值,如状态码或选项。
5.字符串
5.1字符常量
- 字符常量(Character Constant)
定义:
字符常量是用单引号(')括起来的单个字符,它表示一个整数,对应于该字符在ASCII 码表中的值。
特点:
- 本质是一个整数(通常为 8 位),占用1 个字节的内存空间。
- 可以直接参与算术运算,因为其值是对应的 ASCII 码值。
- 用char类型变量存储。
示例:
char c1 = 'A'; // 字符常量'A',ASCII值为65
char c2 = '9'; // 字符常量'9',ASCII值为57(注意:不是数值9)
char newline = '\n'; // 转义字符,表示换行符
// 字符常量可参与运算
int result = 'B' - 'A'; // 结果为1(因为'B'的ASCII值是66,'A'是65)
5.2什么是字符串
字符串(String)
定义:
字符串是由多个字符组成的序列,**用双引号(")括起来。**在 C 语言中,字符串以空字符’\0’(ASCII 值为 0)结尾(牢记非常重要),存储在字符数组中。
特点:
- 字符串本质是一个字符数组,占用N+1 个字节(N 为字符数,+1 用于存储’\0’)。
- 必须以’\0’结尾,否则会导致字符串处理函数(如strlen、printf)越界访问。
- 可以用字符数组或字符指针表示。(先知道有不用了解)
示例:
# include <string.h>
#include <stdio.h>
int main()
{
// 用字符数组存储字符串
char str1[6] = "Hello"; // 数组大小为6(包含'\0')
char str2[] = "World"; // 自动计算大小为6('W','o','r','l','d','\0')
// 用字符指针指向字符串常量
const char* str3 = "C语言"; // 指向静态存储区的字符串常量
// 字符串处理示例
//strlen是专门求字符串里面有多少个字符的函数 头文件为 string.h
printf("长度:%lu\n", strlen(str1)); // 输出5(不包含'\0')
printf("连接后:%s\n", strcat(str1, str2)); // 输出"HelloWorld"
return 0;
}
那么没有字符串结束标志会导致什么呢?
上面我们了解到当我们经行打印的时候printf会一直打印到 ‘\0’ 结束.那么一个字符串的末尾没有’\0’就会一直打印直到遇到’\0’所以就会打印一堆我们不用的东西。例如下面这样
6.转义字符
在 C 语言中,转义字符是一种特殊的字符表示方式,用于表示一些无法直接输入或具有特殊含义的字符。它们以反斜杠\开头,后面紧跟特定的字符或数字组合。以下是常见的转义字符及其用法:
1. 常用转义字符列表
转义字符 | 含义 | ASCII 值 | 用途示例 |
---|---|---|---|
\n | 换行符(LF) | 10 (0x0A) | printf(“Hello\nWorld”); |
\t | 水平制表符(Tab) | 9 (0x09) | printf(“Name\tAge\n”); |
\\ | 反斜杠本身 | 92 (0x5C) | printf(“C:\Users\file.txt”); |
\" | 双引号 用于表示一个字符串内部的双引号 | 34 (0x22) | printf(“She said: “Hi!””); |
\’ | 单引号 用于表示字符常量 | 39 (0x27) | char c = ‘’'; |
\b | 退格符(Backspace) | 8 (0x08) | printf(“abc\b”); → 输出 ab |
\r | 回车符(CR) | 13 (0x0D) | printf(“123\r456”); → 输出 456 |
\a | 警报符(响铃) | 7 (0x07) | printf(“\a”); |
\0 | 空字符(字符串结束符) | 0 (0x00) | char str[] = “Hi\0World”; |
2. 转义字符的用途
2.1 控制输出格式
转义字符常用于格式化输出,例如:
printf("姓名\t年龄\t城市\n");
printf("张三\t25\t北京\n");
printf("李四\t30\t上海\n");
输出结果:
plaintext
姓名 年龄 城市
张三 25 北京
李四 30 上海
2.2 表示特殊字符
当需要在字符串或字符常量中包含引号、反斜杠等特殊字符时,必须使用转义字符:
// 包含双引号的字符串
printf("他说:\"你好!\""); // 输出:他说:"你好!"
// 包含反斜杠的路径
printf("文件路径:C:\\temp\\data.txt"); // 输出:文件路径:C:\temp\data.txt
// 单引号字符常量
char quote = '\''; // 等价于 ASCII 值 39
2.3 字符串中的特殊处理
空字符\0:作为 C 语言字符串的结束标记,用于标记字符串的终止位置。
运行
char str[5] = "abc\0d"; // 字符串实际长度为3(遇到\0结束)
printf("%s", str); // 输出:abc
回车符\r:常用于覆盖当前行的输出(例如进度条):(了解即可不太常用)
printf("Loading... 0%%\r");
sleep(1); // 等待1秒
printf("Loading... 50%%\r");
sleep(1);
printf("Loading... 100%%\n");
3. 八进制和十六进制转义
除了上述预定义的转义字符,还可以用八进制或十六进制表示任意字符:
也就是把八进制或者十六进制转换为十进制在根据ASCII码值打印
- 八进制转义:\ddd(ddd为 1-3 位八进制数)。如: \130 打印结果为X
- 十六进制转义:\xdd(dd为 1-2 位十六进制数)。如: \x30 打印结果为0
示例:
// 用八进制表示换行符
printf("Line1\12Line2"); // \12 等价于 \n
// 用十六进制表示欧元符号(假设使用UTF-8编码)
printf("价格:\xE2\x82\xAC 100"); // 输出:价格:€ 100
// 直接用ASCII码值表示字符
char a = '\x41'; // 等价于 'A'(0x41是'A'的ASCII码)
4. 注意事项
转义字符是单个字符:例如’\n’是一个字符,占用 1 个字节。也就是一个字符
字符串中的转义:在字符串中使用转义字符时,需确保反斜杠后面的字符是合法的转义序列,否则会导致编译警告或错误。
printf("无效转义:\z"); // 错误:未知转义序列
与正则表达式的区别:C 语言转义字符与正则表达式、Python 等语言中的转义规则可能不同,需注意区分。
7.注释
- 代码中有不需要的代码可以直接删除,也可以注释掉
- 代码中有些代码比较难懂,可以加一下注释文字
在 C 语言中,注释是程序员插入到代码中的说明性文本,用于解释代码的功能、逻辑或设计思路。编译器会忽略注释,因此注释不会影响程序的执行结果。以下是关于注释的详细介绍:
1. 注释的语法形式
C 语言支持两种注释方式:
单行注释(//)
以双斜杠//开头,直到行末的所有内容均为注释。
int a = 10; // 定义一个整数变量a并初始化为10
// 这是一个单独的注释行
printf("Hello"); // 输出Hello到控制台
多行注释(/* … */)
用/开头,/结尾,可以跨越多行。
/*
* 这是一个多行注释
* 计算两个数的和
* 参数:a和b是两个整数
* 返回值:a和b的和
*/
int add(int a, int b) {
return a + b; // 返回两数之和
}
2. 注释的作用
2.1 解释代码逻辑
帮助其他开发者(或未来的自己)理解代码的意图和实现方式。
// 使用冒泡排序算法对数组进行升序排序
for (int i = 0; i < n-1; i++) {
for (int j = 0; j < n-i-1; j++) {
if (arr[j] > arr[j+1]) {
// 交换arr[j]和arr[j+1]
int temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
2.2 记录设计思路
说明为什么采用特定的实现方式,或代码模块的设计目标。
/*
* 本函数采用二分查找算法,时间复杂度为O(log n)
* 前提条件:数组必须已按升序排序
*/
int binarySearch(int arr[], int target, int left, int right) {
// ... 函数实现 ...
}
2.3 标记待解决问题
使用注释标记代码中的待完善部分或已知问题。
// TODO: 增加输入验证,防止用户输入负数
int getAge() {
int age;
scanf("%d", &age);
return age;
}
// FIXME: 当除数为0时会导致程序崩溃
int divide(int a, int b) {
return a / b;
}
2.4 调试辅助
临时注释掉部分代码以排除错误,或记录调试过程。
// 调试用:打印中间结果
// printf("当前位置:x=%d, y=%d\n", x, y);
3. 注释与代码可读性
良好的注释能显著提高代码的可读性,但过度注释或错误注释反而会降低代码质量。理想情况下,代码本身应尽可能自解释,而注释则用于补充无法直接从代码中看出的信息(如设计决策、算法复杂度等)。
8.选择语句
我们在现实生活中处处都会遭遇选择,选择的结果可以分为两类是或者不是
在c语言中也类似大体可以分为四类:
8.1. if-else语句
基本语法
if (条件表达式) {
// 条件为真时执行的代码
} else {
// 条件为假时执行的代码
}
执行逻辑
- 如果条件表达式的值为非零(即真),则执行if块内的代码。
- 如果条件表达式的值为零(即假),则执行else块内的代码。
int age = 18;
if (age >= 18)
{
printf("你已成年,可以投票。\n");
}
else
{
printf("你还未成年,无法投票。\n");
}
8.2. 嵌套if-else语句
语法
if (条件1) {
// 条件1为真时执行
} else if (条件2) {
// 条件1为假但条件2为真时执行
} else if (条件3) {
// 条件1和2都为假但条件3为真时执行
} else {
// 所有条件都为假时执行
}
示例
int score = 85;
if (score >= 90) {
printf("优秀\n");
} else if (score >= 80) {
printf("良好\n");
} else if (score >= 60) {
printf("及格\n");
} else {
printf("不及格\n");
}
8.3. 三元运算符(条件表达式)
语法
条件表达式 ? 表达式1 : 表达式2;
执行逻辑
1. 如果条件表达式为真,则返回表达式1的值。
2. 如果条件表达式为假,则返回表达式2的值。
示例
int a = 10, b = 20;
int max = (a > b) ? a : b; // 等价于 if (a > b) max = a; else max = b;
printf("较大值是:%d\n", max);
8.4. switch语句
基本语法
switch (表达式) {
case 常量1:
// 表达式等于常量1时执行
break;
case 常量2:
// 表达式等于常量2时执行
break;
// 更多case...
default:
// 表达式不等于任何常量时执行
}
执行逻辑
表达式的值会与每个case后的常量进行比较。
如果匹配,则执行对应case下的代码,直到遇到break语句跳出switch。
如果没有匹配的case,则执行default(可选)下的代码。
示例
int day = 3;
switch (day) {
case 1:
printf("星期一\n");
break;
case 2:
printf("星期二\n");
break;
case 3:
printf("星期三\n");
break;
default:
printf("其他\n");
}
9.循环语句
在 C 语言中,循环语句用于重复执行一段代码,直到满足特定条件为止。循环是实现程序自动化和高效处理批量任务的基础工具。C 语言提供了三种主要的循环语句:for、while和do-while。以下是详细介绍和示例:
9.1. for循环
基本语法
for (初始化表达式; 条件表达式; 迭代表达式) {
// 循环体:重复执行的代码
}
执行流程
初始化表达式:只执行一次,通常用于初始化循环变量。
条件表达式:每次循环前检查,如果为真则执行循环体,否则跳出循环。
循环体:条件为真时执行的代码。
迭代表达式:每次循环体执行后执行,通常用于更新循环变量。
重复步骤 2-4,直到条件为假。
示例:计算 1 到 10 的和
int sum = 0;
for (int i = 1; i <= 10; i++) {
sum += i; // 等价于 sum = sum + i;
}
printf("1到10的和是:%d\n", sum); // 输出55
9.2. while循环
基本语法
while (条件表达式) {
// 循环体
}
执行流程
条件表达式:每次循环前检查如果为真则执行循环体,否则跳出循环。
循环体:条件为真时执行的代码。
重复步骤 这个直到条件为假。
示例:计算 1 到 10 的和
int sum = 0;
int i = 1;
while (i <= 10) {
sum += i;
i++; // 更新循环变量,否则会死循环
}
printf("1到10的和是:%d\n", sum); // 输出55
9.3. do-while循环
基本语法
do {
// 循环体
} while (条件表达式); // 注意分号!
执行流程
循环体:先执行一次。
条件表达式:执行完循环体后检查,如果为真则继续循环,否则跳出。
什么这个过程,直到条件为假。
示例:至少输入一次密码
int password;
do {
printf("请输入密码:");
scanf("%d", &password);
} while (password != 123456); // 密码正确时跳出循环
printf("密码正确,欢迎登录!\n");
10.函数
10.1. 什么是函数?
函数就像一台 “机器”,你给它一些 “原料”(输入),它会帮你加工出 “产品”(输出)。
比如,一台榨汁机:
原料:苹果、橙子
加工:榨汁机内部处理
产品:果汁
在代码里,函数是一段封装好的代码,你可以重复使用它来完成特定任务。
10.2. 函数的基本结构
输出类型 函数名字(输入原料) {
// 函数内部的代码(加工过程)
return 加工后的产品;
}
举个例子:做加法的函数
// 定义一个加法函数
int add(int a, int b) { // 输入:两个整数a和b
int result = a + b; // 加工:把a和b相加
return result; // 输出:返回相加的结果
}
10.3. 怎么使用函数?
就像使用榨汁机一样:
准备原料:把数据传递给函数
启动机器:调用函数
获取产品:使用函数返回的结果
int main() {
// 准备原料:两个数字
int num1 = 5;
int num2 = 3;
// 启动机器:调用add函数,把num1和num2作为原料传进去
int sum = add(num1, num2); // 得到结果:8
printf("相加的结果是:%d\n", sum); // 输出:8
return 0;
}
10.4. 为什么需要函数?
- 避免重复劳动
比如,你需要在多个地方做加法:
// 如果没有函数,每次做加法都要写一遍代码
int a = 2 + 3;
int b = 7 + 8;
int c = 100 + 200;
// 有了函数后,只需要写一次加法逻辑,然后反复调用
int a = add(2, 3);
int b = add(7, 8);
int c = add(100, 200);
- 让代码更有条理
把复杂的任务拆分成小函数,就像把一个大工程拆分成多个小零件:
// 一个复杂的程序
int main() {
// 代码A:读取用户输入
// 代码B:验证输入是否合法
// 代码C:计算结果
// 代码D:显示结果
return 0;
}
// 使用函数拆分后
int main() {
readInput(); // 调用读取输入的函数
validateInput(); // 调用验证输入的函数
calculate(); // 调用计算的函数
displayResult(); // 调用显示结果的函数
return 0;
}
10.5. 函数的常见疑问
- 函数一定要有输入和输出吗?
不一定:
可以没有输入:比如 “打印欢迎信息” 的函数,不需要原料。
可以没有输出:比如 “关闭文件” 的函数,只需要执行操作,不需要返回结果。
示例:没有输入和输出的函数
// 打招呼的函数,不需要输入,也不返回结果
void sayHello() {
printf("你好!\n");
// 没有return语句,因为返回类型是void
}
- 函数可以自己调用自己吗?(很难先知道有后面再详细了解)
可以,这叫 “递归”。但需要设置停止条件,否则会无限循环。
示例:用递归计算阶乘
// 计算n的阶乘(n! = 1×2×3×...×n)
int factorial(int n) {
if (n == 1) {
return 1; // 停止条件:1的阶乘是1
} else {
return n * factorial(n-1); // 自己调用自己
}
}
总结
函数就是把一段代码包装起来,起个名字,让它可以被重复使用。就像生活中的各种工具一样,函数让代码更简洁、更有条理。
11.数组
11.1. 什么是数组?
数组就是 “一排格子”,用来存放多个相同类型的数据。
比如,超市的储物柜:
每个格子有编号(从 0 开始)
每个格子可以放一件物品(比如书包、行李箱)
所有格子的大小是一样的
在代码里,数组是连续存储的同类型数据集合,可以通过索引快速访问每个元素。
11.2. 如何定义数组?
数据类型 数组名[格子数量];
举个例子:定义一个存放 5 个整数的数组
int scores[5]; // 定义一个名为scores的数组,有5个格子,每个格子存放一个整数
11.3. 如何使用数组?
- 给数组赋值
scores[0] = 85; // 第1个格子(索引0)赋值为85
scores[1] = 92; // 第2个格子(索引1)赋值为92
scores[2] = 78; // 第3个格子(索引2)赋值为78
scores[3] = 95; // 第4个格子(索引3)赋值为95
scores[4] = 88; // 第5个格子(索引4)赋值为88
或者一次性赋值
int ages[3] = {20, 25, 30}; // 定义数组时直接初始化
- 访问数组元素
printf("第1个分数:%d\n", scores[0]); // 输出:85
printf("第3个分数:%d\n", scores[2]); // 输出:78
int sum = scores[0] + scores[1]; // 计算前两个分数的和
11.4. 数组的常见操作
- 遍历数组
用循环依次访问每个元素:
// 打印所有分数
for (int i = 0; i < 5; i++) {
printf("第%d个分数:%d\n", i+1, scores[i]);
}
- 计算数组长度
int length = sizeof(scores) / sizeof(scores[0]); // 数组总大小 ÷ 单个元素大小
printf("数组长度:%d\n", length); // 输出:5
- 修改数组元素
scores[2] = 80; // 把第3个分数修改为80
-
数组的注意事项
- 索引从 0 开始
- 第 1 个元素的索引是 0
- 第 2 个元素的索引是 1
……
最后一个元素的索引是数组长度 - 1
- 避免越界访问
- 索引从 0 开始
int numbers[3] = {1, 2, 3};
printf("%d\n", numbers[3]); // 错误!数组索引最大是2,访问3会导致越界
- 数组名就是首地址
int arr[5] = {10, 20, 30, 40, 50};
printf("数组首地址:%p\n", arr); // 输出数组的起始内存地址
12操作符
1. 什么是操作符?
操作符就是 “运算符”,是用于执行各种运算的符号。
比如数学中的+、-、×、÷,在 C 语言中对应的操作符是:
+
(加法)-
(减法)*
(乘法)/
(除法)
markdown
2. 常见操作符分类
(1)算术操作符
操作符 | 名称 | 示例 | 结果 |
---|---|---|---|
+ | 加法 | 5 + 3 | 8 |
- | 减法 | 5 - 3 | 2 |
* | 乘法 | 5 * 3 | 15 |
/ | 除法 | 5 / 3 | 1 |
% | 取余 | 5 % 3 | 2 |
++ | 自增 | int a=5; a++; | a=6 |
-- | 自减 | int a=5; a--; | a=4 |
注意:
- 除法
/
:如果两个操作数都是整数,结果会截断小数部分(如5/3=1
)。 - 自增
++
和自减--
有前置和后置之分:
int a = 5;
int b = ++a; // 前置:先自增,再赋值,b=6, a=6
int c = a++; // 后置:先赋值,再自增,c=6, a=7
(2)赋值操作符
操作符 | 名称 | 示例 | 等价于 |
---|---|---|---|
= | 基本赋值 | a = 5 | a = 5 |
+= | 加后赋值 | a += 3 | a = a + 3 |
-= | 减后赋值 | a -= 3 | a = a - 3 |
*= | 乘后赋值 | a *= 3 | a = a * 3 |
/= | 除后赋值 | a /= 3 | a = a / 3 |
%= | 取余后赋值 | a %= 3 | a = a % 3 |
(3)比较操作符
操作符 | 名称 | 示例 | 结果(假设a=5, b=3 ) |
---|---|---|---|
== | 等于 | a == b | 0 (假) |
!= | 不等于 | a != b | 1 (真) |
> | 大于 | a > b | 1 (真) |
< | 小于 | a < b | 0 (假) |
>= | 大于等于 | a >= b | 1 (真) |
<= | 小于等于 | a <= b | 0 (假) |
(4)逻辑操作符
操作符 | 名称 | 示例 | 结果(假设a=1, b=0 ) |
---|---|---|---|
&& | 逻辑与 | a && b | 0 (假) |
` | ` | 逻辑或 | |
! | 逻辑非 | !a | 0 (假) |
逻辑与&&
和逻辑或||
的短路特性:
&&
:如果左边为假,右边不再执行。int a = 0, b = 5; if (a && (b = 10)) { // a为0,右边的(b=10)不会执行 printf("执行"); } printf("b=%d", b); // 输出b=5
||:如果左边为真,右边不再执行。
int a = 1, b = 5;
if (a || (b = 10)) { // a为1,右边的(b=10)不会执行
printf("执行");
}
printf("b=%d", b); // 输出b=5
(5)位操作符
操作符 | 名称 | 示例 | 说明(二进制) |
---|---|---|---|
& | 按位与 | 5 & 3 | 0101 & 0011 = 0001 |
` | ` | 按位或 | `5 |
^ | 按位异或 | 5 ^ 3 | 0101 ^ 0011 = 0110 |
~ | 按位取反 | ~5 | ~0101 = 1010 |
<< | 左移 | 5 << 1 | 0101 << 1 = 1010 |
>> | 右移 | 5 >> 1 | 0101 >> 1 = 0010 |
用途:
- 左移
<<
:相当于乘以2的n次方(如5<<1
等价于5*2=10
)。 - 右移
>>
:相当于除以2的n次方(如5>>1
等价于5/2=2
)。
markdown
(6)其他操作符
操作符 | 名称 | 示例 | 说明 |
---|---|---|---|
sizeof | 求字节大小 | sizeof(int) | 返回4 (32位系统) |
& | 取地址 | &a | 返回变量a的内存地址 |
* | 解引用 | int *p = &a; *p | 获取指针p指向的值 |
(类型) | 强制类型转换 | (float)5/2 | 将5转换为float型 |
markdown
3. 操作符优先级
优先级决定了表达式中操作符的计算顺序。例如:
2 + 3 * 4
先算乘法(3*4=12
),再算加法(2+12=14
)。
常见操作符优先级(从高到低):
()
(括号)++
、--
(自增自减)*
、/
、%
(乘除取余)+
、-
(加减)>
、<
、>=
、<=
(比较)==
、!=
(等于不等于)&&
(逻辑与)||
(逻辑或)=
、+=
、-=
等(赋值)
建议:不确定优先级时,用括号明确指定计算顺序!
markdown
4. 示例代码:操作符综合应用
#include <stdio.h>
int main() {
int a = 5, b = 3;
// 算术操作符
printf("a+b = %d\n", a + b); // 8
printf("a-b = %d\n", a - b); // 2
printf("a*b = %d\n", a * b); // 15
printf("a/b = %d\n", a / b); // 1(整数除法)
printf("a%%b = %d\n", a % b); // 2(取余)
// 赋值操作符
a += 2; // a = a + 2 = 7
printf("a += 2后,a = %d\n", a); // 7
// 比较操作符
printf("a > b 结果:%d\n", a > b); // 1(真)
// 逻辑操作符
printf("a>b && a<10 结果:%d\n", a>b && a<10); // 1(真)
// 位操作符
printf("a & b = %d\n", a & b); // 7 & 3 = 0111 & 0011 = 0011 = 3
// 其他操作符
printf("a的地址:%p\n", &a); // 输出a的内存地址
printf("int类型的大小:%lu\n", sizeof(int)); // 输出4(32位系统)
return 0;
}
5. 总结
操作符是C语言中进行各种运算的基础工具,主要分为:
- 算术操作符:
+
、-
、*
、/
、%
、++
、--
- 赋值操作符:
=
、+=
、-=
、*=
、/=
、%=
- 比较操作符:
==
、!=
、>
、<
、>=
、<=
- 逻辑操作符:
&&
、||
、!
- 位操作符:
&
、|
、^
、~
、<<
、>>
- 其他操作符:
sizeof
、&
、*
、(类型)
使用时注意:
- 整数除法会截断小数
- 逻辑运算有短路特性
- 不确定优先级时用括号明确顺序
12.关键字
C语言常见关键字详解
markdown
1. 什么是关键字?
C语言关键字是编译器预先定义的保留标识符,具有特殊含义和用途,不能作为变量名或函数名使用。C语言标准(如C11)共定义了44个关键字,可分为以下几类:
- 数据类型关键字(如
int
,char
,struct
) - 控制语句关键字(如
if
,for
,while
) - 存储类关键字(如
static
,extern
) - 其他关键字(如
const
,sizeof
)
2. 数据类型关键字
(1)基本数据类型
关键字 | 用途 | 示例 | 占用字节(32位系统) |
---|---|---|---|
int | 整数类型 | int age = 20; | 4 |
char | 字符类型(1字节整数) | char c = 'A'; | 1 |
float | 单精度浮点数 | float pi = 3.14f; | 4 |
double | 双精度浮点数 | double e = 2.71828; | 8 |
void | 无类型(用于函数返回值等) | void printHello(); | - |
(2)类型修饰关键字
关键字 | 作用 | 示例 |
---|---|---|
short | 缩短整数范围 | short num = 1000; |
long | 延长整数范围 | long count = 100000L; |
signed | 有符号数(默认) | signed int x = -5; |
unsigned | 无符号数(只有正数) | unsigned int age = 30; |
const | 常量(值不可修改) | const float PI = 3.14; |
volatile | 告诉编译器不要优化变量 | volatile int sensor_val; |
(3)复杂数据类型
关键字 | 用途 | 示例 |
---|---|---|
struct | 定义结构体 | struct Point { int x, y; }; |
union | 定义共用体(共享内存) | union Data { int i; float f; }; |
enum | 定义枚举类型 | enum Color { RED, GREEN, BLUE }; |
typedef | 为类型定义别名 | typedef int Age; |
3. 控制语句关键字
(1)条件判断
关键字 | 用途 | 示例 |
---|---|---|
if | 条件判断 | if (x > 0) { ... } |
else | 条件不满足时执行 | if (x>0) { ... } else { ... } |
switch | 多分支选择 | switch (day) { case 1: ... } |
case | switch 中的分支 | case 1: printf("Monday"); |
default | switch 中的默认分支 | default: printf("Invalid"); |
(2)循环控制
关键字 | 用途 | 示例 |
---|---|---|
for | 固定次数循环 | for (int i=0; i<10; i++) { ... } |
while | 条件循环(先判断) | while (x > 0) { ... } |
do | 条件循环(后判断) | do { ... } while (x > 0); |
break | 跳出循环或switch | if (x==5) break; |
continue | 跳过本次循环剩余代码 | if (x==5) continue; |
(3)跳转语句
关键字 | 用途 | 示例 |
---|---|---|
goto | 无条件跳转到标签位置 | goto error; ... error: ... |
return | 从函数返回值 | return 0; |
4. 存储类关键字
(1)变量存储特性
关键字 | 作用 | 示例 |
---|---|---|
static | 静态变量(延长生命周期) | static int count = 0; |
extern | 声明外部变量 | extern int shared_var; |
5. 关键字使用注意事项
markdown
(1)避免冲突
- 关键字不能作为变量名或函数名,例如:
int if = 10; // 错误!不能用关键字if作为变量名
(2)存储类关键字的作用域
static
变量在函数内部只会初始化一次:void func() { static int count = 0; // 第一次调用时初始化,后续调用保留值 count++; printf("Count: %d\n", count); }
(3)const
的使用
const
修饰的变量必须在定义时初始化:
const int MAX = 100; // 正确
const int MIN; // 错误!未初始化
MIN = 0; // 错误!不能赋值
7. 示例代码:关键字综合应用
#include <stdio.h>
// 外部变量声明
extern int shared_data;
// 静态全局变量(只在本文件可见)
static int file_private = 0;
// 结构体定义
struct Point {
int x;
int y;
};
// 函数返回值为void
void print_point(struct Point p) {
printf("Point: (%d, %d)\n", p.x, p.y);
}
int main() {
// 自动变量(默认)
int a = 10;
// 常量
const float PI = 3.14f;
// 枚举类型
enum Color { RED, GREEN, BLUE } color = GREEN;
// for循环
for (int i = 0; i < 3; i++) {
// 条件判断
if (i == 1) {
printf("Current color: %d\n", color);
}
}
// 结构体变量
struct Point p = { .x = 5, .y = 10 };
print_point(p);
// 静态局部变量
static int call_count = 0;
call_count++;
printf("Function called %d times\n", call_count);
return 0;
}
markdown
8. 关键字 static
让变量"常住"或"隐身"
(1)static 是什么?
static
就像给变量或函数加了特殊"标签",主要有两个作用:
- 让变量"住得久一点"
- 让变量或函数"只在自己家可见"
(2)static 怎么用?
① 修饰局部变量(函数里的变量)
普通局部变量:函数结束就消失,下次调用重新开始
static局部变量:函数结束后不消失,下次调用接着用
#include <stdio.h>
void count() {
static int num = 0; // 加了static的变量
num++; // 每次调用都+1,不会重置
printf("第%d次调用\n", num);
}
int main() {
count(); // 输出:第1次调用
count(); // 输出:第2次调用(num记住了上次的1)
count(); // 输出:第3次调用
return 0;
}
plaintext
② 修饰全局变量(函数外的变量)
普通全局变量:其他文件也能访问
static全局变量:只有自己文件能访问,别人用不了
// 文件1:a.c
static int score = 100; // 加了static,只能在a.c里用
// 文件2:b.c
// 想访问a.c里的score会报错,因为它被static藏起来了
plaintext
③ 修饰函数
普通函数:其他文件可以调用
static函数:只有自己文件能调用,别人调不了
// 文件1:tool.c
static void print_hello() { // 加了static的函数
printf("你好\n");
}
// 文件2:main.c
// 想调用print_hello会报错,因为它被static藏起来了
9. 关键字 typedef
给类型起"外号"
(1)typedef 是什么?
typedef
就是给数据类型起个简单的外号,让代码更简洁。
比如给"结构体"这种长名字起个短名字。
(2)typedef 怎么用?
① 给基本类型起外号
// 给int起个外号叫Age(年龄)
typedef int Age;
// 以后定义年龄变量时,就可以这样写:
Age person_age = 25; // 等价于 int person_age = 25;
plaintext
② 给结构体起外号(最常用)
没有typedef时,定义结构体变量要写很长:
struct Student {
char name[20];
int age;
};
// 定义变量时必须带struct,很麻烦
struct Student s1;
用 typedef 后:
c
// 给struct Student起外号叫Stu
typedef struct Student {
char name[20];
int age;
} Stu;
// 定义变量时直接用外号,简洁多了
Stu s1; // 等价于 struct Student s1;
③ 给指针类型起外号
// 给int*(整数指针)起外号叫IntPtr
typedef int* IntPtr;
// 定义指针变量时更简单
IntPtr p1, p2; // 等价于 int *p1, *p2;
13 #define定义常量和宏
1. 什么是 #define?
#define
是C语言里的"替换工具"。
简单说就是:给一段内容起个代号,写代码时用代号,编译前自动换成原内容。
比如给"3.14"起个代号"PI",写代码时用PI,实际运行时会变成3.14。
2. #define 定义常量:给数值起外号
(1)怎么用?
// 定义:把 PI 换成 3.14
#define PI 3.14
int main() {
// 写代码时用 PI,实际会变成 3.14
float circle_area = PI * 2 * 2; // 计算半径2的圆面积
printf("面积:%f\n", circle_area);
return 0;
}
(2)为什么好用?
- 改值方便:如果想把PI改成3.14159,只改
#define PI 3.14159
这一行就行,不用改所有用到的地方。 - 一眼看懂:看到PI就知道是圆周率,比直接写3.14清楚。
(3)注意
- 定义时不要加分号!加了会出错:
#define MAX 100 // 对(没分号) #define MAX 100; // 错(多了分号)
惯例:常量名用大写字母(比如 PI、MAX)。
3. #define 定义宏:带参数的替换
(1)什么是宏?
宏和常量类似,但可以带参数,像个"迷你计算器"。
比如定义一个"加两个数"的宏,用的时候传两个数进去,自动算出结果。
(2)怎么用?
// 定义宏:把 ADD(a,b) 换成 a + b
#define ADD(a, b) a + b
int main() {
// 用的时候传两个数,会自动替换成 3 + 5
int sum = ADD(3, 5); // 相当于 int sum = 3 + 5;
printf("和是:%d\n", sum); // 输出 8
return 0;
}
(3)实用例子:求最大值
// 定义宏:找两个数里大的那个
#define GET_MAX(x, y) (x > y ? x : y)
int main() {
int max_num = GET_MAX(10, 20); // 替换成 (10>20?10:20) → 20
printf("大的数是:%d\n", max_num);
return 0;
}
(4)注意:加括号!
宏是直接替换,不加括号可能算错:
// 不好的定义
#define MUL(a, b) a * b
int res = MUL(2+3, 4); // 替换成 2+3*4 → 14(错,应该是20)
// 好的定义:加括号
#define MUL(a, b) ((a) * (b))
int res = MUL(2+3, 4); // 替换成 ((2+3)*4) → 20(对)
14 指针
指针比较难以理解这里说明的内容都是能懂的就懂,不懂的了解即可
1. 什么是指针?
指针就是"地址记录器"——专门用来存储变量在内存中的地址。
可以把内存想象成一排带编号的抽屉:
- 每个抽屉(内存单元)有唯一编号(地址)
- 变量就放在某个抽屉里
- 指针变量就是用来记这个抽屉编号的小本子
2. 指针怎么定义?
定义指针时要加 *
,格式:
数据类型 *指针名;
比如:
c
int a = 10; // 普通变量a,值是10
int *p; // 定义一个int类型的指针p
p = &a; // 把a的地址存到p里(&是取地址符号)
&a 表示 "取变量 a 的地址"(拿到抽屉编号)
p = &a 表示 "让指针 p 记住 a 的地址"
3. 指针怎么用?
用 *
可以通过指针找到变量的值(*叫"解引用"):
int a = 10;
int *p = &a; // p存着a的地址
printf("a的值:%d\n", a); // 直接访问a,输出10
printf("通过指针找a的值:%d\n", *p); // 通过p找到a,输出10
就像:知道抽屉编号(p),就能找到里面的东西(*p)
4. 指针的常见操作
(1)修改指针指向的变量值
通过指针可以改它指向的变量的值:
int a = 10;
int *p = &a;
*p = 20; // 通过指针把a的值改成20
printf("a现在的值:%d\n", a); // 输出20
相当于:拿着抽屉编号(p),把里面的东西换成新的(20)
plaintext
(2)指针指向另一个变量
指针可以随时改变指向的变量:
int a = 10, b = 20;
int *p = &a; // 先指向a
printf("p指向的值:%d\n", *p); // 输出10(a的值)
p = &b; // 改成指向b
printf("p指向的值:%d\n", *p); // 输出20(b的值)
就像:小本子上的抽屉编号可以擦掉重写
5. 为什么要用指针?
- 传参方便:函数传参时,用指针可以直接修改外面变量的值(不用复制大变量)
- 处理数组:数组名本质就是指针,用指针操作数组更灵活
- 动态内存:后面学的动态分配内存(malloc)必须用指针
简单说:指针是C语言的"遥控器",能灵活操作内存里的数据
6. 注意事项
(1)指针必须初始化
没初始化的指针不知道指向哪里,乱用会出错:
int *p; // 危险!p没初始化,不知道指哪里
// *p = 10; // 禁止!可能修改重要数据导致程序崩溃
(2)指针类型要匹配
int类型的指针只能指向int变量,不能乱指:
int a = 10;
float *p;
// p = &a; // 错误!float指针不能指向int变量
7. 一句话总结
符号 | 作用 | 例子 |
---|---|---|
& | 取变量的地址 | &a (a的地址) |
* | 定义指针 / 通过指针取值 | int *p (定义指针)、*p (取p指向的值) |
指针就是"记地址的变量",用&取地址,用*读值改值
15.总结
这篇文章只是简单的去了解一下什么是c语言以及c语言的一些常见的语法等
里面的内容没看懂也没有关系后面的文章还会详细的讲解
这里附上我的gitee仓库 里面我会分享面试题和源码gitee仓库