C语言大纲
C 语言概述
C语言的起源
C语言的起源在这里就不细讲了。总之是一门历史比较悠久的编程语言。也是语言的基石。
C语言的特点
优点:
代码量小,运行速度快,功能强大
缺点:
安全性不高。
开发周期长(因为是面向过程的语言,开发大型的应用不太方便)。
可移植性差(不能够任何机器上都可以跑,有点类似与汇编,每个不同的机器都有不同的汇编指令)
C语言的应用领域
C语言的应用领域比较广吧。
一般是系统的开发(我们所熟知的三大操作系统都是基于C语言来写的
(windoms,linux,unix三大常用的操作系统))。部分应用的开发,如游戏等等。
最主要的还是应用于系统软件。编写应用软件不是C语言的强项
C语言的重要性
C语言基本上算是现在流行语言的祖宗吧,是学习《数据结构》,《操作系统》,《java》的基础。特别是《数据结构》。
C语言还是连接电脑硬件的最有力的语言。重要性不言而喻。学计算机的如果C语言学不好那基本上不会走的很远,即使现在找到了工作,也是走不长远的。
C语言小例子练习来感受一下C语言的魅力
这是一个解一元二次方程的例子。直接输入方程的系数就可以进行求解。
# include<stdio.h>
# include<math.h>
int main(void)
{
int a ;
int b ;
int c ; //定义方程系数的三个变量
double middle;
double ra,rb;
printf("请输入方程系数...\n");
scanf("%d%d%d",&a,&b,&c);
middle = b*b-4*a*c;
if(middle>0)
{
//有两个解
ra=(-b+sqrt(middle))/(2*a);
rb=(-b-sqrt(middle))/(2*a);
printf("x1=%f,x2=%f\n", ra,rb);
}
else if(middle==0)
{
//有且只有一个解
ra=(-b)/(2*a);
rb=ra;
printf("x1=%f\n", ra);
}
else
{
//方程没有解
printf("方程没有解!\n");
}
return 0;
}
C语言编程预备知识
cup、内存、硬盘、显卡、显示器、主板之间的关系
这里我们用一个电影的例子来进行讲解
最开始的时候电影是在硬盘的上存放的>>>>>然后我们用鼠标进行点击给 cpu 发送指令>>>> cup给内存说我要看电影>>>>内存将电影从硬盘中调取出来>>>并给cup传送指令>>>>cup进行解析
是声音就通过声卡进行输出,是图片就通过显卡进行输出>>>>最后输出到显示器上或者通过喇叭进行传给用户直观的感受。
主板就是起一个连接的作用。相当于各个城市之间的道路一样。
HelloWord是如何运转起来的
我们在vc++6.0中运行C语言的时候都是需要先进性编译在进行连接最后再点击执行就可以在终端中看到我们程序的结果
实际上编译和链接是vc++6.0这个软件进行的,将通过编译后的文件生成一个可以执行的 .exe 后缀的文件。然后再通过操作系统去申请我们的cup去执行。然后才是我们的结果。
所以说不是单单只是软件的功能
数据类型
我们在这里只是简单的先介绍一下有一个概念。具体的类型会在后面进行讲解的
在数学中我们可以将现实生活中的数字分为: 整数,实数,复数等等。我们在C语言中也是这样差不多的进行分类。在C语言中我们大致的分一下类:
基础数据类型:
整型:
在整型中我们由分为长整型,短整型,和整型。
这样分的目的就是为了节省内存资源。
我们有时候一个数比如 3 只需要一个字节就能够进行存储。
但是如果用一个 long int 来进行存储的话当然是可以的,但是long int 它是占有8个字节的长整型。
存储一个一字节的数实在是浪费空间。
整型的分类
长整型: long int (8字节)
整型: int (4字节)
短整型: hort int (2字节)
浮点型的分类
单精度浮点型:float (4字节)
双精度浮点型:double (8字节)
字符
字符 char 只能用于存储一个字符,而不能存储字符串。在java、python、等等的语言中都是可以的,但是在C语言中是不行的。存储字符串的我们后面在讲解.
复合数据类型
所谓的复合数据类型就是将一些基本的数据类型进行拼接然后成为复合数据类型
结构体
枚举
共同体
我们后面将会重点讲解 **枚举**
进制
进制在我们生活中很常见。像我们说的 89, 90 等等是十进制数。
我们生活中使用的都是十进制数,逢十进一。我们说的几进制就是逢几进一,例如我们熟知的还有二进制,八进制,十六进制等等。这里就不详细的进行讲解了。后面会专门进行讲解的
十进制转化为R进制的方法
用十进制的数除以R进制,将余数从下到上排列,就行。和二进制的短除法差不多。
R进制转换为十进制的方法
从右到左依次是R的(0,1,2,3…n)次方
例如:(1100)二进制转换为十进制就是:
02^0 + 02^1 + 12^2 + 12^3
变量
变量是内存中的一段存储的空间。这里我们简单的通过一个程序进行讲解一下
# include<stdio.h>
int main(void)
{
int i=3;
printf("%d\n", i);
return 0;
}
我们定义了一个变量 i 并为它赋值为 3 。这个变量是存储在内存中的,我们定义一个变量的时候就是在内存中寻找空余的位置分配给它,然后记住它的地址。所以说变量就是内存中的一段空闲的空间
变量为什么必须初始化
这里说的初始化就是对变量进行赋值。我们先看看没有进行初始化的变量会输出什么。
# include<stdio.h>
int main(void)
{
int i;
printf("%d\n", i);
return i;
}
我们来看看程序的输出结果
-858993460
Press any key to continue
我们看到程序输出了很大的一串数字。重复执行几次程序结果都是一样的。这又是为什么呢?
前面我们讲解到,变量是内存中一段空闲的空间,我们在使用这段空闲的空间之前很有可能之前也有别的程序也使用过了,留下来了一些残留的垃圾数据。 但是如果是这样的话,这些数据应该是随 机的而不应该每次执行都是一样的结果啊!原来 vc++6.0 这个软件很早就发现了这个问题,于是在我们没有对变量进行初始化的时候提前的给变量所在的内存空间进行赋初值了,赋了一个 -8 开头的很大的数据,让我们看到后就知道是变量没有初始化。
垃圾数据
软件在运行之前会向操作系统申请存储空间。在内存空间足够的情况下,操作系统将会分配一段空闲的内存空间,并将外存中的软件拷贝一份放入内存空间中,并启动该程序的运行
在软件运行期间,该软件所占有的内存空间不再分配给其余的软件,而是自己一个人占有直至任务结束
当软件运行完毕后,操作系统将回收该内存空间(注意: 操作系统并不会清空该内存空间中遗留下来的数据),以便给别的软件分配空间。(这些遗留下来的数据我们叫做垃圾数据)
常量如何存储在计算机中的
整数是以补码的形式转换成为二进制代码存储在计算机中的
实数是以IEEE754标准转换成为二进制代码存储在计算机中的
字符存储的方式和整数差不多。在Ascll表中有对应字符的数字,然后再将数字的补码形式转换成为二进制代码存储在计算机中。例如:A 在ascll表中的值是65.然后再将65的补码转换成为二进制代码存储在计算机中。
这些标准讲解起来都比较麻烦。在后面我们会作为专题进行讲解。
浮点数的存错所带来的问题
-
说明
float 和 double 都不能够保证可以把所有的实数都准确的保存在计算机中。原因是因为计算机存储的格式来决定。因为计算机是通过补码的形式转化为二进制的文件进行存储的。所以转化的过程中可能会造成不准确。
例子:
# include<stdio.h> int main(void) { float i=99.9; printf("i=%f\n",i); return 0; } /* --------vc++6.0中运行的结果------------ i=99.900002 Press any key to continue --------------------------------------- 心得: 我们看到浮点数的 99.9 的后面不是五个0而是出现了一个2 所以说在计算机中实数不是能够完全正确无误的存储在计算机中的。 */
字节
字节是存储数据的单位。也是硬件所能够进行访问的最小的单位。前面我们说cup是从内存进行调用程序然后执行。在内存中程序的存储是一位一位的进行存储的。但是cpu不能够精确的访问到每一位(byte)。
而是只能够访问字节(bit)以上的单位。这里的 1字节(bit)=8位(btye)。现在我们说的32位64位也是这个意思。
我们下面来讲讲关于计算机计量单位的进位
1bit=8byte
1K=1024bit(字节)
1M=1024K
1G=1024M
1T=1024G
Ascll码
Ascll就是一种编码规则。只是说我们的C语言满足这个编码规则。这个编码规则将一些常见的字符转化成为数字然后再转化为二进制文件放在计算机中。
详细的讲解看这里 A scll
Char的讲解
我们来看一个代码
# include<stdio.h>
int main(void)
{
//char 字符是用来进行单个字符的而不是字符串的定义
char a='a'; //这是可以的,因为只是单个字符
char b="ABch"; //这是错误的。char定义的只是一个字符而不是字符串,将字符串赋值给char这是错误的
char c='ab'; //这是错误的。单引号只能够将单个字符括起来,而不能够将字符串括起来。
char d="A"; //这也是错误的。每个字符串默认会带上一个"\0"的终止符(后面我们会讲解终止符)。所以这个是一个字符串。
printf("%c\n",c);
return 0;
}
这里我们要注意的就是字符串后面会自动的加上一个 \0 的终止符。
原本是3个字符的会变为4个。
“” (双引号)用于将字符串括起来 。
‘’ (单引号)用于将字符括起来。
char只能够用于存储单个的字符而不是字符串。
字符的存储
字符的存储实质上就是和整数的存储时差不多的。通过Ascll表将字符转化成为一个整数,然后再将整数按照整数的方式进行存储。所以说字符的存储实际上和整数的存储时差不多的。
强制转化
-
格式
(数据类型)(表达式)
-
例子
/*
-----------2020/3/16(星期一)19:34--------------
*/
# include<stdio.h>
int main(void)
{
int i;
float k;
i =(int)(5.6+1.2); //将浮点型转换为整型
k = (float)(6); //将整型转换为浮点型
printf("i=%d,k=%f\n",i,k);
return 0;
}
/*
-------------vc++6.0中运行的结果---------------
i=6,k=6.000000
Press any key to continue
-----------------------------------------------
心得:
浮点数转化为整型得时候,是和除法一样直接将小数部分去掉。
整型转化为浮点数得时候,是直接在后面补零。
*/
三目运算符
-
格式
A ? B:C
等价于
if (A)
B
else
C
-
功能
满足A就执行B,不满足A就执行C
-
例子
/* ----------2020/3/16(星期一)21:50------------- 目的: 探究三目运算符 */ # include<stdio.h> int main(void) { (3>2)? printf("AAAA\n"):printf("BBBBB\n"); return 0; } /* ------------vc++6.0中运行的结果------------------ AAAA Press any key to continue ------------------------------------------------- */
逗号表达式
-
格式
(A,B,C,D)
-
功能
从左往右执行,最终表达式的值是最后一项的值。
-
例子
/* ----------2020/3/16(星期一)21:50------------- 目的: 探究逗号表达式 */ # include<stdio.h> int main(void) { int i=0; printf("最终的结果是%d\n",(i+6,i-5,i*7,i-9)); return 0; } /* ------------vc++6.0中运行的结果------------------ 最终的结果是-9 Press any key to continue ------------------------------------------------- 心得体会: 逗号表达式还是比较简单的,一瞬执行,最后返回一个值就行。 */
* 的几种含义
-
乘法
-
定义指针变量
char* p;
定义了一个名字叫做p的变量。char* 表示只能够存放char类型的地址
-
指针运算符
该运算符放在已经定义好的指针变量的前面
如果p是一个已经定义好了的指针变量
则*p 表示以p的内容为地址的变量
自增自减
分类
前自增 ++i
后自增 i++
前后自的异同
相同:
最终的结果都使变量值加 1
不同:
前自增整体表达式的值是 i+1 之后的值
后自增整体表达式的值是 i+1 之前的值
为什么会出现自增自减
代码更加的简洁
自增的速度更快
我们来比较一下几个自增的。
i = i+1 和 i+=1 这两个是等价的,运算速度是一致的。都是将数据从内存中读取到cup中的寄存器中,在由运算器进行运算。
i++ 可以直接在cup的寄存器中进行自增的运算,不需要从主存中调用数据。所以说速度更加的快一些。
学习自增需要明白的几个问题
我们应该尽量频闭掉前自增和后自增的区别。
自增表达式不要作为一个更大表达式的一部分使用.
或者说 i++ 或者 ++i 单独成为一个语句.
而不要用它作为一个完整语句的一部分来使用。如 int k = (i++) + (++i)+9+4。
下面我们来编写一个程序看一下。
-
在表达式中使用
/* ----------2020/3/16(星期一)21:19------------- 目的: 探究自增表达式 */ # include<stdio.h> int main(void) { int i=0; //自增变量1 int k=0; //存储前自增变量的结果 int j=0; //自增变量2 int m=0;//存储后自增变量的结果 k=++i + 6; m=j++ + 6; printf("k=%d,m=%d\n",k,m); printf("i=%d,j=%d\n",i,j); return 0; } /* ------------vc++6.0中运行的结果------------------ k=7,m=6 i=1,j=1 Press any key to continue ------------------------------------------------- 心得体会: 最后得 i j 还是一样得,只是在运算的时候有先后顺序,这就是为什么别把自增运算放入整体的表达式中,这样会造成结果不一样。 */
-
单独使用(1)
/* ----------2020/3/16(星期一)21:27------------- 目的: 探究自增表达式 */ # include<stdio.h> int main(void) { int i=0; //自增变量1 int j=0; //自增变量2 i++; j++; printf("i=%d,j=%d\n",i,j); return 0; } /* ------------vc++6.0中运行的结果------------------ i=1,j=1 Press any key to continue ------------------------------------------------- 心得体会: 单独使用就没有差异了,最后的结果是一样的。 */
-
单独使用(2)
/* ----------2020/3/16(星期一)21:27------------- 目的: 探究自增表达式 */ # include<stdio.h> int main(void) { int i=0; //自增变量1 int j=0; //自增变量2 int k=0; //结果1 int m=0; //结果2 k=i++; m=++i; printf("k=%d,m=%d\n",k,m); return 0; } /* ------------vc++6.0中运行的结果------------------ k=0,m=2 Press any key to continue ------------------------------------------------- 心得体会: k=0 先用 i 在+1 ,所以k=0 j 先+1 再使用i,原来i就是1的,再加1就是2了。所以j=2 */
-
表达式中使用(2)
/*
----------2020/3/16(星期一)21:27-------------
目的:
探究自增表达式
*/
# include<stdio.h>
int main(void)
{
int i=0; //自增变量1
int j=0; //自增变量2
int k=0;
int m=0;
k=i++ + 6;
m=++i + 6;
printf("k=%d,m=%d\n",k,m);
return 0;
}
/*
------------vc++6.0中运行的结果------------------
k=6,m=8
Press any key to continue
-------------------------------------------------
心得体会:
从这个结果我们可以看出 i++ ++i 和 + 的优先级的
++i > + > i++
*/
printf函数用法
直接输出在屏幕上
# include<stdio.h>
int main(void)
{
printf("你好世界!\n");
return 0;
}
/*
用于直接输出信息
*/
输出单个变量
# include<stdio.h>
int main(void)
{
int i=10;
printf("%d\n",i); //这里的%d是输出控制符,表示的是以十进制进行输出。
return 0;
}
输出两个变量
# include<stdio.h>
int main(void)
{
int i=10, j=20;
printf("%d %d\n",i,j);
//printf("%d%d",i,j);
return 0;
}
/*
两个输出控制符之间没有添加空格
------------输出结果-------------------
1020
---------------------------------------
两个输出控制符之间添加了空格
------------输出结果-------------------
10 20
---------------------------------------
总结:
我们可以看到在两个输出控制符之间如果没有添加空格的话,两个输出的数据是会连接在一起的。这样不方便与人交互。
*/
带有描述信息得输出
/*
-------------2020/3/13 21:13-------------
目的:探究printf的用法
*/
# include<stdio.h>
int main(void)
{
int i=10, j=20;
printf("i=%d,j=%d\n",i,j);
return 0;
}
/*
------------在vc++6.0中运行的结果-------
i=10,j=20
----------------------------------------
*/
scanf函数的讲解
为什么要使用输出控制符
在回答这个问题之前我们先来看看另外的一个东西。我们知道用键盘从终端上输入的都是字符或者是字符串,哪怕输入123它也是字符串而不是我们说的数字。所以我们就需要使用控制符转换成为我们需要的数据类型。所以我们需要使用输出控制符。
scanf(“输出控制符”,&变量) &是一个取地址的符号
# include<stdio.h>
int main(void)
{
int i;
printf("请输入...\n");
scanf("%d",&i);
printf("i = %d\n",i);
return 0;
/*
-----------vc++6.0中输出的结果------------------
请输入...
12mmmmmmm
i = 12
请输入...
12
i = 12
请输入...
mmm344343434
i = -858993460
心得:
为什么会出现这样得结果呢?原来C语言在执行输入得时候不满足输出控制符得数据都不会录入。所以第二个后面得mmmm就直接省略了。
而如果mmmm在前面得话,系统就会认为你的数据都是错得,就不会把值赋给变量。
------------------------------------------------
*/
}
scanf(“非输出控制符,输出控制符”,&变量)
# include<stdio.h>
int main(void)
{
int i;
printf("请输入...\n");
scanf(",%d",&i);
printf("i = %d\n",i);
return 0;
/*
-----------vc++6.0中输出的结果------------------
请输入...
12
i = -858993460
请输入...
,2
i = 2
请输入...
【】3
i = -858993460
心得:
为什么会出现这样得结果呢?
scanf函数中非输出控制符都要原封不动得输入,否则就会报错。
------------------------------------------------
*/
}
scanf(“输出控制符 输出控制符” ,&变量,&变量)
# include<stdio.h>
int main(void)
{
int i,j;
printf("请输入...\n");
scanf("%d,%d",&i,&j);
printf("i = %d,j=%d\n",i,j);
return 0;
/*
-----------vc++6.0中输出的结果------------------
请输入...
1,2
i = 1,j=2
请输入...
12
i = 12,j=-858993460
请输入...
1;2
i = 1,j=-858993460
心得:
这个和前面讲解的是一样的。任何的非输出控制符都要在输入的时候输入,不然很可能就会进行报错。
第二个输入就是没有在12之间输入非输出控制符导致系统将12认为是一个数赋值给了变量i, 而变量j没有赋值出现垃圾数据
所以说在使用scanf函数的时候不要轻易的添加非输出控制符,或者在前面给用户提示怎么输入数据。
------------------------------------------------
*/
}
输出控制符
为什么要使用输出控制符
我们知道在计算机中数据是以二进制的形式存储在计算机中的,但是01组成的代码既可以表示数据也可以表示指令。如果不用输出控制符变成我们想要的样子的话,很容易的造成误解。
如果01组成的代码表示的是数据的话,那么同样的 01 代码组合不同的输出格式就会有不同的输出结果。所以需要使用输出控制符
常见的输出控制符包含如下
%d --------------- int
%ld --------------- long int
%c --------------- char
%f --------------- float
%lf --------------- double
%x(或者%x 后者%#X) ------------整型(长短都可以)(用是十六进制输出)
%o ------------整型(长短都可以)(八进制输出)
%s ------------字符串
非进制输出控制符
代码演示
/*
----------------2020/3/13 21:45---------------
目的: 探究非进制控制输出符
*/
# include<stdio.h>
int main(void)
{
int i=10;
char a='a';
float b=2.123459789;
double c=3.123456789;
//%d 一般多用于int类型。表示用十进制进行输出
printf("%d\n",i);
//%ld 表示long int 类型
printf("%d\n",i);
//%c 用于输出字符串
printf("%c\n",a);
//%f 用于输出单精度浮点数
printf("%f\n" ,b);
//%lf用于输出双精度浮点数
printf("%lf\n" ,c);
return 0;
}
/*
--------------------vc++6.0上运行的结果------------------
10
10
a
2.123460
3.123457
---------------------------------------------------------
心得:
我们可以看到浮点数最多输出得是小数点后面六位,不论单精度还是双精度都是一样得。
超过了六位的浮点数从第七位四舍五入,无论第八位有多大,第七位后面的数已经与这个数无关了。只看第七位是否四舍五入
不足六位数的浮点数在后面补0
*/
进制的输出控制符
代码演示
/*
-----------------2020/3/13 22:35----------------
目的:
探究进制输出符的控制
*/
# include<stdio.h>
int main(void)
{
int i=30;
//用八进制输出
printf("%o\n",i); //输入的是小写的o
printf("%O\n",i); //输入的大写的O
printf("%0\n",i); //输入的数字零
printf("%#o\n",i); //输入的是井号加小写字母o
printf("%#O\n",i);
printf("----------------\n");
//十六进制输出
printf("%x\n",i); //输入的是小写x
printf("%X\n",i); //输入的是大写的X
printf("%#x\n",i); //输入的是井号加小写字母o
printf("%#X\n",i); //输入的是井号加小写字母o
return 0;
}
/*
---------vc++6.0中运行的结果--------------
36
O
036
O
----------------
1e
1E
0x1e
0X1E
------------------------------------------
心得:
在八进制中小写得o输出不带符号得八进制数。大写得O和数字零看起来差不多,但是输入大写得O出来得是一个零。而数字0出来的是空行
所以在八进制中只有小写得o能够输出数据,#o能够输出带符号得八进制数
在十六进制中大小写得x都可以输出数据。X输出得超过10得大写得字符。小写得就是小写的字符。#x带有符号的十六进制数。
在十六进制中大小写都可以输出而八进制只有小写可以输出的原因可能是因为十六进制中超过的10的部分是字母表示的。可以用大小写。而八进制都是数字无法用法大小写区分
*/
}
非法输入
什么是非法输入
# include<stdio.h>
int main(void)
{
int i,j,k;
char ch;
printf("请进行第一次输入...\n");
scanf("%d",&i);
printf("i=%d\n",i);
printf("----------------\n");
printf("请进行第二次输入....\n");
scanf("%d",&j);
printf("j=%d\n",j);
printf("----------------\n");
printf("请进行第三次输入....\n");
scanf("%d",&k);
printf("k=%d\n",k);
return 0;
/*
----------------vc++6.0中输出得结果-----------------------
请进行第一次输入...
123
i=123
----------------
请进行第二次输入....
456
j=456
----------------
请进行第三次输入....
789
k=789
-------------------------------------------------------------
请进行第一次输入...
123.。。。。
i=123
----------------
请进行第二次输入....
j=-858993460
----------------
请进行第三次输入....
k=-858993460
-------------------------------------------------------------
请进行第一次输入...
mmmmm
i=-858993460
----------------
请进行第二次输入....
j=-858993460
----------------
请进行第三次输入....
k=-858993460
-----------------------------------------------------------
心得:
我们看到只有第一次的输入是正确的。我们先来说说为什么要使用输出控制符。
我们从终端中输入的都是字符或者字符串,例如我们输入123,在我们看来是数字,但是在计算机看来就是字符,我们在python中也是的,使用的时候需要将字符转换成为相应的数据类型。
所以我们就是需要使用输出控制符将字符转化成为我们需要的数据类型进行输入。搞懂了为什么要使用输出控制符,我们再来回答上面的问题。
scanf函数在进行输入的时候,先对字符串进行读取,例如"123mmmm",读取的123符合%d的输入形式,所以就赋值给了i。但是后面的mmmm不符合%d的输入形式,继续留在系统给scanf分配的内存,等待在下一次的读取。结果后面再一次使用scanf函数的时候,率先从上面没有读完的地方继续进行读写,mmmm显然不符合%d所以初始化变量j失败,系统自