C语言概述
为什么要学习C语言
1)程序设计语言的发展:
第一代语言:机器语言(0,1)
第二代语言:汇编语言(add 1,2)
第三代语言:高级语言(结构化C-Fortran-Basic-Pascal、面向对象C+±Java-C#)a+b
2)C的特点
优点:代码量小、速度快、功能强大
缺点:危险性高、开发周期长、可移植性弱
3)应用领域
- 系统软件开发:
- 操作系统:Windows、Linux、Unix
- 驱动程序:主板驱动、显卡驱动、摄像头驱动
- 数据库:DB2、Oracle、Sql Server
- 应用软件开发:
- 办公软件:Wps
- 图形图像多媒体:ACDsee Photograph MediaPlayer
- 嵌入式软件开发:智能手机、掌上电脑
- 游戏开发:2D、3D游戏
C语言简介
第一讲:基本编程知识
-
CPU 内存条 硬盘 显卡 主板显示器 之间的关系 内容顺序:硬盘==>内容==>CPU==>结果(显卡、声卡) 主板提供一个整体框架 -
helloworld 程序如何运行起来的 编译-链接==>.exe文件==>操作系统运行 -
什么是数据类型 基本类型数据:
- 整数
- 整型(int)4字节
- 短整型(short int)2字节
- 长整型(long int)8字节
- 浮点数(实数)
- 单精度浮点数(float)4字节
- 双精度浮点数(double)8字节
- 字符(char)1字节
复合类型数据
-
什么是变量 在定义变量时,通过与内容条中的空闲地址进行链接,将赋值的内容放入地址中 变量的本质就是内存中的一段存储空间 -
cpu内存条 vc++6.0 操作系统 之间的关系 定义变量与内存条的存储空间产生关联,执行生成.exe后,利用操作系统进行运行 -
变量为什么必须要初始化 所谓初始化就是赋值的意思 注:
-
如何定义变量 ? 数据类型 变量名 = 要赋的值 等价于 ? 数据类型 变量名; ? 变量名 = 要赋的值 举例子 ? int i = 3; 等价于 int i; i = 3; ? int i, j; 等价于 int i; int j; ? int i, j=3; 等价于 int i; int j; j=3 ? int i = 3; j = 5 等价于 int i ; int j; i=3; j=5 ? int i, j; i=j=5; 等价于 int i, j; i =5; j=5; -
什么是进制 十进制就是逢十进一 D %d表示以十进制输出 二进制逢二进一 B 八进制 O %o表示以八进制输出 十六进制 H %x或%X表示以十六进制输出 注释:// /**/ [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cCjzjjnI-1627043706049)(C:\Users\98614\AppData\Roaming\Typora\typora-user-images\image-20210628161500212.png)] -
常量在C语言中是如何表示的
-
整数
- 十进制:传统的写法
- 十六进制:前面加0x或0X
- 八进制:前面0(数字零)
-
浮点数
-
字符
-
常量以什么样的二进制 编码 int i = 86; 整数以补码的形式转换为二进制代码存储在计算机中的实数是以IEEE754标准转化为二进制代码存储在计算机中的字符的本质实际也是与整数的存储方式相同。 -
代码规范化 缩进,一对括号, 函数后空格,运算符左右加空格,运算级别最低的地方敲空格 换行,功能相对独立,进行换行。 -
什么是字节 字节就是存储数据的单位,并且是硬件所能访问的最小单位。
1
字
节
=
8
位
1字 节=8位
1字节=8位;
1
k
=
1024
字
节
1k=1024字节
1k=1024字节;
1
M
=
1024
k
1M=1024k
1M=1024k;
1
G
=
1024
M
1G=1024M
1G=1024M -
不同类型数据之间相互赋值的问题 int i = 45;
long j = 102345;
i = j;
printf("%ld %d\n", i, j);
float x = 6.6;
double y = 8.8;
printf("%f %lf\n, x, y")
-
什么是ASCII ASCII不是一个值,而是一种规定。 ASCII规定了不同的字符是使用哪个整数值去表示 它规定了 ‘A’–65; ‘B’–66; ‘a’–97; ‘b’–98; ‘0’–48 ……… -
字符的存储[字符本质上与整数的存储方式相同] 基本的输入和输出函数的用法 printf() :将变量的内容输出到显示器上
- 四种用法:
printf("字符串\n") ;printf("输出控制符", 输出参数); // d是十进制,o是八进制,x十六进制printf("输出控制符1 输出控制符2...", 输出参数1,输出参数2...); // 输出控制符和输出参数的个数必须一一对应printf("输出控制符 非输出控制符,", 输出参数); // 输出控制符包含:%d %ld %c %f %lf %o %s %x(或者%X或者%#X) #表示显示字符关系- 为什么需要输出控制符:
- 01组成的代码可以表示数据也可以表示指令
- 如果01组成的代码表示数据的话,那么同样的01代码组合以不同的输出格式输出就会有不同的输出结果
scanf() :通过键盘将数据输入到变量中
- 两种用法:
-
用法一:scanf("输入控制符", 输入参数); 功能:将从键盘输入的字符转化为输入控制符所规定格式的数据,然后存入以输入参数的值为地址的变量中。//&i 表示i的地址 &是一个取地址运算符scanf("%d", &i); -
用法二:scanf("非输入控制符 输入控制符", 输入参数); 功能:将从键盘输入的字符转化为输入控制符所规定格式的数据,然后存入以输入参数的值为地址的变量中(非输入控制符必须原样输入) -
如何使用scanf 编写出高质量代码
-
使用scanf 之前最好先使用printf 提示用户以什么样的方式来输入 -
scanf 中尽量不要使用非输入控制符,尤其是不要用\n (非常不好的格式,不要加\n ) -
应该编写代码对用户的非法输入做适当的处理。 while ((ch=getchar()) != '\n')
continue;
-
scanf 中输入会逐个字节解析,如果存在问题,则停止解析后面的,直接赋值,再次解析时,从停止位置开始,完成解析过程。 -
代码注释 时间,功能,目的,输出结果,总结
第二讲:数据类型
第三讲:运算符和表达式
- 算术运算符: + - * / %
- 除法/的运算结果和运算对象的数据类型有关,两个数都是
int ,则商就是int ,若商有小数,则截取小数部分;被除数和除数中只要有一个或两个都是浮点型数据,则商也是浮点型,不截取小数部分。 - 取余%的运算对象必须是整数,结果是整除后的余数,其余数的符号与除数相同
- 关系运算符: > >= < <= !=(不等于) ==
- 逻辑运算符:!(非) &&(并且) ||(或)
- C语言对真假的处理:非零是真,零是假;真是1表示,假是0表示
- &&左边的表达式为假,右边的表达式不执行 (不含分号为表达式,含分号叫语句)
- ||左边的表达式为真,右边的表达式不执行
- 赋值运算符:= += -= *= /=
- 优先级别:算术>关系>逻辑>赋值
第四讲:流程控制
-
什么是流程控制 成都代码执行的顺序 -
流程控制的分类
第五讲:函数(面向过程)(重点)
为什么需要函数
什么叫函数
- 逻辑上:能够完成特定功能的独立的代码块
- 物理上:能够接受数据[当然也可以不接受数据];能够对接受的数据进行处理;能够将数据处理的结果返回[当然也可以不返回任何值];
- 总结:函数是一个工具,它是为了解决大量类似问题而设计的函数可以当做一个黑匣子
如何定义函数
函数的分类
-
有参函数和无参函数 -
有返回值和无返回值 -
库函数和用户自定义函数 -
值传递函数和地址传递函数 -
普通函数和主函数(main函数) 一个程序必须有且只有一个主函数 主函数可以调用普通函数,普通函数不能调用主函数 普通函数可以相互调用 主函数是程序的入口,也是程序的出口
注意的问题
- 函数调用和函数定义的顺序
- 如果函数调用写在了函数定义的前面,则必须加函数前置声明(如果被调函数的返回值是整型或字符型时,可以不对被调函数作说明,而直接调用)
- 函数前置声明的作用:
- 告诉编译器即将可能出现的若干个字母代表的是一个函数
- 告诉编译器即将可能出现的若干个字母所代表的函数的形参和返回值的具体情况
- 函数声明是一个语句,末尾必须加分号
- 对库函数的声明是通过# include <库函数所在的文件的名字.h>来实现的
- 形参和实参
- 如何在软件开发中合理的设计函数来解决实际问题
- 一个函数的功能尽量独立,单一
- 多学习,多模仿牛人的代码
- 函数是C语言的基本单位,类是Java,C#,C++的基本单位
常用的系统函数
- double sqrt(double x); 求的x的平方根
- int abs(int x); 求x的绝对值(整数)
- double fabs(double x); 求x的绝对值(浮点数)
专题:递归
第六讲:数组
为什么需要数组
? 为了解决大量同类型数据的存储和使用问题
? 为了模拟现实世界
数组的分类
-
一维数组
-
怎样定义一维数组
- 为n个变量连续分配存储空间
- 所有的变量数据类型必须相同
- 所有变量所占的字节大小必须相等
-
有关一维数组的操作
-
初始化
-
完全初始化 int a[5]={1,2,3,4,5}; -
不完全初始化,为被初始化的元素自动为零 int a[5]={1,2,3}; -
不初始化,所有元素是垃圾值 int a[5]; -
清零 int a[5]={0}; -
错误写法 int a[5];
a[5] = {1,2,3,4,5};
只有在定义数组的同时才可以整体赋值,其他情况下整体赋值都是错误的
a[5] = 100;
int a[5]={1,2,3,4,5}
int b[5];
如果要把a数组中的值全部复制给b数组
b = a; // 错误
正确写法
for(int i=0; i<5;i++)
b[i] = a[i]
-
赋值 -
排序 -
求最大值、最小值 -
倒置 -
查找 -
插入 -
删除 -
二维数组 int a[3][4]总共12个元素,可以当成3行4列看待,这12个元素的名字依次是 a[0][0] a[0][1] a[0][2] a[0][3] a[1][0] a[1][1] a[1][2] a[1][3] a[2][0] a[2][1] a[2][2] a[2][3] a[i][j]表示第i+1行,第j+1列的元素 int a[m][n]; 该二维数组右下角的元素位置只能是a[m-1][n-1] 初始化
-
int a[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12}; -
int a[3][4]={ ? {1,2,3,4}, ? {5,6,7,8}, ? {9,10,11,12} ? }; 输出二维数组内容: for (i=0; i<3; i++)
{
for (j=0; j<4; j++)
printf("%-5d ", a\[i][j]);
printf("\n");
}
-
多维数组 是否存在多维数组: 不存在,因为内存是线性一维的,n维数组可以当做每个元素是n-1维数组的一维数组 比如:int a[3][4]; 该数组是含有3个元素的一维数组,只不过每个元素都可以再分成4个小元素 int a[3][4][5]; 该数组是含有3个元素的一维数组,只不过每个元素都是4行5列的二维数组
第七讲:指针
指针的重要性
-
表示一些复杂的数据结构 -
快速的传递数据,减少了内存的消耗 -
使函数返回一个以上的值 -
能直接访问硬件 -
能够方便的处理字符串 -
是理解面向对象语言中引用的基础 总结:指针是C语言的灵魂
指针的定义
- 地址
- 内存单元的编号
- 从零开始的非负整数
- 范围:4G [0–4G-1] 32位
- 指针
- 指针就是地址,地址就是指针
- 指针变量就是存放内存单元编号的变量,或者说指针变量就是存放地址的变量
- 指针和指针变量事两个不同的概念
- 但是要注意:通常我们叙述时会把指针变量简称为指针,实际它们含义并不一样
- 指针的本质就是一个操作受限的非负整数
指针的分类
-
基本类型指针 int * p; // 定义p为指针变量
int i=3;
p = &i; //把i的地址赋给p
*p // 表示i,以p的内容为地址的变量
*的含义:
- 乘法
- 定义指针变量 int *p; // 定义了一个名字叫p的变量,int *表示p只能存放int变量的地址
- 指针运算符:
- 该运算符放在已经定义好的指针变量的前面
- 如果p是一个已经定义好的指针变量,则 *p表示以p的内容为地址的变量
如何通过被调函数修改主调函数普通变量的值
- 实参必须为该普通变量的地址
- 形参必须为指针变量
- 在被调函数中通过
*形参名 = 。。。 的方式就可以修改主调函数相关变量的值 -
指针和数组 指针和一维数组
-
数组名 一维数组名a
- 一维数组名是个指针常量
- 它存放的是一维数组第一个元素的地址
-
下标和指针的关系 如果p是个指针变量,则p[i]永远等价于 *(p+i) -
确定一个一维数组需要几个参数【如果一个函数要处理一个一维数组,则需要接受该数组的哪些信息】 需要两个参数:
-
指针变量的运算 指针变量不能相加,不能相乘,也不能相除 如果两个指针变量指向的是同一块连续空间中的不同存储单元,则这两个指针变量才可以相减 -
一个指针变量到底占几个字节 sizeof(数据类型/变量名)
假设p指向char类型变量(1个字节) 假设q指向int类型变量(4个节字) 假设r指向double类型变量(8个字节) p、q、r本身所占的字节数一样,都占4个字节。—4字节=32位,32根线控制地址,2的32次方组合结果,使用4个字节可以完全保存地址总线 总结:
- 一个指针变量,无论它指向的变量占几个字节,该指针变量本身只占四个字节
- 一个变量的地址使用该变量首字节的地址来表示
指针和二维数组 -
指针和函数 -
指针和结构体 -
多级指针 int i = 10;
int *p = &i;
int **q = &p;
int ***r = &q;
第八讲:变量的作用域和存储方式
分类:
- 按作用域分:全局变量和局部变量
- 全局变量:在所有函数外部定义的变量叫全局变量。
- 局部变量:在一个函数内部定义的变量或者函数的形参都统称为局部变量
- 按变量的存储方式:静态变量、自动变量和寄存器变量
- 静态存储方式:是指在程序运行期间分配固定的存储空间的方式。
动态存储方式:是在程序运行期间根据需要进行动态的分配存储空间的方式。 - 全局变量全部存放在静态存储区,在程序开始执行时给全局变量分配存储区,程序行完毕就释放。在程序执行过程中它们占据固定的存储单元,而不动态地进行分配和释放;
- 函数中的局部变量,如不专门声明为static存储类别,都是动态地分配存储空间的,数据存储在动态存储区中。函数中的形参和在函数中定义的变量(包括在复合语句中定义的变量),都属此类,在调用该函数时系统会给它们分配存储空间,在函数调用结束时就自动释放这些存储空间。这类局部变量称为自动变量。自动变量用关键字auto作存储类别的声明。
- 静态局部变量属于静态存储类别,在静态存储区内分配存储单元。在程序整个运行期间都不释放。而自动变量(即动态局部变量)属于动态存储类别,占动态存储空间,函数调用结束后即释放。
- 静态局部变量在编译时赋初值,即只赋初值一次;而对自动变量赋初值是在函数调用时进行,每调用一次函数重新给一次初值,相当于执行一次赋值语句。
- 如果在定义局部变量时不赋初值的话,则对静态局部变量来说,编译时自动赋初值0(对数值型变量)或空字符(对字符变量)。而对自动变量来说,如果不赋初值则它的值是一个不确定的值。
- 为了提高效率,C语言允许将局部变量得值放在CPU中的寄存器中,这种变量叫“寄存器变量”,用关键字register作声明。
- 只有局部自动变量和形式参数可以作为寄存器变量;
- 一个计算机系统中的寄存器数目有限,不能定义任意多个寄存器变量;
- 局部静态变量不能定义为寄存器变量。
注意的问题:
第九讲:扩展数据类型
第十讲:专题
动态内存分配
结构体
为什么需要结构体
- 为了表示一些复杂的事物,而普通的基本类型无法满足实际要求
声明叫结构体
- 把一些基本类型数据组合在一起形一个新的复合数据类型,这个就叫结构体
如何定义一个结构体
怎样使用结构体变量
-
赋值和初始化 定义的同时可以整体赋初值 如果定义完之后,则只能单个的赋初值 struct Student st = {80, 66.6, 'f'}; //初始化,定义的同时赋初值
struct Student st2;
st2.age = 10;
st2.score = 88;
st2.sex = 'f';
-
如何取出结构体变量中的每一个成员
-
结构体变量名.成员名 st.age;
st.score;
st.sex;
-
指针变量名->成员名 指针变量名->成员名 在计算机内部会被转化成(*指针变量名).成员名的方式来执行 所以说这两种方式是等价的 struct Student *pst = &st;
pst->age = 88;
pst->score = 66.6;
pst->sex = 'f';
pst->age在计算机内部会被转化为(*pst).age -
结构体变量的运算 结构体变量不能相加,不能相减,也不能相互乘除,但结构体变量可以相互赋值 -
结构体变量和结构体变量指针作为函数参数传递的问题 推荐使用结构体指针变量作为函数参数来传递
举例:动态构造存放学生信息的结构体数组
? 动态构造一个数组,存放学生的信息,然后按分数排序输出
枚举:
-
什么是枚举 把一个事物所有可能的取值一一列举出来 -
怎样使用枚举 -
枚举的优缺点 代码更安全 书写麻烦
进制:
- 逢n进一
- 十进制到其它进制,除以进制数,直至结果为0,余数倒输出。
- 其它进制到十进制,进制的次方相加
- 二进制<=>八进制:3位一体
- 二进制<=>十六进制:4位一体
- 八进制和十六进制:利用二进制或者十进制转化
补码:
链表:
-
算法:
-
通俗定义 解题的方法和步骤 -
狭义定义 对存储数据的操作 对不同的存储结构,要完成某一个功能所执行的操作是不一样的 比如: 要输出数组中所有的元素的操作和要输出链表中所有元素的操作肯定是不一样的 这说明:算法是依附于存储结构的,不同的存储结构,所执行的算法是不一样的 -
广义定义 广义的算法也叫泛型 无论数据是如何存储的,对该数据的操作都是一样的 我们至少可以通过两种结构来存储数据
-
数组
-
优点 存储速度很快 -
缺点 需要一个连续的很大的内存 插入和删除元素的效率很低 -
链表
-
专业术语 首节点
尾节点
头结点:
- 头结点的数据类型和首节点类型是一模一样的
- 头结点是首节点前面的那个节点
- 头结点并不存放有效数据
- 设置头结点的目的是为了方便对链表的操作
头指针
确定一个链表需要一个参数
-
优点 插入删除元素效率高 不需要一个连续的很大的内存 -
缺点 查找某个位置的元素效率低
位运算符:
-
&:按位与;&&逻辑与,也叫并且,&&与&的含义完全不同 -
|:按位或,||逻辑或 -
~:按位取反 -
^:按位异或,相同为零,不同为1 -
<<:按位左移,右边补零:左移n位相当于乘以2的n次方(前提是数据不能溢出) 面试题:
-
i = i*8; -
i = i<<3; ------------快 请问上述两个语句,哪个语句执行的速度快 -
>>:按位右移,左边一般补零(也可以补1);右移n位相当于除以2的n次方(前提是数据不能溢出) 通过位运算符可以对数据的操作精确到每一位 3. 空指针NULL NULL本质也是零,而这个零不代表数字零,而表示的是内存单元的编号零 我们计算机规定了,以零为编号的存储单元的内容不可读,不可写 int类型的变量所能存储的数字的范围是:
- int类型变量所能存储的最大正数十六进制表示是:7FFFFFFF
- int类型变量所能存储的绝对值最大的负整数用十六进制表示是:80000000
- 绝对值最小负数的二进制代码是:FFFFFFFF
链表:
-
算法:
-
通俗定义 解题的方法和步骤 -
狭义定义 对存储数据的操作 对不同的存储结构,要完成某一个功能所执行的操作是不一样的 比如: 要输出数组中所有的元素的操作和要输出链表中所有元素的操作肯定是不一样的 这说明:算法是依附于存储结构的,不同的存储结构,所执行的算法是不一样的 -
广义定义 广义的算法也叫泛型 无论数据是如何存储的,对该数据的操作都是一样的 我们至少可以通过两种结构来存储数据
-
数组
-
优点 存储速度很快 -
缺点 需要一个连续的很大的内存 插入和删除元素的效率很低 -
链表
-
专业术语 首节点
尾节点
头结点:
- 头结点的数据类型和首节点类型是一模一样的
- 头结点是首节点前面的那个节点
- 头结点并不存放有效数据
- 设置头结点的目的是为了方便对链表的操作
头指针
确定一个链表需要一个参数
-
优点 插入删除元素效率高 不需要一个连续的很大的内存 -
缺点 查找某个位置的元素效率低
位运算符:
-
&:按位与;&&逻辑与,也叫并且,&&与&的含义完全不同 -
|:按位或,||逻辑或 -
~:按位取反 -
^:按位异或,相同为零,不同为1 -
<<:按位左移,右边补零:左移n位相当于乘以2的n次方(前提是数据不能溢出) 面试题:
-
i = i*8; -
i = i<<3; ------------快 请问上述两个语句,哪个语句执行的速度快 -
>>:按位右移,左边一般补零(也可以补1);右移n位相当于除以2的n次方(前提是数据不能溢出) 通过位运算符可以对数据的操作精确到每一位
|