前言
C的学习没有捷径,唯有坚持与努力。
初步认识C语言的基本知识
一、初识C语言
- 计算机语言: C / C++ / java / python…(人和计算机交流)
- C语言 : 通用的计算机编程语言
- 国际标准ANSI C:
C89,C90(主流) C99,C11 - 写C语言代码的工具: 编译器 — Visul Studio 2019 / 2022(推荐)
二、C的编程机制
- 基本思路:
- 机制:
1.编译器:翻译 源代码(高级语言) — 机器语言(目标代码)
2.链接器:合并目标代码 + 启动代码 + 库代码(链接器仅提取所需库代码)
3.库代码:源代码只包含使用库函数printf()的指令,实现printf()函数的代码存储在库 (一个文件名) 中
4.启动代码(startup code):
- 不用系统 (Window / Linux) 处理程序方式不同.
- 程序与操作系统间的接口
三、第一个C语言项目
新建项目
新建源文件
编写代码 + 执行
- 写出主函数 (main函数) 【一个工程中main函数有且只有一个】
int main()
{
return 0;
}
C语言的代码 - 从main函数第一行开始执行
main函数 - 入口
- Printf() - 库函数
a. 在屏幕上打印信息
b.使用需引用头文件<stdio.h>(C的编程机制)
#include<stdio.h> //引用头文件
int main() //int - 函数的返回类型 - 整型
{
printf("Love\n"); //{}内为函数体
return 0;
}
- 执行代码 (编译+链接) : Ctrl + F5
四、数据类型
计算机中的单位
- 计算机的CPU识别: 二进制 0 / 1
- bit - 比特 - 二进制数中的一个数位 0 / 1
- byte - 字节 1 byte = 8 bit
- kb - 1024byte
- mb -1024kb
- gb - 1024mb
- tb - 1024gb
- pb - 1024tb
数据类型
数据类型是为了创建各种类型的变量
1.字符
- char 字符数据类型 — 1byte (占空间的大小)
2.整型
- short 短整型 — 2byte
- int 整型 — 4byte
- long 长整型 — 4byte
- long long 更长的整型 — 8byte
C语言规定:sizeof(long) >= sizeof(int)
VS2019编译器采用=
sizeof - 关键字 - 计算类型或变量所占空间的大小
3.浮点数
- float 单精度浮点数 — 4byte
- double 双精度浮点数 — 8byte
利用printf( ) 和 sizeof 求类型所占内存大小
#include<stdio.h>
int main()
{
//sizeof - 关键字 - 计算类型或变量所占空间的大小
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);
return 0;
}
五、变量
变量 - 能被改变的量
1.定义变量的方法
int main()
{
//数据类型 + 变量的名字 = 0(赋初值)
int age = 18;
double weight = 71.2;
//数据类型 + 变量的名字;
int age;//不推荐
return 0;
}
2.变量的分类
- 局部变量 :{ }内定义的
- 全局变量 :{ }外定义的
局部变量和全局变量名字冲突时,局部优先 (不建议这样写)
#include<stdio.h>
int main()
{
int a = 100;
return 0;
printf("%d\n", a);//执行时,打印100 - 局部优先
}
int a = 2;
3.变量的使用
求整数 a + b = ?
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main()
{
int a = 0;
int b = 0;
int sum = 0;
scanf("%d", &a, &b);//输入函数 - scanf
sum = a + b;
printf("%d", sum);
return 0;
}
scanf函数不安全解决方法:
1. #define _CRT_SECURE_NO_WARNINGS 1 一定放在源文件第一行
2.scanf_s( ) VS编译器提供,不是C语言标准规定的 (只能在VS上跑,其他不行)
4.变量的作用域
作用域(scope):
a. 程序设计概念
b. 一段代码中变量的名字并不总是 可用 /有效的
c. 限定变量名的使用范围
- 局部变量:变量所在的局部范围{ }
- 全局变量:整个工程
5.变量的生命周期
变量的创建和销毁之间的时间段
局部变量的生命周期: 进入局部范围生命开始,出局部范围生命结束
全局变量的生命周期: 整个程序的生命周期
六、常量
常量 - 不能改变的量
1.字面常量
int main()
{
3.14;
10;
'a';
“abcdef”;//字符串字面值
return 0;
}
2.const修饰的常变量
3.define定义的标识符常量
4.枚举常量
可以一一列举的常量: 性别
星期
血型
七、字符串
字符串字面值(String Literal): 由双引号(Double Quote)引起的一串儿字符.
1. 简称: 字符串
2. 结束标志: 转义字符 \0
3.计算字符串长度时,\0不算作字符串内容 \0是结束标志
\0 - 字符串结束标识
strlen( )求字符串长度
引用头文件<string.h>
#include<string.h>
int main()
{
int len = strlen("abc");//string length
printf("%d\n", len);
return 0;
}
/0 - 字符串的结束标识
八、转义字符
\a 警报、蜂鸣 - Alarm
\b 退格符 - Backspace
光标向前移动一位
\r 回车
光标移动至本行开头
\n 换行符 - New line
光标移动至下一行开头
\t 水平制表符 - Table
\v 垂直制表符 - Vertical table
\\ 表示字符 \
\? 表示字符?
\’ 表示字符 ’
\" 表示字符"
\ddd 1到3位八进制数所代表的任意字符
\xdd 十六进制所代表的任意字符
eg:八进制 \101 - 表示字符A
十六进制 \x41 - 表示字符A
ASCII码表
所有的数据在存储和运算时都要使用二进制数(0 / 1) 表示
为编码 英文字母(abcd)、符号(,.;')、数字(1,2,3)而规定了统一的编码规则
注释
解释复杂代码
int main()
{
//C++注释风格 - 常用
*/C语言注释风格 - 不支持嵌套使用 */
return 0;
}
九、分支语句
C语言是结构化的程序设计
分支语句(选择结构):
- if
- switch
if语句
规定 :0为假,非 0为真
语句:C语言中,由一个 ; 隔开的就是一条语句
#include<stdio.h>
int main()
{
//规定 0 为假,非0 为真
if(0)
{
printf("假的,不执行本条");
}
if(1)
{
printf("真的,运行");
}
;//空语句
}
错误逻辑及分析:
正确逻辑
悬空else
常见错误
= 是赋值号
== 是等于号
#include<stdio.h>
int main()
{
int n = 3;
if (n == 5)
{
printf("这条语句不执行,n != 5");
}
if (n = 5)
{
printf("这条语句执行,把 5 赋值给 n,n == 5");
}
return 0;
}
switch语句
1.常用于多分支
switch(整型表达式)
{
语句项;
}
2.语句项:一些case语句 - 决定入口
case 整型常量表达式 :
语句;
3.break - 跳出 / 停止 - 决定出口
分析
1.case语句会从入口一直执行到出口 - break;
2.switch(整型)
case 整型常量:
3.编程好习惯
default子句
switch( ) - ( )中的值,与所有case N:N的值都不匹配
便执行default子句
eg: switch (1) 与 csae 2:不匹配
十、循环语句
循环语句(循环结构):
- while
- for
-do while
while
#include<stdio.h>
//打印出整数1 - 10
int main()
{
int a = 1;//初识化
while (a <= 10) //判断部分
{
printf("%d", a);
a++; //调整部分
}
return 0;
}
调整部分 - 避免死循环
for
Remark:
a. 不要在for循环体内修改循环变量 - 防止for循环失去控制
b. 建议for语句的循环变量采取 " 前闭后开区间 " 的写法
for循环的初始化、判断、调整都可以省略
会产生不同的效果
省略 - 判断部分
省略 - 初始化
多个变量的判定
#include<stdio.h>
int main()
{
int x , y;
for(x = 0, y = 0; x < 2 && y < 5;x++, y++)
{
printf("Love");
}
return 0;
}
逻辑分析
do while
循环中的break和continue
以while循环为例
break
continue
十一、goto语句
适用场景
一次性跳出多个嵌套循环
int main()
{
for(;;)
{
for(;;)
{
for(;;)
{
goto flag;
}
}
}
flag:
return 0;
}
十二、函数
函数:
i. 子程序(subroutine , routine , function , method , subprogram , callable unit)
ii.具有独立性
iii.一系列 C 语句的集合,能完成某个特定的功能
iv.需要该功能时 - 直接调用该函数
1.函数的分类
i. 库函数
ii.自定义函数
1、库函数
一些频繁、大量使用的函数
eg: printf() - 将信息按照一定的格式打印到屏幕上
为避免反复写同一个函数( 无用功 ) - 提高效率
将频繁、大量使用的函数存入一个名为 “库” 的文件中
使用时直接调用即可
- IO函数 printf scanf getchar putchar
- 字符串操作函数 strlen strcmp
- 内存操作函数 memset memcmp memcpy
- 时间 / 日期函数 time
- 数学函数 sqrt pow
- 其他库函数
如何学习库函数:
1.https://cplusplus.com/
2.https://en.cppreference.com/w/(英文)
https://zh.cppreference.com/w/(中文)
2、自定义函数
实现库函数所不能完成的任务
模拟实现库函数
- 自定义函数 - 求X + Y = ?
#define _CRT_SECURE_NO_WARNINGS 1
#include(stdio.h)
//类型int 函数名Add
int Add(int x, int y)//函数的参数 x,y - 参数的类型是int
{
return x + y;
}
int main()
{
int x = 0;
int y = 0;
scanf("%d", &x, &y);
int sum = Add(x, y);
printf("%d\n", sum);
return 0;
}
2.函数的参数
实际参数 - 实参:
1) 常量、变量、表达式、函数
2) 具有特定的值 - 传给形参
形式参数 - 形参:
1) 仅在函数调用的过程中实例化 (分配内存单元)
2) 函数调用结束后自动销毁
理解分析
- 写函数Swap( ) - 交换整数a 和 b 的值
错误分析
正确修改
3.函数的调用
传值调用:
i) 函数的形参和实参分别占不同的内存块
ii) 修改形参 - 不影响实参
传址调用:
i) 函数外部创建变量的内存地址传给函数参数
ii) 函数与函数外变量建立真正的联系
iii) 函数内部可直接操控外部变量
4.链式访问
function 1的返回值作为 function 2的参数
5.嵌套调用
6.函数的声明
1、告诉编译器 - 函数的返回类型 函数名(参数的返回类型)
2、函数 - 先声明后使用
3、声明一般放在头文件中
Eg:int Add(int x, int y); - 声明名为Add的函数
7.函数的定义
8.函数递归
函数调用自身的编程技巧
理解与分析1
- 按顺序打印输入的无符号数字
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
void print(unsigned int n)
{
if(n > 9)
{
print(n % 10);
}
printf("%u ", n % 10);
}
int main()
{
unsigned int input = 0;
scanf("%u", &n);
print(input);
return 0;
}
理解与分析2
- 求 n!
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int Fac(unsigned int n)
{
if (n <= 1)
{
return 1;
}
else
{
return n * Fac(n - 1);
}
}
int main()
{
unsigned int n = 0;
scanf("%u", &n);
int ret = Fac(n);
printf("%u", ret);
}
理解与分析3
- 求第n个斐波那契数
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int count = 0;
int Fib(int n)
{
if (n == 3)
{
count++;
}
if (n <= 2)
{
return 1;
}
else
{
return Fib(n - 1) + Fib(n - 2);
}
}
int main()
{
int n = 0;
scanf("%d", &n);
int ret = Fib(n);
printf("%d", ret);
printf("%d", count);
return 0;
}
通过对conut的统计 - 递归的思想求斐波那契数计算量很庞大 (可改进算法)
9.递归中的栈溢出
栈能使用的内存有限 (1M~8M)
每次创建函数参数都会占用栈区一部分 - 栈区满了就会溢出
1.不能死递归
a. 要有跳出条件
b. 每次递归逼近跳出条件
2.递归层次不能太深
十三、数组
一组相同类型元素的集合
一维数组
创建
数组元素类型 数组名[ ] = { 0 };
eg: int arr[] = { 0 };
初始化
char ch[ ] = “love”; - 根据后面内容初始化数组 - 字符串结尾隐藏了一个 \0
字符串
理解与分析
求数组元素个数
int sz = sizeof(arr) / sizeof(arr[0]);
访问数组
- 下标引用操作符 - 用下标访问数组
在内存中的存储
i.一维数组在内存中是连续存放的
ii.数组下标增长,地址由低到高变化
二维数组
数组的创建和初始化
数组的访问
在内存中的存储
1、二维数组在内存中是连续存放的
2、可以省行,不可省列
知道了列 - 才能知道第二行存在哪儿 - arr[ ][4]
数组名
数组名 - 数组首元素的地址
数组作为函数参数
- 数组的升序排列 - 冒泡排序
算法优化
变长数组
十四、操作符
十五、常见关键字
define -
include - 不是关键字 - 预处理指令
十六、define定义常量和宏
define 是一个预处理指令
1、define定义常量
#include<stdio.h>
#define MAX = 1000;
int main()
{
printf("%d", MAX);
return 0;
}
2、define定义宏
宏 - 替换
#include<stdio.h>
#define Add(X,Y) X+Y
#define add(X,Y) ((X)+(Y))
int main()
{
printf("%d", Add(2,3)); //- 5
//2 + 3 = 5
printf("%d", 4 * Add(2,3)); //- 11
//4 * 2 + 3 = 11
printf("%d",4 * add(2+3,3+3)); // - 44
//4*((2 + 3) + (3 + 3)) = 44
return 0;
}
宏:一种抽象 (Abstraction) - 根据预定义的规则 - 替换一定的文本模式
十七、指针 - 内存地址
指针 - pointer
内存 - 32位 / 64位的含义
变量在内存中的存储
int类型大小 - 4byte
指针变量
指针大小
• 指针大小是相同的
• 指针的空间 — depend on地址存储所需空间
- 32位 — 32bit — 4byte
- 64位 — 64bit — 8byte
十八、结构体
结构体类型 - 构造的数据类型 - 描述复杂对象
构造一个类型 - 描述学生
int类型 - 描述整型
结构体的使用
不苛求过去,满怀信心对待未来。