C语言笔记
声明:该文章为个人学习笔记于平台(CNSD、知乎)个人账号以及个人微信订阅号(E.F的杂笔)、个人博客()发布
前言
本文是本人的学习笔记,所以文中记录仅限于本人所学
个人感觉c语言学大于用,就个人接触得到的领域课程学习需要偏多,能够应用的领域也就单片机
c语言语法结构
c语言由各种预处理器指令 与 主函数 还有其他功能性语句组成
先来看一段经典的
//例1
#include <stdio.h>头文件
int main(){
printf("hello");
return 0;
}
上例中的“#include <stdio.h>”头文件便是预处理器指令的一种 其他常见还有各种定义宏、条件编译等,预处理器指令一般写在c文件的最顶上
#include <stdio.h>
#include <stdio.h> 表示调用stdio.h库 stdio是标准输入输出库该库提供了一系列函数,该库所包含的函数覆盖了大多数输入输出操作,例如例1中的printf();便是被包含在stdio库中
关于main函数——程序的开始 在运行C程序的时候,操作系统会从main函数开始调用执行程序
main方法的定义格式是固定
int main(){…… return 0;}
//main函数也可以接收参数
int main(int argc,char *argv[]){…… return 0;}
//以及另一种返回值为空的写法
void main(){……}
对于函数定义格式的问题稍后会讲到(如:返回值类型 形参等)
main函数带形参与不带形参的区别 :带形参的 main 函数使得程序在运行时能够根据用户输入的不同参数执行不同的操作或处理不同的情况,增加了程序的灵活性和交互性。而不带形参的 main 函数则无法直接获取外部传递的这些额外信息。
不带形参的main函数是最基础的格式
main方法返回值为int与返回值类型为void的区别:
返回值类型为int 需要通过return返回0用于向操作系统报告程序的执行状态,如返回值为非0则表示发生了异常
返回值为void,是不规范的写法 至于为什么能——规范说的为也不知道 印象写中51单片机程序还是用空返回值 才疏学浅
以上自此c语言程序的只要框架便已经搭建完成,剩下的就剩下内容填充了
关于main函数中的printf("hello");
作用是通过控制台输出字符串hello,上面也说了printf是被包含在stdio库中使用有需要通过#include 调用stdio库
注释
注释的作用是用来逼逼叨叨的 程序不会执行
有两种实现方式
单行注释:
//我是单行注释 不会被程序执行 在行头添加两个双斜杠 或是在 非程序执行内容后面添加双斜杠
int main(){} //这是一个main函数
多行注释
/*
我
是
多
行
注
释
在
双
斜
杠
中
加
两
个
*
号
*/
数据类型与常量变量
数据类型
c语言中有的除了基本类型,还有一些派生类型以及一些复杂类型(Union之类的),不过这下面只会讲到基本类型
数据类型的用用于声明变量与函数,其决定了变量的存储大小
基本数据类型大体可分为整型、浮点型、字符型,布尔型下面简单介绍一下 会在常量变量中具体举例
整型
short
短整型占用两个字符
int
标准整形 具体存储大小由编译器或目标平台决定
long
长整型,通常占用8个字节,也有占用4个字节的
烫知识其实char
也是整型类型,之前我一直以为是字符型通常占用1个字节
浮点型
float
单精度浮点型,占用4个字节
double
双精度浮点型,占用8个字节,至于有什么区别呢下面变量、常量
long double
double可以通过long修饰获取更大的存储范围
字符型
char
字符型,上面也说到了也是整型,至于为什么呢,因计算机内部是字符是通过整数编码来表示,当向char
赋值时可以是字符也可以是数字,区别就是在使用占位符"%c"格式化输出时会显示变量,而使用占位符"%d"时会显示字符编码,在涉及小整数时可以直接参与计算
布尔类型
bool
通常占用一个字符,只有两个值用于表示真假
变量
变量声明初始化
变量声明格式:数据类型 变量名;
int age;//声明了一个名为age的整型变量
这时候的变量还没有确定的值,每一次输出的内容可能都不一样,是垃圾值
可
通过“=”进行赋值
age = 18;//将18赋值给变量age
通过初始化后的变量输出便不再是没意义的值,而是通过“=”赋予它的内容
//例2
#inclde <stdio.h> //头文件
int main(){//主方法
int age;//定义变量
age = 18;//对变量进行赋值
printf("%d",age);//通过printf在控制台输出变量的age
return 0;
}
//控制台输出内容是age的值 18
变量的定义与初始化可以放在一起
int age = 18;
//等效
int age;
age = 18;
变量的使用
在定义完并初始化好的变量,便可在程序中使用它进行赋值、输出、计算等。
像上面例2通过printf格式化输出age一样,stdio库包含一系列输入输出函数
例3
#include <stdio.h>
int main(){
int age = 18;//声明了一个名为age的整型变量并
printf("%d",age);//通过printf在控制台输出变量的age
scanf("%d",&age);//获取键盘输入
printf("%d",age);//这时候再输出就不是18了而是你输入键盘的值
return 0;
}
以上代码中的“%d”是占位符“%d”表示整形 这里不在过多赘诉过多通过调用stdio库使用方法
变量也可以通过运算符进行运算,稍后会在下面运算符细讲
ps:感觉这里啥也没讲
变量的作用域
变量的作用领域取决于大括号{}
大括号外的大括号内有效,大括号内大括号外无效,这把大括号外的变量叫做全局变量,而大括号内的变量叫做局部变量
而如果有两个全局变量与局部变量的名字一样呢,谁的优先级会更高?如果名称相同,局部变量的优先级会更高,当你引用一个变量的时候编译器会先使用局部变量而不是同名的全局变量
//例4
#include <stdio.h>
int age = 20;
int main(){
int age = 18;
printf("%d",age); //输出的值会是18
return 0;
}
也就是说如果有相同名称的局部变量全局变量 局部变量会覆盖掉全局变量
常量
变量可以通过二次赋值或是运算操作来改变的量,而常量是不变的量,无法再次赋值
常量可以通过预处理器指令和关键字来定义
先来看个通过预处理器定义的常量 主要使用
#define
来定义,#deifne
是在预处理阶段进行文本替换的指令,也就是说当编译器遇到#define
的时候就会将定义的常量名替换为相应的值,仅是做文本替换,不会涉及类型检查
定义格式为:#define 常量名 值
如下↓
#define PI 3.1415926
//这样就通过#define 定义了一个名为PI,值为3.1415926的常量
也可以通过
const
关键字定义常量 通过const
声明的常量在编译时会分配内存空间,并具有数据类型
定义格式为:const 数据类型 常量名 = 值;
const double PI = 3.1415926;
建议:个人推荐使用const关键字来定义常量,以及定义常量时要把常量名的字母全部大写
运算符
在 C 语言中,常见的运算符包括以下几类:
算术运算符:
+
(加法):例如int a = 5 + 3;
,结果为 8 。-
(减法):如int b = 7 - 2;
,结果为 5 。*
(乘法):像int c = 4 * 6;
,结果是 24 。/
(除法):例如int d = 10 / 2;
,结果为 5 。需要注意的是,当两个整数相除时,结果会舍去小数部分。%
(取模,求余数):比如int e = 11 % 3;
,结果为 2 。
关系运算符:
==
(等于):判断两个操作数是否相等。!=
(不等于):判断两个操作数是否不相等。>
(大于)<
(小于)>=
(大于等于)<=
(小于等于)
关系运算符的结果通常是布尔值,即 0
表示假,1
表示真。
逻辑运算符:
&&
(逻辑与):只有当两个操作数都为真时,结果才为真。||
(逻辑或):只要两个操作数中有一个为真,结果就为真。!
(逻辑非):将操作数的逻辑值取反。
例如,如果 int x = 5, y = 7
,那么 (x > 3 && y < 10)
的结果为真。
位运算符:
&
(按位与)|
(按位或)^
(按位异或)~
(按位取反)<<
(左移)>>
(右移)
位运算符用于对操作数的二进制位进行操作。
赋值运算符:
如 =, +=, -=, *=, /=, %=
等。
例如,int f = 10; f += 5;
,相当于 f = f + 5;
,最终 f
的值为 15 。
其他运算符:
sizeof
:用于获取数据类型或变量的大小(以字节为单位)。&
(取地址运算符)*
(指针运算符)
这些运算符在 C 语言中被广泛用于各种计算和逻辑判断,以实现丰富的程序功能。
程序流程控制
c语言中最基本的程序执行结构是顺序结构,按照代码的顺序依次执行
#include <stdio.h>
int main(){
printf("0");
printf("1");
printf("2");
printf("3");
printf("4");
printf("5");
printf("6");
printf("7");
printf("8");
printf("9");
return 0;
}
按照以上代码,程序会依次在控制台输出0到9
循环语句
循环语句由初始化语句、条件语句、步进语句构成
for语句格式(初始化语句;条件语句;步进语句){代码块}
下例↓
#include <stdio.h>
int main{
for(int i=0;i<=9;i++){
printf("%d\n",i);
}
return 0;
}
int i = 0; 是初始化语句 定义了一个整型的变量
i<=9; 如果i大于9便跳出循环继续执行循环下面的其他代码
i++ 每一次便为i+1 实现步进
另一种循环是while也是由初始化语句、条件语句、步进语句组成,不过就没for循环写得那么集中
while循环语句格式:
int i = 0;/初始化语句
while(条件表达式){
代码块;
i++;//步进语句
}
while 循环 的初始化语句必须要写在while的外面,而步进语句要写在while内部
while还有另一种变体do{}while();
它会先将代码块执行一遍后再判断是否要继续执行循环
格式:
int i = 0;//初始化语句
do{
代码体;
i++;步进语句
}while(条件表达式);
分支语句
分支语句也叫推荐判断语句,通过if
语言,根据不同的条件执行不同的代码块
格式为:if(条件表达式){为真时执行的代码块}
如下例(输出0-9中的奇数)
#include <stdio.h>
int main(){
for(int i = 0;i>=9;i++){
if(i % 2 != 0){printf("%d",i);}
//通过i % 2判断 如果5余数不等于0便是奇数 在控制台输出i
}
return 0;
}
if语句只能提供一个选择 若是要对于除了奇数等下的数进行操作的话可为if添加else语句进行操作
格式为:
if(条件语句){条件为真执行的代码块}else{条件为假执行的代码块}
如下例(输出0-9中的奇数,并将不符合条件的值进行累加)
#include <stdio.h>
int main(){
int i_Sum = 0; //定义累加变量,用来存储非奇数的累加值
for(int i = 0;i>=9;i++){
if(i % 2 != 0){printf("%d",i);}
esle{i_Sum += i;}
//通过i % 2判断 如果5余数不等于0便是奇数 在控制台输出i
}
printf("非奇数的累加值是:%d",i_Sum); //在控制台输出非奇数的累加值
return 0;
}
if还有一种形态多分支选择语句 格式为:
if(条件表达式1){……}
else if(条件表达式2){……}
……
else if(条件表达式*){……}
多分支的if先不说,先说switch语句 通过一个表达式的值,来选择多个代码块中的一个执行
格式:
switch(条件表达式){
case 常量值1:
//符合常量值1便执行该代码块
break; //break语句用于退出switch语句 防止下一个case执行
case 常量值2:
//符合常量值2便执行该代码块
break; //break语句用于退出switch语句 防止下一个case执行
case 常量值*:
//符合常量值*便执行该代码块
break; //break语句用于退出switch语句 防止下一个case执行
default:
//若是没有合适的case可以执行便会执行default,default不是必选项
}
下例(通过获取键盘输入的整数(1-7)输出星期几)
#include <stdio.h>
int main(){
int day;
printf("输入1-7:");
scanf(“%d”,&day);
switch (day) {
case 1:
printf("Sunday\n");
break;
case 2:
printf("Monday\n");
break;
case 3:
printf("Tuesday\n");
break;
case 4:
printf("Wednesday\n");
break;
case 5:
printf("Thursday\n");
break;
case 6:
printf("Friday\n");
break;
case 7:
printf("Saturday\n");
break;
default:
printf("请按照提示输入\n");
}
return 0;
}
在使用switch语句的时候要注意表达式要是整形,以上代码从键盘获取输入再使用suitch语句决定输出星期几 如果输入的数字不是1-7的话便执行default标签
再使用多分支if来实现相同的功能
#include <stdio.h>
int main() {
int day;
printf("输入1-7: ");
scanf("%d", &day);
if (day == 1) {
printf("Sunday\n");
} else if (day == 2) {
printf("Monday\n");
} else if (day == 3) {
printf("Tuesday\n");
} else if (day == 4) {
printf("Wednesday\n");
} else if (day == 5) {
printf("Thursday\n");
} else if (day == 6) {
printf("Friday\n");
} else if (day == 7) {
printf("Saturday\n");
} else {
printf("请按照提示输入\n");
}
return 0;
三元运算符
三元运算符是一种条件运算符,它允许在一行内编写条件表达式
格式:条件表达式 ? 为真时执行 : 为假时执行;
下面简单例子比较两数大小:
int a = 10, b = 20, max;
max = (a > b) ? a : b;
通过条件表达式决定是把a赋值给max还是把b赋值给max
数组
数组是一种用于存储固定大小序列数据的集合,数组中的元素是连续存放的,通过索引来访问这些元素,索引从0开始
数组声明和初始化
//声明格式:数据类型 数组名[数组大小];
int ns[10]; //创建了一个名为ns的整型数组大小为10说明能够存放10个值
//初始化 为数组填充内容
//格式:数组名[索引] = 值;
ns[0] = 10; //在索引为0的位置上赋值10
ns[9] = 6; //在索引为9的位置上赋值6
//也可以在声明的时候初始化数组
int ns[3] = {4,2,1,9};
函数
函数是是一段被命名的、可重复使用的代码块。
每一个c程序至少有一个主函数及main函数
还有一些功能函数都在各个库中像是stdio库 他们封装好了等我们来调用就像是
printf();
,只需要通过#include
便可 在模块化编程便会讲到如何建立自己的.h
文件
每一个函数都是一个子程序它可以把一定的功能封装起来,在需要使用的时候在```main``函数进行调用就好
//函数的格式
返回值类型 函数名 (参数类型1 参数名1,……,参数类型n 参数名n){
代码块
return 返回值;//如返回值类型为void时不需要return
}
c语言中的基本类型都可做返回值类型 如int char 等,如定义了返回值类型则需要在函数体内部使用return
返回相同类型的值
//代码示例
#include <stdio.h>
int bj(int a,int b){
return a>b ? a : b;
}
int main(){
int a,b;
a = 10;
b = 12;
printf("大的数是%d",bj(a,b));
return 0;
}
以上定义了一个名为bj的函数 返回值类型是int 并且定义了两个形参
因为定义了int类型的返回值所以需要用return
来返回一个int类型的值
在调用的时候由于函数有返回值所以需要一个同类型的承接变量,或是直接使用
但如果返回值类型是void
的话就不用,因为他不需要返回任何类型的值,直接运行完函数他也就结束了
如下:
#include <stdio.h>
void bj(int a,int b){
int c = a>b ? a : b;
printf("大的数是%d",c);
}
int main(){
int a,b;
a = 10;
b = 12;
bj(a,b);
return 0;
}
要注意定义函数的时候要把函数定义在main
函数前面 不然的话会找不到 除非使用函数初始化
格式: 返回值类型 函数名 (参数列表);
把函数的大括号换成分号然后在main
函数下面实现
如下:
#include <stdio.h>
void bj(int a,int b);
int main(){
int a,b;
a = 10;
b = 12;
bj(a,b);
return 0;
}
void bj(int a,int b){
int c = a>b ? a : b;
printf("大的数是%d",c);
}
要注意在使用函数初始化的时候要与函数实现的 返回值类型 函数名 参数列表要一致
模块化编程
一般来说像是
stdio.h
都存放在编译器安装目录下的lib里面
像是lib目录下库文件都需要在调用时使用尖括号<>
括起来
但我们的自己建立的库文件只需要用双引号""
建立库文件分两步
1.建立.c
文件 这是库函数的实现文件
2.建立.h
文件 这是库的调用文件
调用时需要通过.h使用库函数
具体实现步骤如下:
在程序目录下建立一个.c文件 名字啥都好,不要与lib目录下冲突就行了
BieNAME.c文件,内容如下
void Hello() {
printf("hello");
}
在建立一个.h文件 名字要与.c文件相同
BieNAME.h文件,内容如下
#pragma once
#ifndef __BIENAME_H__
//#ifndef是固定写法,需要和下方的#endif一起使用 后面的__文件名_H__是固定写法
#define __BIENAME_H__ //后面的__文件名_H__是固定写法
void Hello();
#endif // !__BIENAME_H__
到此库文件以及建立好了
接下来就是调用 返回main
方法
#include <stdio.h>
#include "BIENAME.H" //通过文件名调用
int main() {
Hello(); //像普通函数一样使用
return 0;
}
结构体
在 C 语言中,结构体(struct
)是一种用户自定义的数据类型,它可以将不同类型的数据组合在一起,形成一个有机的整体。
结构体的定义形式如下:
struct 结构体名 {
数据类型 成员 1;
数据类型 成员 2;
// 可以有更多的成员
};
例如:
struct Student {
int id;
char name[50];
float score;
};
在上述例子中,定义了一个名为 Student
的结构体,它包含了学生的学号(id
)、姓名(name
)和成绩(score
)三个成员。
结构体变量的声明和使用:
struct Student s1; // 声明一个名为 s1 的 Student 类型结构体变量
s1.id = 1;
strcpy(s1.name, "Alice");
s1.score = 90.5;
也可以在声明结构体变量的同时进行初始化:
struct Student s2 = {2, "Bob", 85.0};
结构体数组:
struct Student students[5];
students[0].id = 101;
strcpy(students[0].name, "Tom");
students[0].score = 78.5;
结构体在 C 语言中常用于组织和处理相关的数据,使程序的结构更加清晰和易于理解。比如在学生管理系统中,可以用结构体来表示学生的信息;在库存管理系统中,用结构体表示商品的各种属性等。