来福是只狗。德福是只猫。张三是个人。A 是一个整型变量。变量是我们计算机中用来存储数据的具体空间的指代,而类型是这个变量所对应的类型。
计算机存储数据的基本单位:字节
计算机表示数据的基本单位:位
表示数据方式:二进制
位权:十进制每高一位,位权是乘 10,二进制每高一位,位权是乘 2
整型类型占 4 个字节
这四个格子(字节),每个格子(字节)上面都有一串 16 进制的数字,那是这些格子(字节)的地址。并且这四个地址要是连续的。
int 类型的数据范围是:
为什么负数的范围要比正数多 1 呢?
因为计算机中,会把第一位当成符号位。当第一位为 1 的时候,表示为负数,为 0 的时候为正数。
1 的二进制:0001
0 的二进制:0000
-1 的二进制:1111
-2 的二进制:1110
-3 的二进制:1101
负数的补码等于正数的原码取反后再加 1
例如:1 的原码是:0001 那么-1 的补码就是:取反(1110)再加 1 --> 1111
补码表示法的好处:把十进制的减法,变成二进制的加法。
补码让我想到一个成语,否极泰来,泰卦在《易经》中排序第十一,否卦第十二,泰卦之前还有十个卦,从乾、坤开天辟地起,得历尽艰险、排除万难,才营造出泰的局面。而由泰到否,就不必这么麻烦,直接从云端一路狂泻跌入谷底。事物发展交替更换,由开始0到高峰0111(7),一下跌倒低谷1000(-8),当否到极致了(1111),再往前(0000)就开始转运了。
数据类型 | 描述 | 格式化占位符 | 存储范围 |
---|---|---|---|
char |
用于存储单个字符 | %c |
-128 到 127 或 0 到 255 |
int |
用于存储整数 | %d |
-32,768 到 32,767 或更大范围 |
float |
用于存储单精度浮点数 | %f |
约 ±3.4e−38 到 ±3.4e+38 |
double |
用于存储双精度浮点数 | %lf |
约 ±1.7e−308 到 ±1.7e+308 |
printf
printf
函数
stdio.h
int printf(const char *format,...);
format
:格式控制字符串...
可变参数列表1 |
|
scanf
scanf
函数:
stdio.h
int scanf(const char *format,...);
format
:格式控制字符串...
可变参数列表1 |
|
1 |
|
scanf
函数会以空格作为截断符号,scanf
会从第一个非空白符开始,一直往下读取,直到读取到第一个空白符开始截断。
1 |
|
%[a-z]
表示集合读入,允许读入的字母是从 a~z,当读入到第一个不在集合内的字符时,就会停止读入。
scanf
小技巧:scanf("%[^\n]", &s)
表示只要不是换行,就全部读入。
在字符串中,会有一个结束符’\0’,虽然 abc 看起来是三个字符,但因为还有一个结束符’\0’,所以其实它占四位。
sscanf
sscanf
作用是:从字符串中读入数据
scanf 其实是将字符串信息,转化为其他信息的一种方式。
1 |
|
sprintf
sprintf
作用是:输出数据到字符串中
sprintf 其实是将其他信息,转化为字符串信息的一种方式。
1 |
|
1 |
|
按位与&
:两个位都为 1 结果位才为 1,否则为 0
按位或|
:只要有一个位置为 1,结果位就为 1
1 |
|
按位异或^
:相应位置不同结果位则为 1,相同结果位为 0
异或运算又可以被理解为无进位相加,可用统计相应二进制位上 1 的奇偶性。
1 |
|
异或运算自己就是自己的逆运算: a^b = c
那么 a^c = b
,c^b = a
1 |
|
小问题:不新开变量,实现交换两个数的值
1 |
|
按位取反~
:将原来数二进制上的 1 变为 0,0 变为 1
补码 = 原码取反再加 1
1 |
|
左移位<<
将一个数的所有二进制位向左移动指定的位数,右边空出的位用0填充。左移运算的效果就是将原数乘以2的指定次方。
右移位>>
:将一个数的所有二进制位向右移动指定的位数,左边空出的位的填充方式通常是取决于原数的符号位。对于正数,左边空出的位用0填充;对于负数,左边空出的位用符号位的值填充。 右移运算的效果就是将原数除以2的指定次方。
但是要注意:/
是向 0 取整,>>
是向下取整。
1 |
|
函数 | 描述 | 头文件 |
---|---|---|
fabs(x) |
返回 x 的绝对值 | #include <math.h> |
sqrt(x) |
返回 x 的平方根 | #include <math.h> |
cbrt(x) |
返回 x 的立方根 | #include <math.h> |
pow(x, y) |
返回 x 的 y 次方 | #include <math.h> |
exp(x) |
返回 e 的 x 次方 | #include <math.h> |
log(x) |
返回 x 的自然对数(以e为底) | #include <math.h> |
log10(x) |
返回 x 的常用对数(以10为底) | #include <math.h> |
floor(x) |
返回不大于 x 的最大整数 | #include <math.h> |
ceil(x) |
返回不小于 x 的最小整数 | #include <math.h> |
round(x) |
返回最接近 x 的整数,四舍五入 | #include <math.h> |
fmod(x, y) |
返回 x 除以 y 的余数 | #include <math.h> |
remainder(x, y) |
返回 x 除以 y 的余数,保留符号 | #include <math.h> |
运算符 | 描述 |
---|---|
== |
相等 |
! |
真变假,假变真 |
< |
小于 |
> |
大于 |
<= |
小于等于 |
>= |
大于等于 |
&& |
逻辑与 |
|| |
逻辑或 |
1 |
linkely(x)
表示 X 经常成立,加载条件分支内部的代码。
unlinkely(x)
表示 X 经常不成立,加载条件分支外部的代码。
这叫逻辑值的归一化,什么意思?
比如:!!(5) = 1
不关 x 为和值,通过两次的非后!!
所有的真值都会变为 1,假值都会变成 0
if 语句的语法形式如下:
1 | if ( 表达式 ) |
下面我们通过一个样例,来理解一下上面的语法。
示例代码
输入一个整数,判断是否为奇数
1 |
|
如果一个数不是奇数,那就是偶数了,如果任意一个整数,我们要清楚的判断是奇数还是偶数怎么表示呢?
这里就需要if...else...
语句了,语法形式如下:
1 | if ( 表达式 ) |
示例代码
输入一个整数,判断是否为奇数,如果是奇数,就打印"奇数",否则打印"偶数"
1 |
|
示例代码
输入一个年龄,
>=
18岁就输出:“成年”,否则就输出:“未成年”
1 |
|
除了if
语句外,C语言还提供了switch
语句来实现分支结构。switch
语句是一种特殊形式的 if...else
结构,用于判断条件有多个结果的情况。它把多重的else if
改成更易用、可读性更好的形式。
语法格式
1 | switch (expression) { |
上面代码中,根据表达式expression
的运算结果,匹配不同case
中的value
的值,匹配成功,执行相应的case
分支。如果找不到对应的值,就执行default
分支。
注:
switch
后的expression
必须是整型表达式case
后的值,必须是整形常量表达式直接通过一组对比代码,来看看两者的区别
示例代码 - 输入任意一个整数值,计算除3之后的余数
if版本
- 如果使用if语句完成,如下:
1 |
|
switch版本
- 如果使用switch语句改写,就可以是这样的:
1 |
|
上述的代码中,我们要注意的点有:
case
和 后边的数字之间必须有空格case
语句中的代码执行完成后,需要加上break
,才能跳出这个switch语句。C语言提供了3种循环语句,while
就是其中一种,接下来就介绍一下while
语句。
while
语句的语法结构和if
语句非常相似。
1 | if(表达式) //如果if分支想包含更多的语句,可以加上大括号 |
为了快速说明问题,我们通过if
和while
的对比,先看看他们的共性和区别。
示例代码 - if
1 |
|
示例代码 - while
1 |
|
通过运行结果,能看出他们的区别,while语句是可以实现循环打印效果的。关于if
我们就不再浪费笔墨了,重点来看看while
。
首先需要说明的是:
while
本身就具有判断能力。即while
条件为真的时候,while
匹配的语句才会执行,这点和if
一致。所以,当while
条件不满足条件,while
匹配的代码块也就不会执行,比如:
示例代码:
1 |
|
其次需要说明的是:
while
条件满足的时候,执行完匹配的语句之后,要回到while
判断中,继续判断。不像if
语句,条件满足,执行完匹配的语句之后,会继续向后执行。这点是两者的不同。
示例代码:
在屏幕上打印 1~10 的值
1 |
|
运行结果
1 | 1 |
如果上述代码,把while
更换成为if
,代码块最多被执行一次,这里可以看出来,while
在条件满足的情况下,是会重复执行的。
结合上面的代码样例,我们总结一下while执行的步骤:
i <= 10
先要计算自己的逻辑结果(即:真或假)而上面的样例代码中,while (1)、while(0)
是上面的流程的特殊情况,相当于已经有了逻辑结果,直接判断即可。
说明一下while
语句的执行流程:
首先执行表达式,然后判断表达式的结果,表达式的结果值为0(假),循环判断直接结束;表达式的结果值不为0(真),循环判断条件满足,则执行循环语句,语句执行完后,再继续判断。
结论:
当while
中的表达式结果为真的时候,就要执行while
后续的语句,执行完毕之后,不像if
会运行后续代码,while
要重新回到()
括号中的表达式中,再次执行表达式,得到逻辑结果,判断逻辑结果是否为真,如果还是真,就继续重复上面所说的工作。如果为假,就结束while
。
如果我们想执行指定次数的循环,一般先要确认的就是循环条件。循环条件就是while(表达式)中圆括号中的表达式。一般而言,我们会首先定义一个计数器变量来跟踪循环的次数,每次循环开始执行时,计数器会增加1或者减少1,通过控制计数器,来控制循环次数。下面我们用代码进行一下简单说明
示例代码
求1-100的和是多少,并输出结果
1 |
|
运行结果
1 | [1-100] sum is: 5050 |
一般而言,我们要编写一个有限次数的循环,基本要有下面的循环三要素:
while循环也比较灵活,有时候会有很多缺省写法。
如果我们想写一个一直在运行的循环,不让其退出,那么只要想办法让它的循环条件一直满足即可,这种循环我们叫做死循环。
示例代码
1 |
|
运行结果
1 | dead loop! |
C99
已经支持bool
类型,所以如果你想写一个基于bool类型的死循环,可以采用如下方式
1 |
|
目前我们使用死循环的场景还是很少的,但是需要知道哦
语法形式
for
循环是三种循环中使用最多的,for
循环的语法形式如下:
1 | for(表达式1; 表达式2; 表达式3) // 注意,三个表达式中间用的是英文分号 |
for
循环如果满足条件的时候,也想执行多行语句,也强烈建议在for
之后带上{}
,限定代码块范围
for循环的执行流程:
表达式1
,初始化循环变量表达式2
的判断部分,表达式2
的结果如果==0,则循环结束;表达式2
的结果如果!=0则执行循环语句表达式3
,调整循环变量,然后再去表达式2
的地方执行判断,表达式2
的结果是否为0,决定循环是否继续。注意:
整个循环的过程中,表达式1初始化部分只被执行1次,剩下的就是表达式2、循环语句、表达式3在循环。
示例代码
在屏幕上打印1~10的值
1 |
|
结合上面的代码,可以看出来:
i=1;
表达式1
,循环变量的初始化i<=10;
表达式2
,循环结束条件的判断i++;
表达式3
,循环变量的调整其中表达式1只在最开始执行一次,剩下的都是表达式2和表达式3在进行控制
注意:
上面的例子,也是一次有限次数的for循环,我们就不在写重复的代码来进行说明了
for也支持死循环写法,而且更简单。在思路上,其实只要保证表达式2即循环结束条件永远为真就可以。类似:
1 |
|
for有更简洁的写法。
1 |
|
运行结果
1 | dead loop! |
没错,for循环中的3个表达式,支持省略写法
比较常见的省略除了上面的死循环,还有初始化部分省略,比如
如果我们已经把变量进行初始化了,就可以省略表达式
1
了
1 |
|
语法形式:在循环语句中do while
语句的使用最少,它的语法如下:
1 | do |
while
和for
这两种循环都是先判断,条件如果满足就进入循环,执行循环语句,如果不满足就跳出循环;
而do while
循环则是先直接进入循环体,执行循环语句,然后再执行while
后的判断表达式,表达式为真,就会进行下一次,表达式为假,则不再继续循环。
do while
内部的语句,至少会执行一次do while
的时候,使用{}
限定代码块范围下面我们用代码先进行一下do while语法的基本特征
示例代码
验证至少执行一次
1 |
|
运行结果
1 | do while execute at least once! |
do while
先执行,后判断while(表达式)
习惯写在}
的后面,不要忘记while(表达式)
后面的;
哦。当然,新启一行也是可以,如写在位置A
,不过不太推荐当然,do while
也是循环,也能实现while,for
的循环功能。
示例代码
验证
do while
循环功能题目要求: 在屏幕上打印1~10的值
1 |
|
通过上面的代码我们可以看出来
do while
也能实现循环效果do while
也有循环三要素哦我们几乎可以用对for,while
的理解,来理解do while
,三者共性较多,就不在花太多篇幅重复介绍了。
上面的循环形式,也是do while的有限次数的循环,和while,for一致。此处不再赘言。
下面我们用简单的流程图说明一下do while
的执行逻辑
do while也一定可以执行无限次数的循环 - 死循环。因为之前已经有过类似的介绍,这里以代码形式展示用法即可。
示例代码
1 |
|
运行结果
1 | dead loop! |
所以,do while的循环三要素也可以省略。
break
:跳出 switch 语句和最近一层的循环。
continue
:结束本次循环,继续下一次循环。
数学中我们其实就见过函数,比如:一次函数y=kx+b
,k
和b
都是常数,给一个任意的x
,就得到一个y
值。C语言也引入函数的概念。
C语言中的函数就是一个完成某项特定的任务的一小段代码 ,这段代码通常有特殊的语法和调用形式。
风格良好的C语言程序,应该是由无数个小的函数组合而成的。简单来说,比如一个大的计算任务可以被分解成若干个较小的函数(对应较小的任务),通过各个较小函数的组合调用,完成整体的计算任务。同时,如果一个函数能完成某项特定的、大家都需要的功能,我们就没必要重新写,直接用就行,即该函数被复用,从而在一定程度提升了软件开发的效率。
我们可以把函数想象成小型的一个加工厂,首先需要输入若干原材料,经过工厂加工才能生产出产品。那函数也是一样的,函数一般会输入一些值(可以是0
个,也可以是多个),经过函数内的计算,得出结果。
1 | 返回值 函数名(参数列表)函数体; |
😐 为什么一定要有函数这种结构?
形参的作用是用来接收实参的值,实参的作用是给形参传递值的。
1 | //形参 |
实参:李逵,形参:李鬼。形参的所有修改,都不会改变实参。
声明:说明函数的形式
定义:实现函数的内容
1 | //声明 |
函数定义在调用的前面时,不需要声明也能调用。如果函数的定义在调用的后面,需要声明一下。
递归函数:自己调用自己的函数。递归 = 递推 + 回归。
1 |
|