目录
一、面向过程和面向对象
概述:面向过程和面向对象是一种程序设计的框架,面向过程注重“怎么去实现这个对象”,面向对象更加注重“怎么去使用这个对象”。比如经典的造船问题,面向过程的计算机语言会注重,这个轮船的零配件怎么去制作;面向对象的计算机语言会注重,制作轮船的零配件怎么去使用。很显然,面向对象(C++,Java,python)程序设计更加符合我们的思维逻辑,但是面向过程(C语言)的计算机语言运行速度更加快,各有优缺点。
注意:本文接下来的程序设计会基于VScode编辑器,请自行下载配置,可参考视频:手把手教会VSCode的C++环境搭建,多文件编译,Cmake,json调试配置( Windows篇)_哔哩哔哩_bilibili
二、算法——程序的灵魂
2.1、程序 = 算法 + 数据结构
解释:算法是解决问题的方法和步骤;数据结构是计算机储存、组织数据的方式,是指相互之间存在一种或多种特定关系的数据元素的集合。
2.2 最简单的C语言程序设计
打印输出“HelloWorld!”
源码:
#include <stdio.h>
int main()
{
printf("HelloWorld!!!");
}
运行结果:
三、顺序结构程序设计
3.1 概念:
顺序结构程序,即顺序执行,从上往下运行代码。
例如下面代码:
#include <stdio.h>
void main()
{
int a, b, c;
a = 10;
printf("%d\n", a);
b = a;
printf("%d\n", b);
c = a + b;
printf("%d\n", c);
}
代码简析:程序从上往下执行,先:a = 10,然后b=a=10,最后:c = a + b =20;
3.2 常量和变量
定义:常量是在程序执行过程中其值不可以发生改变的数据;变量是在程序执行过程中其值可以发生变换的数据。
示例:
#include <stdio.h>
#define pi 3.14 //宏定义了一个pi的常量
void main()
{
int a = 10; //定义了一个变量a,并赋初值为10
pi = 10;//此处会报错,因为pi是常量,其值在程序执行过程中不可以去修改
a = 3.14;
printf("a=%d,pi=%d", a, pi);
}
3.3 数据类型
定义:数据类型是根据其数据大体特征和占用的内存空间进行数据类型的划分。
C语言数据类型大体上可以分为:基本类型、构造类型、指针类型、空类型,接下来会对基本类型进行详解,其他数据类型后期会提及。
3.3.1 基本数据类型
用一个程序进行基本数据类型的理解:
#include <stdio.h>
void main()
{
short a1 = 10; //定义了一个短整型的变量,并且赋初值为10
int a2 = 10; //定义了一个整型的变量,并且赋初值为10
long a3 = 10; //定义了一个长整型的变量,并且赋初值为10
float b1 = 10.00; //定义了一个单精度浮点型的变量,并且赋初值为10.00
double b2 = 10.00; //定义了一个双精度浮点型的变量,并且赋初值为10.00
char c = 'x'; //定义了一个字符串
}
打印输出格式:
数值类型范围:
3.4 运算符和表达式
概念:运算符即对数据进行某些操作的符号,比如:加(+)、减(-)、乘(*)、除(/),求余(%)等。
表达式 = 运算符 + 数据
示例源码:
效果图:
3.4.1 输出水仙花数
概述:“水仙花数”是指一个三位数其各部分的立方和等于本身的数。例如:153 = + + 。
示例代码:
#include <stdio.h>
#include <stdlib.h>
void main()
{
int bai = 0; //百位
int shi = 0; //十位
int ge = 0; //个位
int s;
printf("水仙花数为:\n");
for (int n = 100; n < 1000; n++)
{
bai = n / 100 % 10;
shi = n / 10 % 10;
ge = n % 10;
s = bai * bai * bai + shi * shi * shi + ge * ge * ge;
if (n == s) //判断是否为水仙花数
{
printf("%d\t", n);
}
}
while (1)
;
}
结果图:
3.5 自增(++)和自减(--)运算符
自增运算:程序每执行自增语句,对应变量会执行加1操作;自减运算:程序每执行自减语句,对应变量会执行减1操作。
自增示例源码:
#include <stdio.h>
void main()
{
for (int i = 0; i < 10; i++)
{
printf("第%d次循环\n", i);
}
}
结果图:
自减示例源码:
#include <stdio.h>
void main()
{
for (int i = 10; i > 0; i--)
{
printf("第%d次循环\n", i);
}
}
结果图:
i++和++i的区别:
1. 当i++、++i作为单独语句的,都等同于i = i + 1,两者没什么区别。
示例代码:
#include <stdio.h>
void main()
{
int i = 0;
printf("++i:\n");
while (i <= 10)
{
/* code */
printf("第%d次循环\n", i);
++i;
}
i = 0;
printf("i++:\n");
while (i <= 10)
{
/* code */
printf("第%d次循环\n", i);
i++;
}
}
结果图:
可见作为单独语句时,i++和++i是一样的。
2. 作为复合语句,i++是先执行运算操作,然后再加1;++i是先加1,然后再执行运算操作。
示例源码:
#include <stdio.h>
void main()
{
int x = 2, y;
y = x++;
printf("y的值为:%d,x的值为:%d\n", y, x);
int a = 2, b;
b = ++a;
printf("b的值为:%d,a的值为:%d", b, a);
}
结果图:
3.6 数据的输入输出
3.6.1 printf打印输出函数:printf是定义于头文件<stdio.h>的格式化输出函数,主要功能是向标准输出设备按规定格式输出信息。printf函数的一般调用格式为:printf("格式化字符串",<参量表>),输出字符串除了可以是字母、数字、空格和一些数字符号外,还可以是一些转义字符表示特殊的含义。
3.6.2 scanf输入函数:scanf是格式输入函数,即按用户指定的格式从键盘上输入数据到指定的变量中。
示例源码:
#include <stdio.h>
void main()
{
float a, b, c;
printf("请输入两个数:\n");
scanf("%f %f", &a, &b);
c = a + b;
printf("和为:%f", c);
}
结果图:
四、选择结构程序设计
概念:选择结构又称为分支结构,是根据给定条件来确定程序的走向的一种语法规则。
4.1 用if语句实现选择结构
4.1.1 语法格式
if(条件)
{
满足条件执行的语句块;
}
else
{
不满足条件执行的语句块;
}
示例源码:用if语句判断两个数的大小
#include <stdio.h>
void main()
{
int a = 10, b = 20;
if (a < b)
{
printf("a小于b");
}
else
{
printf("a大于b");
}
}
结果图:
代码解析:由源程序可知,当a小于b时,执行if里的语句体;当a大于b时执行else里的语句体。
4.1.2 选择的嵌套
语法格式:
if(条件1)
{
if(条件2)
{
语句体;
}
else
{
语句体;
}
}
源码示例: 判断1-100里面即是奇数又能被3整除的数
#include <stdio.h>
void main()
{
for (int i = 0; i <= 100; i++)
{
/* code */
if (i % 2 != 0) //判断是否为奇数
{
if (i % 3 == 0)//判断是否能被3整除
{
printf("%d\t", i);
}
}
}
}
结果图:
程序分析:第一个if条件是用来做第一个条件的判断,看是否不能被2整除,即是否为奇数,然后再此基础上做第二个条件的判断,看是否能被三整除。
4.1.3 关系运算符和关系表达式
关系运算符有:>,>=,<,<=,==,!=六种,当两个表达式用关系运算符连接起来时,就变成了关系表达式,关系表达式返回的结果只有0或1,即真或假。
4.1.4 逻辑运算符和逻辑表达式
逻辑运算符有:与(&、&&)、或(||)、非(!)、 取反(~)、 异或、同或,当两个表达式用逻辑运算符连接起来时,就变成了逻辑表达式。逻辑表达式返回的结果只有两种“真”或“假”(0或者1)。
作用:逻辑表达式常用于条件的判断。
与:所有表达式都满足条件时,整体才满足条件。返回值为真,否则就为假。
或:只要一个表达式满足条件,返回值即为真,否则返回值为假。
非:取反。
4.2 switch实现多分支选择结构
作用:switch可实现无优先级的多分支选择结构。
语法格式:
switch(表达式)
{
case 常量表达式1: 语句1;
case 常量表达式2: 语句2;
… case 常量表达式n: 语句n;
default: 语句n+1;
}
使用switch判断学生成绩的等级:
#include <stdio.h>
void main()
{
//输入一个分数,判断它的等级
// 90-100:优秀
// 80-90:良好
// 70-80:一般
// 60-70:及格
// 60以下:不及格
int a;
while (1)
{
printf("请输入你的分数:");
scanf("%d", &a);
a /= 10;
switch (a)
{
case 9:
printf("成绩优秀\n");
break;
case 8:
printf("良好\n");
break;
case 7:
printf("一般\n");
break;
case 6:
printf("及格\n");
break;
default:
printf("不及格\n");
break;
}
}
}
运行结果图:
五、循环结构程序设计
作用:重复执行某些操作,直到条件不满足;
5.1 for循环
语法格式:
for(循环初值;循环终止值;循环增值)
{
语句体;
}
使用for循环判断1-100的奇偶数
#include <stdio.h>
void main()
{
for (int i = 1; i <= 100; i++)
{
if (i % 2 == 0)
{
printf("%d是偶数!!\n", i);
}
else
{
printf("%d是奇数!!\n", i);
}
}
while (1)
;
}
5.2 while循环
语法格式:
while(条件表达式)
{
语句体;
}
使用while循环判断1-100中的奇偶数
#include <stdio.h>
void main()
{
int i = 1;
while (i <= 100)
{
if (i % 2 == 0)
{
printf("%d是偶数!!\n", i);
}
else
{
printf("%d是奇数!!\n", i);
}
i++;
}
while (1)
;
}
结果图:
5.3 break和continue
联系:都是终止循环的语句。
区别:break是终止整个循环,continue是结束本次循环,进入下一轮循环,直到循环条件不满足。
示例代码:
break:
#include <stdio.h>
void main()
{
for (int i = 1; i <= 10; i++)
{
if (i == 5)
{
break;
}
printf("%d\t", i);
}
while (1)
;
}
结果图:
continue:
#include <stdio.h>
void main()
{
for (int i = 1; i <= 10; i++)
{
if (i == 5)
{
continue;
}
printf("%d\t", i);
}
while (1)
;
}
结果图:
代码分析:由图可见,使用break,代码到第五个数就终止了,使用continue除了第五个数据,剩下的都输出打印了。
5.4 设计一个猜数字的小游戏
#include <stdio.h>
#include <stdlib.h>
void main()
{
int Num1, Num2, i = 0;
Num1 = rand() % 100 + 1; //产生1-100的随机数
while (1)
{
i++;
printf("请输入一个数:");
scanf("%d", &Num2);
if (Num1 > Num2)
{
printf("第%d次,小了\n", i);
}
else if (Num1 < Num2)
{
printf("第%d次,大了\n", i);
}
else
{
printf("相等");
}
}
}
结果图:
六、数组
这里只介绍一维数组和二维数组(矩阵)。
定义:数组是有序的元素序列,是有限个类型相同元素的集合。数组的元素储存是在连续的块内存中,通过索引(下标)进行元素的存储访问。
6.1 数组的简单公式
6.1.1 数组的定义和使用
一维数组:
#include <stdio.h>
#include <stdlib.h>
void main()
{
int a[] = {1, 2, 3, 4}; //定义一个一维数组,自动匹配长度
int b[4] = {4, 3, 2, 1}; //定义一个一维数组,确定数组长度:4
printf("%d\n", a[3]); //打印输出a数组的第4个元素(下标从0开始)
printf("打印输出a的全部元素:\n");
for (int i = 0; i < 4; i++) //循环遍历a的全部元素
{
printf("%d\t", a[i]);
}
while (1)
;
}
结果图:
二维数组:
#include <stdio.h>
#include <stdlib.h>
void main()
{
int a[5][3] = {80, 75, 92, 61, 65, 71, 59, 63,
70, 85, 87, 90, 76, 77, 85}; //定义一个二维数组,按照下标分配元素(维度)
int b[3][2] = {{1, 2}, {3, 4}, {5, 6}}; //定义一个二维数组,确定数组维度
printf("%d\n", a[0][0]); //打印输出a数组的第(0,0)位元素(下标从(0,0)开始)
printf("打印输出a的全部元素:\n");
for (int i = 0; i < 5; i++) //循环遍历a的全部元素,外层输出行,
{
for (int j = 0; j < 3; j++) //内存输出列
{
printf("%d\t", a[i][j]);
}
}
while (1)
;
}
结果图:
注意:对于一维数组,其长度(下标)可以不明确标注,会自动匹配。对于多维度数组,只有第一维可以不标注,编辑器会自己匹配,剩下的维度一定要明确标注,不然会报错。对于其他编辑器,可以不标注数组长度,编辑器会自动为数组匹配内存空间,但是对于vscode不可以,只需要稍微注意一下这个地方就可以了。使用数组时一定要注意数组的长度,如果超出了数组的长度,那么值是不确定的。
对于数组元素的使用,可以使用下标法,单独使用一个元素,下标可以是常量,也可以是变量,但是不能是表达式。 也可以使用循环遍历调用。
由于C语言是面向过程的计算机语言,它不像面向对象,有各种包装好的方法对数组进行操作,在这里数组就讲这么多,想深入学习了解数组的,可以单独找个完整的教程进行查看。
6.2 冒泡法排序
概述:冒泡法排序是实现数组升序或降序的主要方法,“人如其名”冒泡法就是像烧开水冒泡一样,刚开始在底部水泡很小,越往上水泡越大,这就是升序的来源,反过来,从上往下看,水泡越来越小,这就是降序。冒泡法有趟数和轮数,趟数就是相邻两个元素进行比较,如果是升序排序,就是将大的往后一个位置存放,小的往前一个位置存放,降序反过来。轮数就是要跑几趟,才能实现完整排序。接下来用一张照片进行简单理解:
由图可得:设需要排序的数字为n,完成第一轮需要(n-1)趟,依次递减。完成整个排序至多需要(n-1)轮。
示例代码:
#include <stdio.h>
#include <stdlib.h>
void main()
{
int a[5] = {3, 5, 1, 2, 4};
int b[1];
printf("排序前:\n");
for (int i = 0; i < 5; i++)
{
printf("%d\t", a[i]);
}
for (int i = 0; i < 5; i++) //轮数
{
for (int j = 0; j < 5 - i; j++) //趟数
{
/* code */
if (a[j] > a[j + 1]) //进行相邻两个元素的比较,如果a[j]>a[j+1],则将两者调换位置,否则不调换
{
b[0] = a[j + 1];
a[j + 1] = a[j];
a[j] = b[0];
}
}
}
printf("\n");
printf("排序后:\n");
for (int i = 1; i <= 5; i++)
{
printf("%d\t", a[i]);
}
while (1)
;
}
结果图:
降序将if()条件里面的“>”改成“<”即可,示例代码:
#include <stdio.h>
#include <stdlib.h>
void main()
{
int a[5] = {3, 5, 1, 2, 4};
int b[1];
printf("排序前:\n");
for (int i = 0; i < 5; i++)
{
printf("%d\t", a[i]);
}
for (int i = 0; i < 5; i++) //轮数
{
for (int j = 0; j < 5 - i; j++) //趟数
{
/* code */
if (a[j] < a[j + 1]) //进行相邻两个元素的比较,如果a[j]>a[j+1],则将两者调换位置,否则不调换
{
b[0] = a[j];
a[j] = a[j + 1];
a[j + 1] = b[0];
}
}
}
printf("\n");
printf("排序后:\n");
for (int i = 0; i < 5; i++)
{
printf("%d\t", a[i]);
}
while (1)
;
}
结果图:
七、程序的封装——函数
概述:C语言为了提高写代码的速率,减少“造轮子”的过程,提出了函数这么个概念,函数即功能的意思,是将具有实现一个功能的语句块打包封装,赋予一个名称,即函数名。我们在使用函数时,不需要关注内部代码的实现,只需要关注它的功能和接口就可以了,这就是封装的思想。
7.1 无参函数的定义和使用
在这里讲一下“无参、有参、形参、实参”的概念。
无参函数:无参即没有参数的传递,使用这个函数也不需要有返回值,注意这里讲的是不需要,但是可以有。
有参函数:有参即这个函数有参数的传递,有返回值。
形参:形参即函数定义处的参数。
实参:实参是函数调用处的参数。
注意:函数先定义,再声名,最后调用。
示例代码:
#include <stdio.h>
#include <stdlib.h>
void Eat() //定义一个无参函数,函数的功能是打印输出我在吃饭
{
printf("我在吃饭\n");
}
void main()
{
Eat(); //调用吃饭的函数
while (1)
;
}
结果图:
代码解析:在主函数外,定义了一个Eat()的无参函数,这个函数就是实现打印输出“我在吃饭”的作用,不需要返回值,也不需要参数传递,在调用时,使用函数名即可。这里提一下主函数,主函数也是一个函数,是整个程序的入口处。
7.2 有参函数的定义和使用
定义并且调用函数,实现两个数加、减、乘、除。
示例代码:
#include <stdio.h>
#include <stdlib.h>
int Add(int x, int y) //定义一个加法有参函数,实现两个数相加
{
int z;
z = x + y;
return z;
}
int Sub(int x, int y) //定义一个减法有参函数,实参两个数相减
{
int z;
z = x - y;
return z;
}
int Mul(int x, int y) //定义一个乘法有参函数,实现两个数相乘
{
int z;
z = x * y;
return z;
}
int Div(int x, int y) //定义一个除法有参函数,实参两个数相除
{
int z;
z = x / y;
return z;
}
void main()
{
int add, sub, mul, div;
add = Add(10, 20);
printf("10 + 20 = %d\n", add);
sub = Sub(10, 20);
printf("10 - 20 = %d\n", sub);
mul = Mul(10, 20);
printf("10 * 20 = %d\n", mul);
sub = Sub(20, 10);
printf("20 / 10 = %d\n", sub);
while (1)
;
}
结果图:
代码解析:定义了“加、减、乘、除”四个函数,在主函数直接使用函数名进行调用,我们不需要去关注内部的代码如何实现,只关注相应的功能和参数即可。
7.3 全局变量和局部变量
概念:全局变量和局部变量是根据他们的作用域进行划分的,很显然,全局变量的作用范围是大于局部变量的,像上文提到的形式参数就是一个局部变量,它的作用范围是仅局限于定义函数处。
示例代码:
#include <stdio.h>
#include <stdlib.h>
int a; //定义了一个全局变量a,它的作用范围是整个程序,
void test()
{
a = 10;
int b = 20; //定义了一个局部变量b,它的作用范围局限于这个函数
printf("%d", a);//不会发生报错,因为a是全局变量
}
void test01()
{
printf("%d", b);//会发生报错,因为在b的作用范围之外调用b
}
void main()
{
test();
test01();
while (1)
;
}
7.4 打印输出前20项的斐波那契数列
概述:斐波契那数列是前两项为1,从第三项开始,每一项等于前两项之和的数列。
示例代码:
#include <stdio.h>
#include <stdlib.h>
int Function1(int n)//构造递归函数输出数列
{
if (n == 1 || n == 2)
{
return 1;
}
else
{
return Function1(n - 1) + Function1(n - 2);
}
}
void main()
{
int ret;
for (int i = 1; i <= 20; i++)
{
/* code */
ret = Function1(i);
printf("%d\t", ret);
}
printf("%d", ret);
while (1)
;
}
结果图:
代码简析:上面使用“递归”的思想实现斐波那契数列的求解,所谓“递归”,说白了就是自己调用自己。
八、指针
概念:指针也就是内存地址, 指针变量是存放内存地址的变量,也可以说,指针变量指向内存地址。可以直接通过调用指针变量,从而调用内存地址存放的数据。
8.1 指针的定义和使用
示例代码:
#include <stdio.h>
#include <stdlib.h>
void main()
{
int i = 100;
int *p = &i; //定义了一个整形指针变量,指向i的地址
printf("%d\n", i);
printf("%d\n", p);
printf("%d\n", *p);
while (1)
;
}
结果图:
指针是C语言的核心和灵魂,没有学过指针就相当于没有学过C语言,这里我就不深入阐述了(我学的也不深,哈哈哈哈!!!),想深入学习C语言的,请自行了解学习。
十、嘿嘿
文章存在很多不足的地方,请各位批评指正。