⛳️1. 第一章 程序设计和C语言
1.1 基本知识
程序:一组计算机能识别和执行的指令。
计算机语言:计算机和人都能识别的语言。
C语言是在B语言的基础上发展而来。
计算机语言发展阶段:
- 机器语言:计算机能直接识别和接受的二进制代码的集合(即机器指令的集合)。
- 符号语言(或汇编语言):使用英语字母和数字表示指令,再通过汇编语言将其转换为机器语言。如ADD表示“+”...
- 高级语言:使用英文单词表示语句和指令,需要编译程序将源程序(高级程序)转换为目标程序(机器指令程序)。
C语言特点:
- 语言简洁,使用方便灵活
- 运算符丰富
- 数据类型丰富
- 程序可移植性好
- 允许直接访问物理地址,进行位操作
- 生成的目标代码质量高,程序执行效率高
函数包括:函数首部(第一行)+函数体(‘{’+内容+‘}‘),函数体=函数声明(即函数原型)+执行部分。
注意易错:
- 每个C程序有且仅有一个main函数,程序从main函数开始执行且结束
- C语言注释方式://单行注释和/* 多行注释 */
- 一个程序由一个或多个源程序文件组成
- C程序编译运行步骤:源程序.c—编译—>目标程序.obj —连接—>可执行程序.exe
全局变量与局部变量同名:在局部变量作用域内,全局变量被“屏蔽”。
变量的静态存储方式和动态存储方式:
- 静态存储方式:变量的内存空间在程序运行之前就已经分配好,并且在整个程序的执行过程中都存在。这意味着变量的生命周期贯穿整个程序的运行时间。
静态存储方式主要包括:全局变量+静态局部变量- 动态存储方式:涉及到在程序运行时动态分配和释放内存,通过使用特定的函数来完成,如
malloc
、free
等。动态存储的变量在程序运行时根据需要分配内存,因此其生命周期和作用域可以在运行时动态地确定。
动态存储方式主要包括:自动变量+形参等等程序的内存区域:
- 代码区:函数块
- 全局数据区:全局变量+静态局部变量【初始值默认为0】+常量
- 堆区:动态数据,如malloc、free【C语言】或new、delete【C++】
- 栈区:局部变量【无初始值】+形参+函数返回值+保护现场+返回地址
字符串的注意事项
- 比较
"hello"=="hello" //两个不相等,比较的是地址,要用strcmp进行比较
- 赋值
char ch[5]; ch="hello"; //error,而是strcpy(ch,"hello");
- 待续
1.2 练习
【例1.1】 最简单的C语言程序:
#include <stdio.h>//包含输入输出函数 int main()//返回int型,所以需要return 0; { printf("hello world!\n");//换行:‘\n’,printf表示输出 return 0; }
【例1.2】求两个整数和
#include <stdio.h> int main() { int a,b; scanf("%d%d",&a,&b);//scanf()输入函数,除字符串都要加& int sum=a+b; printf("a+b=%d\n",sum); return 0; }
【例1.3】求两个整数的较大者
#include <stdio.h> int max(int num1,int num2); int main() { int a,b; scanf("%d%d",&a,&b); printf("%d\n",max(a,b)); return 0; } int max(int num1,int num2){ return num1>num2?num1:num2; }
⛳️第二章 算法—程序的灵魂
2.1 基本知识
程序=算法+数据结构
算法的特性:
- 有穷性
- 确定性
- 有效性
- 零个或多个输入
- 一个或多个输出
循环结构:当型和直到型
- 当 型:先判断后执行
- 直到型:先执行后判断
2.2 练习
1. 判断是否是闰年(被4整除但不可被100整除 || 被400整除)
2. 1+1/2-1/3+1/4-...+1/n
【例2.1】计算1*2*3*4*5
#include <stdio.h> int Mul(int n);//计算从1乘至n的积 int main(){ int n; scanf("%d",&n); printf("乘积为%d\n",Mul(n)); return 0; } int Mul(int n){//计算从1乘至n的积 int product=1; for(int i=1;i<=n;i++){ product*=i; } return product; }
【例2.2】略(文字表述)
【例2.3】判断是否是闰年(被4整除但不可被100整除 || 被400整除)
#include <stdio.h> void RunYear(int n1,int n2);//判断n1至n2中属于闰年的年份并输出 int main() { int year1,year2; scanf("%d%d",&year1,&year2); RunYear(year1,year2); return 0; } void RunYear(int n1,int n2){//判断n1至n2中属于闰年的年份并输出 int count=1;//限制输出格式 for(int i=n1;i<=n2;i++){ if((i%4==0 && i%100!=0) || (i%400==0)){ if(count%6==0){ printf("\n"); count=1; } count++; printf("%d ",i); } } }
【例2.4】1+1/2-1/3+1/4-...+1/n
#include <stdio.h> double Count(int n); int main() { int n; scanf("%d",&n); printf("sum=%.2f",Count(n)); } double Count(int n){//计算并返回值:1+1/2-1/3+1/4-...+1/n int sign=1; double add=0; for(int i=1;i<=n;i++){ add=add+sign*(1.0/i); sign=(-1)*sign; } return add; }
【例2.5】判断一个大于等于3的正整数是否为素数(只能被1和它本身整除)
#include <stdio.h> int PrimeNum(int n);//n是素数返回1,否则-1 int main() { int n; scanf("%d",&n); if(PrimeNum(n)>0){ printf("%d是素数\n",n); } else printf("%d不是素数\n",n); return 0; } int PrimeNum(int n){//n是素数返回1,否则-1 for(int i=2;i<=n;i++){ if(i==n) {return 1;break;} if(n%i==0){return -1;break;} } return 0; }
⛳️第三章 顺序结构程序设计
3.1 常量和变量
1.常量
- 整型常量:如1000,-324
- 实型常量:12.442,0.145E-25
- 字符常量:'a','\n'
- 字符串常量:"hello"
- 符号常量:#define PI 3.14
2.变量
3.常变量
4.标识符:字母、数字(不可’首位)或下划线
3.2 数据类型
3.3 基本运算符
- 算术运算符:
用于各类数值运算。包括加(+),减(-),乘(*),除(/),求余(%),自增(++),自减(--)。- 关系运算符:
用于比较运算。包括大于(>),小于(<),等于(=),大于等于(>=),小于等于(<=)和不等于(!=)。- 逻辑运算符:
用于逻辑运算。包括与(&&),或(||),非(!)。- 位操作运算符:
参与运算的量,按照二进制位进行运算。包括位与(&),位或(|),位非(-),位异或(^),左移(<<),右移(>>)。- 赋值运算符:
用于赋值运算,分为简单赋值(=),符合算数赋值(+=,-=,*=,/=,%=)和复合位运算符(&=,|=,^=,>>=,<<=)三类。复合位运算符在基础中不太常用,常用的为前两类。- 条件运算符:
也是三目运算符,用于条件求值(?:)。- 逗号运算符:
用于把若干表达式组合成一个表达式(,)。- 指针运算符:
用于取内容(*)和取地址(&)。- 求字节运算符:
用于计算数据类型所占的字节数(sizeof)。- 特殊运算符:
有括号(),下标[],成员(-->,.)等
#include <stdio.h> int main() { printf("10%%5=%d 5%%10=%d",10%5,5%10);//输出:10%5=0 5%10=5 return 0; }
3.4 输入输出
scanf:
- int/short/long:%d
- double:%lf %f 都可以
- float:%f
- char:%c
- string:%s 不用'&', 如:scanf("%s",str); 而非scanf("%s",&str);
printf:
- int/short/long:%d
- double:%lf %f 都可以
- float:%m,nf【右对齐】 %-m,nf【左对齐】, 共占m列,带n位小数
- char:%c
- string:%s
- 输出%:%%
putchar(参数):输出单个字符
getchar():输入单个字符
⛳️第四章 选择结构程序设计
选择语句:
- if-else else与离它最近的 if 配对
- switch
#include <stdio.h> int main() { int choice; printf("Enter a number (1-3): "); scanf("%d", &choice); switch (choice) { case 1: printf("You chose option 1.\n"); break; case 2: printf("You chose option 2.\n"); break; case 3: printf("You chose option 3.\n"); break; default: printf("Invalid choice.\n"); } return 0; }
switch语句的注意事项:
- 每个
case
后要加break
: 在switch
语句的每个case
块结束后都要加上break
语句,以避免穿透到下一个case
。如果省略break
,程序会继续执行后面的case
块,直到遇到break
或者switch
语句结束。switch
表达式的类型:switch
表达式的类型必须是整数类型+字符型(int
、char
、enum
等)。不支持浮点数或字符串作为switch
的表达式。case
中的常量表达式:case
标签中的值必须是常量。default
语句(可省略):default
语句通常放在switch
的最后,用于处理未匹配到任何case
的情况。然而,它也可以放在任何位置。条件表达式:表达式1?表达式2:表达式3;
示例:
max=a>b? a:b; //等价于 if(a>b) max=a; else max=b;
⛳️第五章 循环结构程序设计
循环语句:
- while(表达式) 语句 当型
- do 语句 while(表达式) 直到型
- for(表达式1;表达式2;表达式3) 语句 表达式1、2、3都分别可省,也可以都省
注意:
break:终止循环
continue:结束本次循环,进入下次循环 for的表达式3如i++会被执行
⛳️第六章 利用数组处理批量数据
6.1 基本知识
数组特点:
- 有序数据的集合,属于同一数据类型
- 下标从0开始
- 提前开辟固定大小
- 作为参数传递实际上传递的是地址,故在函数中修改原数组也会改变
C语言无string和bool类型
注意:
1.
int a[10];//范围是a[0]至a[9],不存在a[10]
2.
//需要提前开辟大小 int a[10];//ok int n=10; int a[n];//error!
6.2 一维数组 的定义+初始化
int a[10];//a[0]至a[9],共10个 int a[10]={1,2};//整型后面默认0,字符型默认'\0',指针默认NULL int a[0]={0};//全0 int a[0]=123;//类推
6.3 二维数组 的定义+初始化
float a[3][5]; float a[3][5]={{1,2},{3,4,5},{}}; float a[2][2]={0,1,2,3};
6.4 字符数组 的定义+初始化
char c[10]={'h','e','l','l','o'};///长度10 char c[]={'h','e','l','l','o'};//长度5 char c[]={"hello"};//长度6
#include <stdio.h> double CountNum(int n); int main(){ char ch1[]={'h','e','l','l','o'};//5 char ch2[]="hello";//5+1 printf("ch1.len=%d ch2.len=%d\n",sizeof(ch1),sizeof(ch2)); char c1[]={"I am happy"};//10+1 char c2[]="I am happy";//10+1 printf("c1.len=%d c2.len=%d\n",sizeof(c1),sizeof(c2)); char c3[]={'I',' ','a','m',' ','h','a','p','p','y','\0'};//10+1 char c4[]={'I',' ','a','m',' ','h','a','p','p','y'};//10 printf("c3.len=%d c4.len=%d\n",sizeof(c3),sizeof(c4)); return 0; }
⛳️第七章 用函数实现模块化设计
7.1 基本知识
函数调用:
- 函数间可以相互调用,除了main()函数【由OS调用】
- 调用之前需要先声明
函数类型:
- 需要return 值,则函数类型决定返回值类型,如return的imyCar变量类型和int fuc(xx)保持一致
- 无需return 值 则定义函数类型为void
数组做函数参数示例:
- void fuc(int a[],int n){...} //通过 n 指定数组大小
void fuc(int *a,int n){...} //等价- int fuc(int a[10]){...} //直接指定数组大小,其实 10 不起作用
- int fuc(int a[]){...} //不指定数组大小
内部函数和外部函数:
- 内部函数(static):只在当前文件中可见的函数,不能被其他文件中的函数调用。
- 外部函数(extern【默认】):是指可以被其他文件中的函数调用的函数。
数组做参数在函数内变化,则原数组值也会变。示例:
#include <stdio.h> void inv(int a[],int n); int main() { int a[5]={1,2,3,4,5}; inv(a,5); for(int i=0;i<5;i++){ printf("%d ",a[i]); } return 0; } void inv(int a[],int n) { for(int i=0;i<n-1;i++){ for(int j=0;j<n-1-i;j++){ if(a[j]<a[j+1]){ int t=a[j]; a[j]=a[j+1]; a[j+1]=t; } } } }
⛳️第八章 善于利用指针
8.1 基本知识
基本概念:
- 指针就是地址=数据类型+存储编号 int a1;float a2;//前后的地址不同
int a; int * ptr; //指针变量是ptr,类型是int * ptr=&a;
- 指针的强制类型转换需要加上*,如
int intValue = 42; int *intPointer = &intValue; // 强制转换int指针为float指针 float *floatPointer = (float*)intPointer;
指针数组:每个元素存放一个地址,应用场景:指向若干字符串+做main()的形参
int *p[4]; //指针数组 int (*p)[4]; //指针变量:指向一维数组
const指针用法详见:define与const关键字的多种用法-CSDN博客
8.2 练习
【例8.2】看懂即可
/*地址变,值不变*/ #include <stdio.h> int main() { int a,b;//5 3 scanf("%d%d",&a,&b); int *ptr1=&a; int *ptr2=&b; if(a>b){ int t;t=ptr1;ptr1=ptr2;ptr2=ptr1; } printf("a=%d b=%d\n",a,b);//5 3 printf("*ptr1=%d *ptr2=%d",*ptr1,*ptr2);//3 5 return 0; }
【例8.3】看懂即可
#include <stdio.h> void swap(int *ptr1,int *ptr2); int main() { int a,b;//5 3 scanf("%d%d",&a,&b); int *ptr1=&a; int *ptr2=&b; if(a>b){ swap(ptr1,ptr2); //而不是swap(*ptr1,*ptr2); } printf("a=%d b=%d\n",a,b);//5 3 printf("*ptr1=%d *ptr2=%d",*ptr1,*ptr2);//3 5 return 0; } void swap(int *ptr1,int *ptr2){//地址不变;a,b值交换 int t=*ptr1; *ptr1=*ptr2; *ptr2=t; } /* void swap(int *ptr1,int *ptr2){//地址变;a,b值不变 int *t=ptr1; ptr1=ptr2; ptr2=t; }*/ /* void swap(int ptr1,int ptr2){//没有用 int t=ptr1; ptr1=ptr2; ptr2=t; }*/
8.3 指针指向数组+字符串
一维数组:
//一维数组 int a[5]={1,2,3,4,5}; int *ptr1=&a[0];//等价于int *ptr1=a;
- &a[ i ]等 价于 p+i 等价于a+i
- *(p+i) 等价于 *(a+i)等价于a[ i ]
- scanf("%d",p);
多维数组:
- &a[0][i] 等价于 * (a+0)+i 等价于 a[0]+i
- a[0][i] 等价于 *(* (a+0)+i) 等价于 *(a[0]+i)
- 举例:a[1][2]表示为 *(*(a+1)+2)而非*(a+1+2),后者表示a[3]
int a[3][4]={1,2,3,0,0,0,0,0,0}; int *p=a[0]; int (*p)[4]=a;//指针变量p指向 包含4个元素的一维数组 //(*p)[0],(*p)[1],(*p)[2],(*p)[3]分别代表a[0]至a[3]
字符串
对于字符指针指向字符数组,需要注意使用scanf需要加&,后续定义或输出都不加*和&。
对于字符指针指向字符串,需要注意使用scanf不要加&,后续定义加&,输出不加*。
字符指针指向字符数组
char *str="hello world"; //等价于 char *str; str="hello world";//而不是* str="hello world"; printf("%s",str);//而不是printf("%s",* str);
#include <stdio.h> int main(){ /*简单示例*/ char ch[100]; scanf("%s",&ch);//字符串则不用加 & char *str; str = ch; // 而不是 str =& ch; printf("%s\n", str); return 0; }
字符指针指向字符串【C++程序,C语言不提供string类型】
#include <iostream> #include <cstring> using namespace std; int main(){ /*简单示例*/ string s; getline(cin,s);//字符串使用scanf 则不用加 & char *str; str = &s[0]; // 而不是 str =& ch; printf("%s\n", str); return 0; } /* hellow djck hellow djck */
【例8.8】数组翻转
#include <stdio.h> void inv(int a[],int n);//数组翻转 int main() { int a[6]={1,2,3,4,5,6}; inv(a,6); for(int i=0;i<6;i++){ printf("%d ",a[i]);//6 5 4 3 2 1 } return 0; } //等价于void inv(int *a,int n){ void inv(int a[],int n){//数组翻转 for(int i=0;i<n/2;i++){ int j=n-i-1; int t=a[i]; a[i]=a[j]; a[j]=t; } }
8.4 函数指针
8.5 malloc、free动态内存分配
局部变量存放栈区,而malloc开辟的数据存放在堆区。
malloc
和free
是 C 语言中用于动态内存分配和释放的函数需要导入#include <stdlib.h>
头文件
int *a=(int *)malloc(sizeof(int));//变量 int *arr=(int*)malloc(sizeof(int) * 5);//数组
- 注意:使用
malloc
分配内存后,一定要使用free
来释放,以防止内存泄漏。
示例:
#include <stdio.h> #include <stdlib.h>//调用maloc和free int main() { // 分配一个可以存储 5 个整数的内存块 int *arr=(int*)malloc(5 * sizeof(int)); // 使用动态分配的内存 for (int i = 0; i < 5; i++) { arr[i] = i + 1; } // 打印数组元素 for (int i = 0; i < 5; i++) { printf("%d ", arr[i]); } // 释放动态分配的内存 free(arr); int *a=(int *)malloc(sizeof(int)); a=5; printf("a=%d\n",a); free(a); return 0; }
8.6 void *p (无类型指针)
注意在使用
void *
指针时,为了正确解引用指针并获取其指向的值,需要在使用前进行类型转换。无法进行指针运算malloc返回的就是void *指针
下面代码中使用了 (int*)
、(double*)
和 (char*)
分别进行了类型转换。
#include <stdio.h> int main() { // 声明一个 void 指针 void *p; // 声明一些变量 int num = 10; double pi = 3.14159; char letter = 'A'; // 将 void 指针指向不同类型的变量 p = # printf("整数变量的值:%d\n", *((int*)p)); p = π printf("双精度浮点数变量的值:%lf\n", *((double*)p)); p = &letter; printf("字符变量的值:%c\n", *((char*)p)); return 0; }
⛳️第九章 用户自己建立数据类型
9.1 结构体struct
作用:不同数据类型的组合型,结构体长度=各个成员变量长度累加
结构体类型声明形式
- struct Student
{成员1;成员2;...
};//也可在此定义}s1,s2;
后续定义示例 struct Student s1,s2;- typedef struct Student
{成员1;成员2;...
}Student;
后续定义示例 Student s1,s2;- 结构体指针:struct Student *ptr=&s1;
输出两种方式:ptr->name (*ptr).name 等价 s1.name
示例:
#include <stdio.h> #include <string.h> // 定义学生结构体 struct Student { int studentID; char name[50]; int age; float GPA; }; int main() { // 声明一个学生结构体变量 struct Student student1; // 初始化结构体成员 student1.studentID = 12345; strcpy(student1.name, "John Doe");//不可student1.name="John Doe"; student1.age = 20; student1.GPA = 3.8; // 打印学生信息 printf("Student ID: %d\n", student1.studentID); printf("Name: %s\n", student1.name); printf("Age: %d\n", student1.age); printf("GPA: %.2f\n", student1.GPA); // 声明一个指向结构体的指针 struct Student *ptrStudent; // 让指针指向学生结构体变量 ptrStudent = &student1; // 通过指针访问结构体成员 printf("\nVia Pointer:\n"); printf("Student ID: %d %d\n", ptrStudent->studentID,(*ptrStudent).studentID); printf("Name: %s %s\n", ptrStudent->name,(*ptrStudent).name); printf("Age: %d %d\n", ptrStudent->age,(*ptrStudent).age); printf("GPA: %.2f %.2f\n", ptrStudent->GPA,(*ptrStudent).GPA); return 0; }
9.2 共用体union
作用:同一段存储单元存放不同类型的变量,共用体长度=max(各个成员变量的存储长度)
共用体类型声明形式
- union Data
{成员1;成员2;...
};//也可在此定义}d1,d2;
后续定义示例 union Data d1,d2;- typedef union Data
{成员1;成员2;...
}Data;
后续定义示例 Data d1,d2;注意:同一时间只能存在一个成员。
9.3 枚举类型enum
作用:一个变量只有几种可能的值
枚举型声明形式
- enum Weekday {
枚举常量1,枚举常量2,...
};//也可在此定义}s1,s2;
后续定义示例 enum Weekday s1,s2;- typedef enum Weekday {
枚举常量1,枚举常量2,...
}Weekday;
后续定义示例 Weekday d1,d2;注意:枚举元素按常量处理,不能赋值。默认从0开始赋值。
#include <stdio.h> // 定义一个枚举类型 enum Weekday { SUNDAY, // 0 MONDAY, // 1 TUESDAY, // 2 WEDNESDAY, // 3 THURSDAY, // 4 FRIDAY, // 5 SATURDAY // 6 }; int main() { // 声明一个枚举变量 enum Weekday today; // 给枚举变量赋值 today = WEDNESDAY; // 使用枚举变量 switch (today) { case SUNDAY: printf("It's a lazy day!\n"); break; case MONDAY: printf("Start of the workweek\n"); break; case WEDNESDAY: printf("Halfway through the week!\n"); break; default: printf("It's a regular day\n"); } return 0; }
📝总结
嘘,听说有一位C语言大师突破了次元壁,成功通关了编程的炼金之路!从入门到进阶,你的代码之旅如同编程宇宙的星空,熠熠生辉。你不仅掌握了代码的秘法,更诠释了编程的独特魔力。每一次Debug都是一场魔法修炼,每一行代码都是一篇炫目的咒语。恭喜你,编程冒险家,你已经成为这片代码大陆的传奇英雄。未来等着你用键盘书写更加壮丽的代码史
诗,展开属于你的数字冒险。继续释放编码魔法,让代码的光芒照亮前行的路途!