C语言
1.初识C语言
冯诺依曼架构
输入设备 | 运算器 | 控制器 | 输出设备 | 外部存储器 |
---|
C语言→早期不是很成熟→成熟→流行
国际标准
国际标准 | ANSIC | -C89(89年) |
---|---|---|
C90 | ||
C99/C11(没有流行起来,很多编译器不支持) |
- %c - 表示打印字符格式的数据
- %d - 打印整型十进制数据
- %f - 是打印小数,浮点数字
- %p - 以地址的形式打印
- %lf - 打印双精度小数 double
- %s - 打印字符串用%s
1.1程序框架
#include <stdio.h>
int main()
{
}
1.2输出
printf(“hello word!\n”);
""里面的内容叫做”字符串“,printf会把其中的内容原封不动地输出
\n表示需要在输出的结果后面换一行
终端命令下,
vi 文件名
输入i 底下显示insert,表示可输入
保存esc 冒号:wq 退出
//算法
#include <stdio.h>
int main()
{
printf("%d",12+6);
}
%d意思是我要让printf函数在这个地方给我填一个值进去,本来printf函数说,你给我什么我打印什么
%d呢,就不输出%d了,就是你后面有什么值我就填到这个位置上去,中间用逗号隔开。
1.3讨论
为什么看到有的书上的main()是void main(),而我们课件上是int main()?还有main()里面那句return 0;是必须的吗?
main()前面是函数类型;
void main()是无类型函数,不需要返回值;
int main()是整型类型函数;需要数值返回值0,即return 0
void main() 与 int main() 的区别是有无返回值,前者不需要返回值,后者一定要有返回值。如果使用void main,程序虽能编译、运行成功,却不利于操作系统判断其状态;int main() 里面的return 0是必需的。返回0代表程序正常执行成功,返回非0值代表程序异常结束
1.4讨论
常听到有人说他学的是Visual C,有人学的是Turbo C,不是我们学的什么Dev C,是他们的比我们的高级吗?还有GCC到底是什么?
都是一个编程语言的编译器只是平台不同,GCC 原本作为 GNU 操作系统的官方编译器,现已被大多数类 Unix 操作系统(如Linux、BSD、Mac OS X等)采纳为标准的编译器,GCC 同样适用于微软的Windows。GCC 是自由软件过程发展中的著名例子,由自由软件基金会以 GPL 协议发布。
1.5算找零
#include <stdio.h>
int main()
{
int price = 0;
printf("请输入金额(元):");
scanf("%d", &price);
int change = 100 - price;
printf("找您%d元。\n", change);
return 0;
}
需要:
1、有地方放输入的数字;2、有办法输入数字;
3、输入的数字能参与计算。
1.6两种写法
C99
普遍写法ANSI C
只能在代码开头的地方定义变量
1.7读整数
使用一个新的函数:scanf(“”);
scanf("%d",&zhao);//&读取的意思
关于scanf一定要小心里面的内容。
1.8常量VS变量
C99写法
#include <stdio.h>
int main()
{
const int JinE=100;//JinE 写成大写是因为规范,因为前面是const,如果不是const可以用小写。
int price = 0;
printf("请输入金额(元):");
scanf("%d", &price);
int change = JinE - price;
printf("找您%d元。\n", change);
return 0;
}
让用户自己输入票面,而不是固定值:
#include <stdio.h>
int main()
{
int JinE=100;
int price = 0;
printf("请输入金额(元):");
scanf("%d", &price);
printf("请输入票面:");
scanf("%d",&JinE);
int change = JinE - price;
printf("找您%d元。\n", change);
return 0;
}
定义两个整数相加:
int main()
{
int a;
int b;
printf("请输入两个正整数:");
scanf("%d %d",&a,&b);
printf("%d+%d=%d\n",a,b,a+b);
}
1.9英尺换算
美国人固执地使用英制计量单位,他们习惯用几尺几寸(英尺英寸)来报自己的身高。如果遇到一个美国人告诉你他是5英尺7,他的身高应该是一米几呢?
(5+7÷12)×0.3048=1.7018米
代码演示
#include <stdio.h>
int main()
{
printf("请分别输入身高的英尺和英寸\n""如输入\"5 7\"表示5英尺7英寸:");
double foot;
double inch;
scanf("%d %d",&foot,&inch);
printf("身高是%d米。\n",((foot+inch/12)*0.3048));
return 0;
}
运行效果
原因
//因为两个整数的运算的结果只能是整数
//10和10.0在C中是完全不同的数
//10.0是浮点数
代码测试
int main(){
printf("%d",10/3);
return 0;
}//**运行效果:3
运行效果:3
听人劝吃饱饭
带小数点的数值。浮点这个词的本意就是指小数点是浮动的,是计算机内部表达非整数(包含分数和无理数)的一种方式。另一种方式叫做定点数,不过在C语言中你不会遇到定点数。人们借用浮点数这个词来表达所有的带小数点的数。
换一种思路,用double方式输出
int main()
{
printf("请分别输入身高的英尺和英寸,"
"如输入\"5 7\"表示5英尺7英寸:");
double foot;
double inch;
scanf("%lf %lf", &foot, &inch);
printf("身高是%f米。\n",
((foot + inch / 12) * 0.3048));
return 0;
}
运行结果:
差异突出,c语言整数只能进行整数运算加零和不加零的结果
//无点零,结果是0.0000000
int main(){
printf("%lf",10/3*3);
return 0;
}
//加点零,结果正常为10
int main(){
printf("%lf",10.0/3*3);
return 0;
}
inch是定义为int类型的变量,如果把int换成double,我们就把它改为double类型的浮点数变量了。
double的意思是“双”,它本来是“双精度浮点数”的第一个单词,人们用来表示浮点数类型。除了double,还有float(意思就是浮点!)表示单精度浮点数。
2数据类型
- 整数
-
int
-
printf(“%d”);
-
scanf(“%d”);
- 带小数点的数
- double
- printf(“%lf”);
- scanf(“%f”);
2.1表达式
运算符
运算符(operator)是指进行运算的动作,比如加法运算符“+”,减法运算符
算子(operand)是指参与运算的值,这个值可能是常数,也可能是变量,还可能是一个方法的返回值
int main(){
int sides=4;
sides=7;
sides=sides+5;
printf("%d",sides);
return 0;
}
计算时间差
输入两个时间,每个时间分别输入小时和分钟的值,然后输出两个时间之间的差,也以几小时几分表示
int main()
{
int house1,fen;
int house2,fen2;
scanf("%d %d",&house1,&fen);
scanf("%d %d",&house2,&fen2);
int t1=(house1*60)+fen;
int t2=(house2*60)+fen2;
int t=t2-t1;
printf("时间差是%d小时%d分钟。",t/60,t%60);
return 0;
}
运行效果
house*60+fen—>转换为分钟为单位
t60——>小时部分;
t%60——>分钟部分
总结:我们做了这个程序,设计了四个变量,用两个scanf函数把他们读进来了。但是在计算的过程中,我们还需要更多的变量。我们需要去设计一些计算的方法,来计算它。程序,大概都是这个样子的。只不过,数据有多有 少,算法、计算的方法有复杂的有简单的。
单目运算符优先级最高
a* -b就是它会先去计算 -b 然后再去 *a这就是单目运算符
赋值运算符
- 赋值也是运算,也有结果
- a=6的结果是a被赋予的值,也就是6
- a=b=6 —>a=(b=6)
2.2dev调试
交换两个变量
- 如果已经有:
int a=6;
int b=5;
//如何交换a 、b两个变量的值?
首先肯定不是a=b b=a,这么简单,程序是理解不了的。这是我们人所理解的,计算机只能俺照步骤一步一步的来,如果我们写a= b, b=a 相当于把b原有的值赋值给了a, b还是原来的值。
如何解决
假如我们有三个杯子,两杯有水,一杯是空的,就可以进行互换两个杯子的水。所以我们需要有三个变量。
#include <stdio.h>
int main() {
int a=5;
int b=6;
int t;
t=a;
a=b;
b=t;
printf("a=%d,b=%d",a,b);
return 0;
}
运行效果
2.21调试
先打断点,单击数列行,会出现以下效果
2.3复合赋值和递增递减
2.31复合赋值
2.32递增递减运算符
2.33前缀后缀
++和--可以放在变量的前面,叫做前缀形式,也可以放在变量的后面,叫做后缀形式。
a++的值是a加l以前的值,而++a的值是加了l以后的值,无论哪个,a自己的值都加了l了。
#include <stdio.h>
int main()
{
int a=10;
printf("a++=%d\n",a++);
printf("a=%d\n",a);
printf("++a=%d\n",++a);
printf("a=%d\n",a);
return 0;
}
运行结果
2.34 ++ ——
这两个运算符有其历史来源
可以单独使用,但是不要组合进表达式
++i++ -->?
i++++ -->?
a = b+=c++-d+--e/-f
先单目运算,从右往左,先看最后一个等号后面的表达式
-f单目取负 --e=e-1之后的值 -d单目取负 c++=c+1之前的值
用A表示:c-d+(e-1)/(-f)
再看第二个等号的表达式——b+=A,即b=b+A
最后a=b=b+A
该表达式中有两个优先级为1的运算,即-f和c++,结合关系自右向左,然后是优先级为2的–,再然后是优先级为4的运算符/,再然后是-和+,自左向右,再然后是=和+=,自右向左,我的头发有没有变少。
找几个数试试,让a=8,b=19,c=3,d=88,e=99,f=23,表达式算完后应该是f=23,c=4,e=98,d=88,b=-70,a=-70,写个程序跑一跑,确实如此。
逆序的三位数:
程序每次读入一个正三位数,然后输出逆序的数字。注意,当输入的数字含有结尾的0时,输出不应带有前导的0。比如输入700,输出应该是7。
提示:用%10可以得到个位数,用/100可以得到百位数...。将这样得到的三个数字合起来:百位*100+十位*10+个位,就得到了结果。
注意:除了题目要求的输出,不能输出任何其他内容,比如输入时的提示,输出时的说明等等都不能。这道题目要求输出逆序的数字,程序就只能输出这个数字,除此之外任何内容都不能输出。
输入格式:
每个测试是一个3位的正整数。
输出格式:
输出逆序的数。
输入样例:
123
输出样例:
321
#include<stdio.h>
int main()
{
int num =0;
scanf("%d", &num);
int a = 0;
int b = 0;
int c = 0;
int d = 0;
a = num % 10;//取出个位数
b = num/ 10 % 10;//取出十位数
c = num / 100;//取出百位数
d = a * 100 + b * 10 + c;
printf("%d", d);
return 0;
}
3判断
3.1做判断if语句
3.2做比较,判断的条件
C语言提供了六个关系运算符:
- == 相等
- != 不相等
- > 大于
- >= 大于或等于
- < 小于
- <= 小于或等于
注意其中有两个字符的运算符:==、>=和<=的两个字符必须紧紧连在一起,中间不能插入空格。
关系运算的结果是一个逻辑值,逻辑值只有两种可能的值:true(真,表示成立)或false(假,表示不成立)。当两个值的关系符合关系运算符的预期时,关系运算的结果为true,否则为false。
关系运算的结果
当两个值的关系符合关系运算符的预期时,关系运算的结果为整数l,否则为整数0
#include<stdio.h>
int main()
{
printf("%d\n",5==3);
printf("%d\n",5>3);
printf("%d\n",5<=3);
return 0;
}
优先级
所有的关系运算符的优先级比算术运算的低,但是比赋值运算的高
7>=3+4
int r=a>0
判断是否相等的==和!=的优先级比其他的低,而连续的关系运算是从左到右进行的
5 >3 ==6 >4
6 >5 >4
a ==b ==6
a ==b >0
找零计算器
找零计算器需要用户做两个操作:输入购买的金额,输入支付的票面,而找零计算器则根据用户的输入做出相应的动作:计算并打印找零,或告知用户余额不足以购买。
从计算机程序的角度看,这就是意味着程序需要读用户的两个输入,然后进行一些计算和判断,最后输出结果。
/ /注释
#include<stdio.h>
int main(){
//初始化
int m=0;
int z=0;
//读入金额和票面
printf("请输入金额:\n");
scanf("%d",&&m);
printf("请输入票面:\n");
scanf("%d",&z);
//计算找零
printf("找你%d元钱",z-m);
return 0;
}
#include<stdio.h>
int main(){
//初始化
int m=0;
int z=0;
//读入金额和票面
printf("请输入金额:\n");
scanf("%d",&m);
printf("请输入票面:\n");
scanf("%d",&z);
//计算找零
if(z>=m){
printf("找你%d元钱",z-m);
}else{
printf("票面不够!!!");
}
return 0;
}
运行效果
3.3流程图画法
#include<stdio.h>
int main()
{
const int MINOR =35;
int age=0;
printf("请输入你的年龄:\n");
scanf("%d",&age);
printf("你的年龄是%d\n",age);
if(age<MINOR){
printf("年轻是美好的!");
}
printf("年龄决定了你的精神世界,好好珍惜吧。\n");
return 0;
}
3.4 if和else
#include<stdio.h>
int main(){
int a,b;
printf("请输入两个整数:\n");
scanf("%d%d",&a,&b);
int max=0;
if(a>b){
max=a;
} else{
max=b;
}
printf("大的那的是%d",max);
return 0;
}
3.5 if语句再探:if和else后面也可以没有{}而是一条语句
一个基本的if语句由一个关键字if开头,跟上在括号里的一个表示条件的逻辑表达式,然后是一对大括号“o”之间的若干条语句。如果表示条件的逻辑表达式的结果不是零,那么就执行后面跟着的这对大括号中的语句,否则就跳过这些语句不执行,而继续下面的其他语句。
#include<stdio.h>
int main(){
const int PASS=60;
int score;
printf("请输入你的成绩:");
scanf("%d",&score);
if(score<PASS)
printf("很遗憾,你的成绩不及格\n");
else
printf("恭喜你,你及格了!\n");
printf("再见!");
return 0;
}
#include <stdio.h>
int main()
{
const double RATE = 8.25;
const int STANDARD = 40;
double pay = 0.0;
int hours;
printf("请输入工作的小时数: ");
scanf("%d", &hours);
printf("\n");
if (hours > STANDARD)
pay = STANDARD * RATE +
(hours-STANDARD) * (RATE * 1.5);
else
pay = hours * RATE;
printf("应付工资: %f\n", pay);
return 0;
}
3.6循环
while语句是一个循环语句,它会首先判断一个条件是否满足,如果条件满足,则执行后面紧跟着的语句或语句括号,然后再次判断条件是否满足,如果条件满足则再次执行,直到条件不满足为止。后面紧跟的语句或语句括号,就是循环体。
do-while循环和while循环很像,唯一的区别是我们在循环体执行结束的时候才来判断条件。也就是说,无论如何,循环都会执行至少一遍,然后再来判断条件。与while循环相同的是,条件满足时执行循环,条件不满足时结束循环。
数数几位数
程序要读入一个4位以下(含4位)的正整数,然后输出这个整数的位数。
如:输入:352,输出:3
因为题目明确了4位数及以下的正整数,所以可以简化一些判断
#include <stdio.h>
int main(){
int x;
int n=1;
scanf("%d",&x);
if(x>999){
n=4;
}else if(x>99){
n=3;
}else if(x>9){
n=2;
}
printf("%d",n);
return 0;
}
3.7whlie循环
问题:任意范围的正整数怎么办?
#include <stdio.h>
int main(){
int a;
int b=0;
scanf("%d",&a);
b++;//其实可以不要
a=a/10;//其实可以不要
while(a>0){
b++;
a/=10;
}
printf("这是一个%d位数",b);
return 0;
}
if是一次性的while是一直性的,while循环可能一次也没有循环
如果我们把while翻译作“当”,那么一个while循环的意思就是:当条件满足时,不断地重复循环体内的语句。
循环执行之前判断是否继续循环,所以有可能循环一次也没有被执行;
条件成立是循环继续的条件。
人脑模拟计算机的运行,在纸上列出所有的变量,随着程序的进展不断重新计算变量的值。当程序运行结束时,留在表格最下面的就是程序的最终结果
a | b |
---|---|
352 | 0 |
35 | 1 |
3 | 2 |
0 | 3 |
#include <stdio.h>
int main(){
int a;
int b=0;
scanf("%d",&a);
// b++;
// a=a/10;
while(a>0){
b++;
a/=10;
}
printf("这是一个%d位数",b);
return 0;
}
如果是零怎么办?理论上0应该不算正整数,但是我们就是想处理他,我们需要怎么做?
a | b |
---|---|
0 | 0 |
#include <stdio.h>
int main(){
int a;
int b=0;
scanf("%d",&a);
b++;
a=a/10;
while(a>0){
b++;
a/=10;
}
printf("这是一个%d位数",b);
return 0;
}
我们还是按照刚开始的写法可以解决掉这个问题,因为b一开始b++了所以他有应该1在那。除了这个我们还有一种方法,我们开始就先用if判断一下,如果我们输入的数大于0,就走while循环,否者就else返回
#include <stdio.h>
int main() {
int a;
int b=0;
scanf("%d",&a);
// b++;
// a=a/10;
if(a>0) {
while(a>0) {
b++;
a/=10;
}
} else {
b=1;
}
// while(a>0){
// b++;
// a/=10;
// }
printf("这是一个%d位数",b);
return 0;
}
3.7.1手动模拟电脑运算
就有时候没有电脑,或者有些代码等,我们可以手动写出代码变量的变化。先写出所有变量,a和b,然后开始执行
a | b |
---|---|
352 | 0 |
35 | 1 |
3 | 2 |
0 | 3 |
其实我们除了手动之外我们还可以在程序适当的地方插入printf函数来输出变量的内容。这样呢,我们并不需要启动那个dbug依然能知道变量的变化 |
#include <stdio.h>
int main() {
int a=352;
int b=0;
// scanf("%d",&a);
// b++;
// a=a/10;
while(a>0){
b++;
a/=10;
printf("a=%d,b=%d\n",a,b);
}
printf("这是一个%d位数",b);
return 0;
}
效果图
3.8 do - while循环
在进入循环的时候不做检查,而是在执行完一轮循环体的代码之后,再来检查循环的条件是否满足,如果满足则继续下一轮循环,不满足则结束循环
do
{
<循环体语句>
}whlie(<循环条件>);
两种循环
do-while循环和while循环很像,区别是在循环体执行结束的时候才来判断条件。也就是说,无论如何,循环都会执行至少一遍,然后再来判断条件。与while循环相同的是,条件满足时执行循环,条件不满足时结束循环。
#include <stdio.h>
int main() {
int x;
int n=0;
scanf("%d",&x);
do {
x/=10;
n++;
} while(x>0);
printf("%d",n);
return 0;
}
3.9 阶乘 for循环
阶乘
n! = 1×2×3×4×...n
写一个程序,让用户输入r,然后计算输出n!
while循环
/*思路:
显然读用户的输入需要一个int的n,然后计算的结果需要用一个变量保存,可以是int的factor,在计算中需要有一个变量不断地从1递增到n,那可以是int的i
*/
#include <stdio.h>
int main() {
int n;
scanf("%d",&n);
int fact=1;
int i=1;
while(i<=n) {
fact*=i;
i++;
}
printf("%d!=%d",n,fact);
return 0;
}
for循环
#include <stdio.h>
int main() {
int n;
scanf("%d",&n);
int fact=1;
int i=1;
for(i=1; i<=n; i++) {
fact*=i;
//i=1 初始条件
//i<=n;循环继续的条件
//i++ 循环每一轮要做的动作
}
printf("%d!=%d",n,fact);
return 0;
}
换个思维模式,倒过来写
#include <stdio.h>
int main(){
int n;
scanf("%d",&n);
int fact=1;
int i=1;
for(i=n;i>1;i--){
fact*=i;
}
printf("%d!=%d",n,fact);
return 0;
}
for循环像一个计数循环:设定一个计数器,初始化它,然后在计数器到达某值之前,重复执行循环体,而每执行一轮循环,计数器值以一定步进进行调整,比如加1或者减1
for = 对于
for ( count=l0; count>0; count-- )
就读成:“对于一开始的count=10,当count>0时,重复做循环体,每一轮循环在做完循环体内语句后,使得count–。”先做循环体,再count——
小套路
做求和的程序时,记录结果的变量应该初始化为0,而做求积的变量时,记录结果的变量应该初始化为l
循环次数
for ( i=0; i<n; i++ )
则循环的次数是n,而循环结束以后,i的值是n。循环的控制变量i,是选择从O开始还是从1开始,是判断i<n还是判断i<=n,对循环的次数,循环结束后变量的值都有影响
#include <stdio.h>
int main(){
int i;
for(i=0;i<5;i++){
printf("i=%d\n",i);
}
printf("\n最后的i是%d",i);
return 0;
}
运行效果
for 等价与 while
任何的一个for循环都可以改成while循环。
for循环的每一个表达式都可以省略,但是分号不能省略,因为分号是表达区分这是什么意思的。for(;条件) == while(条件)
如何选择循环
如果有固定次数,用for
如果必须执行一次,用do_while
其他情况用while
4.0 进一步的判断和循环
4.1 逻辑类型和运算
4.1.1 逻辑类型
bool
#include <stdbool.h>
之后就可以使用bool和true、false
实际上是没有这个bool类型,但是可以加个头文件#include <stdbool.h>就可以使用了。但实际上,并没有什么好的方式进行输出,我们还是以整型的方式%d来输出。
4.1.2 逻辑运算
逻辑运算是对逻辑量进行的运算,结果只有0或1
逻辑量是关系运算或逻辑运算的结果
4.1.3 条件运算和逗号运算
4.2 级联和嵌套的判断
4.2.1 嵌套的if-else
4.2.2 级联的if-else
#define _CRT_SECURE_NO_WARNINGS 1 //加在源文件的第一行,可以忽略警告信息。
#include <stdio.h>
int main(){
int num1=0;
int num2=0;
int sum=0;
scanf("%d%d",&num1,&num2);
sum= num1+num2;
printf("sum=%d\n",sum);
return 0;
}
//scanf
//strcpy
//strlen
//strcat
//.....
//传统意义都是不安全的
2.变量&常量
2.1 字面变量
int main() {
//const -是常属性,加了它之后,就变成了常变量,不能更改
// 常变量就像一个中国人拿个美国绿卡,但本质上他还是中国人,也就是说,加上了const就是常变量,但本质上num是一个变量
const int num=4;
printf("%d\n",num);
num=8;
printf("%d\n",num);
//字面常量
// 3;
// 4;
// 3.14;
return 0;
2.2 const修饰的常变量
int main(){
const int n=10;
int arr[n]={0};
return 0;
}
//运行发生错误
//error: variable-sized object may not be initialized
//错误原因,int arr[n]={0};应输入常量表达式int arr[10]={0};
//所以n是变量,但又有常属性所以我们说n是常变量。
2.3 #define定义的标识符常量
#include <stdio.h>
#define MAX 10 //define定义 MAX是名字
int main(){
int arr[MAX]={0}; //定义了一个数组,所以,#define MAX 10是常量
printf("%d\n",MAX);//打印MAX
return 0;
}
2.4 枚举常量
//什么是枚举--就是一一列举
//比如:性别、男、女、保密
//三原色:红、黄、蓝
//星期:1、2、3、4、5、6、7
//枚举关键字 enum
enum Sex{
MALE,
FEMALE,
SECRET
};
//MALE,FEMALE,SECRET -枚举常量
int main(){
printf("%d\n",MALE);//0
printf("%d\n",FEMALE);//1
printf("%d\n",SECRET);//2
return 0;
}
枚举变量是有固定的值的,定义在enum XXX大括号里面的值叫枚举常量
打出来的值MALE0、FEMALE1、SECRET==2
字符串&转义字符&注释
1、字符串
int main(){
"abcdef";//字符串
"hello";//字符串
"";//空字符串
return 0;
}
这种由双引号( Double Quote )引起来的一串字符称为字符串字面值(String Literal ),或者简称字符串。
注∶字符串的结束标志是一个\o的转义字符。在计算字符串长度的时候\o是结束标志,不算作字符串内容。
打印字符串用 %S
2、数组&字符串
#include <stdio.h>
int main(){
char arr1[]="abc";//数组
printf("%s\n", arr1);//打印字符串用%s
return 0;
}
#include <stdio.h>
int main(){
char arr1[]="abc";//数组
char arr2[]={'a','b','c'};
//数组用大括号初始化,由单引号引起的叫字符a,多个字符放在一起用双引号叫字符串
printf("%s\n", arr1);//打印字符串用%s
printf("%s\n",arr2);
return 0;
}
版本不一样,可能有变化
解决办法,arr2数组后面填个0,可以解决,‘\0’表示字符串结束的标志。
#include <stdio.h>
int main(){
char arr1[]="abc";//数组
char arr2[]={'a','b','c',0};// 0或者 \0都可以
// char arr2[]={'a','b','c','\0'};
printf("%s\n", arr1);//打印字符串用%s
printf("%s\n",arr2);
return 0;
}
ASCII 编码 a–97 A–65 ASCII码值
//#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include<cstring>
int main(){
char arr1[]="abc";
char arr2[]={'a','b','c'};
printf("%d\n",strlen(arr1));//strlen()--string length-计算字符串长度的
printf("%d\n",strlen(arr2));
return 0;
}
3、转义字符
转义字符
转变原来的意思
int main(){
printf("abcn");
return 0;
}
int main(){
printf("abc\n");
return 0;
}
int main(){
printf("c:\test\33\hello.c");//想打印这个地址
return 0;
}
\t - 水平制表符
斜杆 \ 也可以转义斜杆 \ 让他变成一个普通的斜杆 转义字符斜杆 \
int main(){
printf("%c,\n",'a');//%c 打印一个字符
return 0;
}
int main(){
printf("%d\n" ,strlen("c:\test\32\test.c"));
return 0;
}
strlen是打印字符串长度,运行结果为 - 13 why?
c:\test\32\test.c
c : \t e s t \32 \t e s t . c
按照这样排序就是13个
其实\32 – 32是两个八进制数字
32作为8进制代表的那个十进制数字,作为ASCII码值,对应的字符
32 – > 10进制 26 -->作为ASCII码值代表的字符
是一个 **→ **箭头
\ 后面直接跟数字就是八进制
\ 后面跟x加数字是表示十六进制数字
循环语句
1、if循环
int main() {
int input=0;
printf("你要干嘛?\n");
printf("吃饭?(1/0)>:");// >:提示语句
scanf("%d",&input);
if(input==1)
printf("牛逼");
else
printf("牛逼plus");
return 0;
}
int main(){
int line=0;
printf("开始吃饭\n");
while(line<1000){
printf("吃一口");
line++;
}
printf("撑死了");
return 0;
}
函数
数组
int main(){
int arr[10]={1,2,3,4,5,6,7,8,9,10};//定义一个存放10个整数数字的数组 =赋值
//下标从0开始 最大的下标n-1
char ch[12];
float fl[5];
printf("%d\n",arr[4]); //打印出来的数字是5 下标的方式访问
return 0;
}
打印所有下标
int main(){
int i=0;
int arr[10]={1,2,3,4,5,6,7,8,9,10};//定义一个存放10个整数数字的数组 =赋值
//下标从0开始
char ch[12];
float fl[5];
while(i<10){
printf("%d",arr[i]);
i++;
}
//printf("%d\n",arr[4]);
return 0;
}
操作符
int main(){
int a=5%2;
// int a=5/2;
// printf("%d",a);//取商 结果是2
printf("%d",a);//取模 结果是1
return 0;
}
移位操作符
位操作
int main(){
//(2进制)位操作
//& 按位与 并且的意思
// | 按位或
// ^ 按位异或
return 0;
}
一真一假为假,c语言0为假,1为真,只有两个1也就是两个真才是真
运行结果是1
或就是,只要有一个真就行了
\ ^ 按位异或
异或的计算规律是:
对应的二进制位相同,则为0;
对应的二进制位相异,则为1;
int main()
{
int a=10;
a=20;// =赋值 ==判断相等
a=a+10;//1
a+=10; //2 他们两个完全等价 2是1的简写
a=a-10;
a-=10;
a=a&10;
a&=10;
//复合赋值符
//+= -= &= |= *=
return 0;
}
单目操作符
双目操作符
int main(){
int a=10;
int b=15;
a+b;// + 双目操作符
return 0;
}
三目操作符
关系操作符
王道
一、C语言的那些事
原来C语言的编译器是B语言写的,C语言为什么叫C语言,因为先有的A语言(ALGOL 60 简称A语言)后来它经过简化变为BCPL语言(改进后称为B语言),而C语言是在B语言的基础之上发展而来的,所以就称为C语言。
主流的编译器是GCC编译器
1972年,Dennis M. Ritchie在B语言的基础上最终设计出了一种新的语言,他以BCPL 的第二个字母作为这种语言的名字,这就是C语言。
1.2程序的作用是什么
程序的作用是帮我们完成某种计算。
#include <stdio.h>//头文件
int main() {//入口函数
printf("Hello, World!\n");//函数内的代码内容
return 0;//函数的返回值
}
说明: :main是主函数名, int是函数返回值类型。每个C程序有且只能有一个主函数 main,程序从main函数开始执行。花括号{ }是函数开始和结束的标志,不可省略。每个C语句均以半角分号结束。
使用标准库函数时应在程序开头一行书写如下内容:
#include <stdio.h> //printf函数需要使用该头文件
printf 函数起打印输出的作用,它将双引号中的字符串 Hello world打印到屏幕上。
1.3程序的编译过程及项目位置
首先编写好源程序main.c。编写完毕后,通过编译器进行编译,main.c经过编译后,得到可执行文件*(windows是有后缀的,通常是XXX.exe文件。Linux和Mac是不带后缀的。)*可执行文件中均是0/1类型的机器码
文件位置
二数据类型
C语言中的关键字
auto | const | double | float | int | short | struct | unsigned |
---|---|---|---|---|---|---|---|
break | continue | else | for | long | signed | switch | void |
case | default | enum | goto | register | sizeof | typedef | volatile |
char | do | extern | if | return | static | union | while |
2 常量
常量是指在程序运行过程中,其值不发生变化的量。常量又可分为整型、实型(也称浮点型)、字符型和字符串型,
- 整型 1、2、3、4100
- 浮点型(实型)3.14 、1.76、0.125
- 字符型 ‘A’、‘b’、‘2’
- 字符串型"abcd"、“12dv”
3 变量
变量代表内存中具有特定属性的一个存储单元,它用来存放数据,即变量的值。这些值在程序的执行过程中是可以改变的。
变量名实际上以一个名字代表一个对应的存储单元地址。编译、链接程序时,由编译系统为每个变量名分配对应的内存地址(就是空间)。从变量中取值实际上是通过变量名找到内存中存储单元的地址,并从该存储单元中读取数据,如左图所示。
变量的命名规定如下:C语言规定标识符只能由字母、数字和下画线三种字符组成,并且第一个字符必须为字母或下画线。例如,
sum,_total,month,Student_name
编译系统认为大写字母和小写字母是不同的字符,因此C语言要求对所有用到的变量做强制定义,即“先定义,后使用”。同时在选择变量名和其他标识符时,应尽量做到“见名知意”,即选择具有含义的英文单词(或其缩写)作为标识符。注意,变量名不能与关键字同名!
4 整型数据
4.1符号常量
#include <stdio.h>
//变量练习
#define PI 3+2//这是一个符号常量,不能在赋值,不能变
int main() {
//定义一个整型变量时要使用关键字int.
int a=PI*2;
printf("a=%d\n",a);
printf("a size=%d",sizeof(a) );
return 0;
}
//运行结果是7 原因是符号常量PI是直接替换的效果 a=3+2*2所以等于7
//sizeof(a)可以打印空间大小
4.2整型变量
这里掌握int i足以应对初试,后面高级阶段会详细讲解不同类型整型变量,没有时间的同学可以不掌握高级,变量i是4个字节
5浮点型数据
5.1浮点型常量
表示浮点型常量的形式有两种,如下表所示,其中e代表10的幂次,幂次可正可负
5.2浮点型变量
通过float f来定义浮点变量,f占用4个字节的空间
#include <stdio.h>
int main() {
float f=3e-3;
printf("f=%f\n",f);
return 0;
}
//运行结果f=0.003
6字符型数据
6.1字符型常量
用单引号括起来的一个字符是字符型常量,且只能包含一个字符!
例如,‘a’、‘A’、‘1’、’ ‘是正确的字符型常量,而’abc’、“a”、" "是错误的字符型常量。
6.2字符数据在内存中的存储形式及其使用方法
字符型变量使用关键字char进行定义,一个字符型变量占用1字节大小的空间。一个字符常量存放到一个字符型变量中时,实际上并不是把该字符的字型放到内存中,而是把该字符的ASCHI码值放到存储单元中,每个字符的ASCII码值详见附录A。打印字符型变量时,如果以字符形式打印,那么计算机会到ASCII码表中查找字符型变量的ASCII码值,查到对应的字符后会显示对应的字符,
#include <stdio.h>
int main() {
char c='A';
printf("c=%c\n",c+32);
printf("%d",c);
return 0;
}
//运行结果c=a c=65
对于字符型变量,无论是赋ASCII码值还是赋字符,使用%c打印输出时得到的都是字符,使用%d打印输出时得到的都是ASCII码值.将小写字母转换为大写字母时,由课件最后的ASCII码表发现小写字母与大写字母的差值为32,因此将c减去32就可以得到大写字母A。
7字符串型常量
字符串型常量是由一对双引号括起来的字符序列。例如,“How do you do.”、“CHINA”.“a"和”$123.45"是合法的字符串型常量,我们可用语句printf(“How do you do.”)输出一个字符串。但要注意的是, 'a’是字符型常量,而"a"是字符串型常量,二者是不同的.
例如,如果先用语句 char c定义字符型变量c,后令c="a"或c=“CHINA”,那么这样的赋值都是非法的,原因是不可以将字符串型常量赋值给字符型变量.C语言中没有定义字符串型变量的关键字,介绍字符数组时我们将详细讲解如何存放字符串。
C语言规定,在每个字符串型常量的结尾加一个字符串结束标志,以便系统据此判断字符串是否结束。C语言规定以字符’\0’作为字符串结束标志。
例如,字符串型常量"CHINA"在内存中的存储结果如下图所示,它占用的内存单元不是5个字符,而是6个字符,即大小为6字节,最后一个字符为’\0’、然而,在输出时不输出\0’,因为’\0’无法显示。
总结,字符型只占一个字节而且是用单引号,里面有且只有一个,字符串是双引号,占所有字符再加一
例如:“A” 是两个字节
二.二混合运算
2.21混合运算,类型强制转换场景
整型数进行除法运算时,如果运算结果为小数,那么存储浮点数时一定要进行强制类型转换,请看下面例子
#include <stdio.h>
int main(){
int i=5;
float j=i/2;
printf("%f",j);
return 0;
}
//运行结果2.000000
#include <stdio.h>
int main(){
int i=5;
float j=i/2;//为什么是2.0因为这里做的是整型运算,因为左右操作数都是整型
float k=(float)i/2;//进行强制转换
printf("%f\n",j);
printf("%f\n",k);
return 0;
}
//运行结果j=2.000000,k=2.500000
//j得到的值为2,k得到的值是2.5,因为当我们整数做除法时,默认进行整除,要得到小数,需要首先进行强制类型转换操作。
2.22 printf函数介绍
printf函数可以输出各种类型的数据,包括整型、浮点型、字符型、字符串型等,实际原理是printf函数将这些类型的数据格式化为字符串后,放入标准输出缓冲区,然后将结果显示到屏幕上。
语法格式
include <stdio.h>
int printf(const char *format, ...);
//printf函数根据format给出的格式打印输出到stdout(标准输出)和其他参数中。字符串格式(format)由两部分组成:显示到屏幕上的字符和定义printf函数显示的其他参数。我们可以指定一个包含文本在内的forhat字符串,也可以是映射到printf的其他参数的“特殊”字符,如下列代码所示:
int age=21;
printf("hello %s,you are %d years old\n","Bob",age);
//运行结果
hello Bob,you are 21 years old
//其中,%s表示在该位置插入首个参数(一个字符串),%d表示第二个参数(一个整数)应该放在哪里。不同的%codes表示不同的变量类型,也可以限制变量的长度。printf函数的具体代码格式如下表所示。
printf函数的具体代码格式 | |
---|---|
%c | 字符 |
%d | 带符号整数 |
%f | 浮点数 |
%s | 一串字符 |
%u | 无符号整数 |
%x | 无符号16进制数,用小写字母 |
%X | 无符号16进制数,用大写字母 |
%p | 一个指针 |
%% | 一个‘%’符号 |
#include <stdio.h>
int main() {
int i=10;
float j=96.3;
printf("student number=%d,score=%f\n",i,j);
i=100;
j=98.5;
printf("student number=%d,score=%f\n",i,j);
return 0;
}
//运行结果
student number=10,score=96.300003
student number=100,score=98.500000//没有对齐,格式混乱
//开始改造
printf("student number=%3d,score=%f\n",i,j);
//运行效果
student number= 10,score=96.300003//默认右对齐
student number=100,score=98.500000
//%3d这个意思是说这个数据在输出的时候会占3个空格的位置,默认右对齐
//不想小数太长开始改造
printf("student number=%3d,score=%5.2f\n",i,j);
//运行效果
student number= 10,score=96.30
student number=100,score=98.50
//%5.2f的意思是5代表的是有五个位置,2是小数点后面保留两位
//左对齐
printf("student number=%-3d,score=%5.2f\n",i,j);
//运行效果
student number=10 ,score=96.30
student number=100,score=98.50
执行结果可以看到整型数10在不加负号时靠右对齐,加负号时靠左对齐,%10s代表字符串共占用10个字符的位置.因为printf 函数默认靠右对齐,所以""hello"字符串相对于左边的起始位置有5个空格的距离。掌握这些内容后,在做oJ作业时,就会很容易掌握打印格式的控制.
printf函数的所有输出都是右对齐的,除非在%符号后放置了负号
用%f精度修饰符指定想要的小数位数。例如,%5.2f会至少显示5位数字并带有2位小数的浮点数.
用%s精度修饰符简单地表示一个最大的长度,以补充句点前的最小字段长度。
2.3整型常量的不同进制表示
计算机中只能存储二进制数,即О和1,而在对应的物理硬件上则是高、低电平。为了更方便地观察内存中的二进制数情况,除我们正常使用的十进制数外,计算机还提供了十六进制数和八进制数。
1字节是八位 1001 0010这就是一个字节byte 位bit
1k =1024字节
1M =1024k
1G =1024M
二进制数最高位是符号位 0100 1100 0011 0001 0101 0110 1111 1110
二进制 0和1
十进制 0-9
八进制 0-7
十六进制 0-9 a-f
英特尔的CPU采用了小端方式进行数据存储,因此低位在前、高位在后.
#include <stdio.h>
int main() {
int i=123;
printf("%d\n",i);
printf("%o\n",i);
printf("%x\n",i);
return 0;
}
//运行结果
123
173
7b
//我们想在开始的时候就给他赋值八进制或者十六进制怎么做
int i=0173;//八进制需要在前面加个零
int i=0x7b;//十六进制需要在前面加零x(小写英文字母x)
2.4scanf读取标准输入
1 scanf函数的原理
C语言未提供输入/输出关键字,其输入和输出是通过标准函数库来实现的。C语言通过scanf函数读取键盘输入,键盘输入又被称为标准输入。当scanf函数读取标准输入时,如果还没有输入任何内容,那么scanf函数会被卡住(专业用语为阻塞)。
scanf用来读取标准输入,scanf把标准输入内的内容,需要放到某个变量空问里,因此变量必须取地址
断点调试,如果变量没有赋值,会直接跳过,如果变量赋值了,则不会。
#include <stdio.h>
//scanf用来读取标准输入,scanf把标准输入内的内容,需要放到某个变量空问里,因此变量必须取地址
int main() {
int i;
char c;
scanf("%d",&i);
printf("i=%d\n",i);//把标准缓冲区中的整型数读走了,其实还剩下一个\n
fflush(stdin);//清空标准输入级冲区
scanf("%c",&c);
printf("c=%c",c);//输出字符变量c
return 0;
}
//问题出现,先看运行效果
12
i=12
c=
//这是怎么回事,两个scanf函数只读了一个,实际上c也出来了,因为scanf函数需要回车才能执行这个函数。当我们的第一个scanf执行的时候我们往标准输入缓冲区,就是一段内存里面存我们输入的东西,然而我们需要回车才能执行我们这个scanf函数,所以我们缓存区其实还有一个\n没有取走,所以下一个scanf直接把这个\n取走了,所以c=\n也就是说,c等于换行。那如何避免我们这个问题,其实也简单,我们可以清空缓存区。
fflush(stdin);//清空标准输入级冲区
//运行效果
12
i=12
a
c=a
进程已结束,退出代码为 0
scanf %d %f发现里边有\n 空格,忽略
scanf %c不忽略内容
行缓冲:在这种情况下,当在输入和输出中遇到换行符时,将执行真正的IO处理操作.这时,我们输入的字符先存放到缓冲区中,等按下回车键换行时才进行实际的I/O操作.典型代表是标准输人缓冲区(stdin)和标准输出缓冲区(stdout) , printf使用的是stdout 。
如上面的例子所示,我们向标准输入缓冲区中放入的字符为’12\n’,输入’\n’(回车)后, scanf函数才开始匹配, scanf函数中的%d 匹配整型数12,然后放入变量i中、接着进行打印输出,这时\n’仍然在标准输入缓冲区(stdin)内,如果第二个scanf函数为scanf(“%d”,&i),那么依然会发生阻塞,因为scanf 函数在读取整型数、浮点数、字符串(后面介绍数组时讲解字符串)时,会忽略’\n’(回车符)、空格符等字符(忽略是指scanf函数执行时会首先删除这些字符,然后再阻塞) 。scanf函数匹配一个字符时,会在缓冲区删除对应的字符.因为在执行scanf(“%c”,&c)语句时,不会忽略任何字符,所以scanf(“%c”,&c)读取了还在缓冲区中残留的’\n’.
scanf会阻塞,是因为标准输入缓冲区是空的
2 多种数据类型混合输入
当我们让scanf函数一次读取多种类型的数据时,对于字符型数据要格外小心,因为当一行数据中存在字符型数据读取时,读取的字符并不会忽略空格和’\n’(回车符),所以使用方法如下例所示。编写代码时,我们需要在%d与%c之间加入一个空格。输入格式和输出效果如下图所示,scanf函数匹配成功了4个成员,所以返回值为4,我们可以通过返回值来判断scanf函数匹配成功了几个成员,中间任何有一个成员匹配出错,后面的成员都会匹配出错。
#include <stdio.h>
int main() {
int i;
char c;
float f;
scanf("%d%c%f",&i,&c,&f);
printf("i=%d,c=%c,f=%5.2f",i,c,f);
return 0;
}
//运行效果
22 d 22.22
i=22,c= ,f= 0.00
进程已结束,退出代码为 0
//解决方案,可以在%d和%c之间加一个空格,只需要在%c前面加个空格进行,其他的不用管,如果%c在最开头的位置,就不需要管了。
#include <stdio.h>
int main() {
int i,ret;
char c;
float f;
ret=scanf("%d %c%f",&i,&c,&f);//ret是指scanf匹配成功的个数,方便我们调试
printf("i=%d,c=%c,f=%5.2f",i,c,f);
//%0.2f 不限制浮点数输出的整体长度
return 0;
}
//运行效果
22 l 22.22
i=22,c=l,f=22.22
进程已结束,退出代码为 0
三.一运算符和表达式
3.1算术运算符与关系运算符
3.1.1运算符分类
C语言提供了13种类型的运算符
(1)算术运算符(+ - * / %)。
(2)关系运算符(> < == >= <= !=)。
(3)逻辑运算符(! && ll) 。
(4)位运算符(<< >> ~ │ ^ &) 。
(5)赋值运算符(=及其扩展赋值运算符)。
(6)条件运算符( ? 😃 。
(7)逗号运算符(,)。
(8)指针运算符(*和&)。—讲指针时讲解
(9)求字节数运算符(sizeof)。
(10)强制类型转换运算符((类型))。
(11)分量运算符(. ->)。—讲结构体时讲解
(12)下标运算符([ ])。----讲数组时讲解
(13)其他(如函数调用运算符( ))。—讲函数时讲解
3.12 算术运算符及算术表达式
算术运算符包含+、-、* 、/和%,当一个表达式中同时出现这5种运算符时,先进行乘(*)、除(/)、取余(%),取余也称取模,后进行加(+)、减(-),也就是乘、除、取余运算符的优先级高于加、减运算符。除%运算符外,其余几种运算符既适用于浮点型数又适用于整型数。当操作符的两个操作数都是整型数时,它执行整除运算,在其他情况下执行浮点型数除法。号为取模运算符,它接收两个整型操作数,将左操作数除以右操作数,但它的返回值是余数而不是商。由算术运算符组成的式子称为算术表达式,表达式一定有一个值。
#include <stdio.h>
int main() {
int result=4+2*5-6/3+11%2;
printf("result=%d",result);
return 0;
}
//运行结果 13
3.13关系运算符与关系表达式
关系运算符>、<、、>=、<=、!=依次为大于、小于、是否等于、大于等于、小于等于和不等于。由关系运算符组成的表达式称为关系表达式。关系表达式的值只有真和假,对应的值为1和0。由于c语言中没有布尔类型,所以在C语言中0值代表假,非0值即为真。例如,关系表达式3>4为假,因此整体值为0,而关系表达式5>2为真,因此整体值为1。关系运算符的优先级低于算术运算符,运算符的优先级的详细情况见课件最后。
在工作中,很多程序员容易不小心将两个等号写成一个等号,因此当判断整型变量i是否等于3时,我们可以写为3i,即把常量写在前面而把变量写在后面。这是因为当不小心将两个等号写为一个等号时,变量在前面就会导致编译不通,从而快速发现错误(这种写法属于华为公司内部的一条编程规范)。
同时,在编写程序时,如果我们需要判断三个数是否相等,那么绝对不可以写为if (555),这种写法的值无论何时都为假,为什么?因为首先55得到的结果为1然后15得到的结果为0。如果要判断三个变量a、b、c是否相等,那么不能写为abc 而应写为ab & & bc。下面来看一个例子。
#include <stdio.h>
//关系运算符优先级是小于算术运算符的
int main() {
int a;
while(scanf("%d",&a)){
if(3<a<10){
printf("%d is between 3 and 10!\n",a);
}else
printf("%d is not between 3 and 10!\n",a);
}
return 0;
}
//运行结果
"E:\My Files\CLion-code\3\untitled\cmake-build-debug\untitled.exe"
20
20 is between 3 and 10!
2
2 is between 3 and 10!
3
3 is between 3 and 10!
m
进程已结束,退出代码为 0
这就导致我们无论输入什么,都在3和10之间,很明显这个程序是有错误的。因为前面说到关系运算符只有0和1。首先,无论a是大于3还是小于3,对于3<a这个表达式只有1或O两种结果.由于1和0都是小于10的,所以无论a的值为多少,这个表达式的值始终为真,因此在判断变量a是否大于3且同时小于10时,要写成a>3 && a<10,这才是正确的写法.
#include <stdio.h>
//换一种写法
int main() {
int a;
while(scanf("%d",&a)){
if(3<a && a<10){
printf("%d is between 3 and 10!\n",a);
}else
printf("%d is not between 3 and 10!\n",a);
}
return 0;
}
//运行结果
4
4 is between 3 and 10!
12
12 is not between 3 and 10!
16
16 is not between 3 and 10!
6
6 is between 3 and 10!
m
进程已结束,退出代码为 0
3.14运算符优先级表
3.2逻辑运算符与赋值运算符,求字节运算符
逻辑运算符与逻辑表达式
逻辑运算符!、&&、l|依次为逻辑非、逻辑与、逻辑或,这和数学上的与、或、非是一致的。逻辑非的优先级高于算术运算符,逻辑与和逻辑或的优先级低于关系运算符。逻辑表达式的值只有真和假,对应的值为1和0。下例中的代码是计算一年是否为闰年的例子,因为需要重复测试,所以我们用了一个 while循环。
针对代码中的逻辑非,首先给变量j赋值10,因为j的值非0,所以!j的值为0;然后,由于逻辑非是单目运算符,结合顺序为从右至左,得到! !j的值为1。也就是说,对0取非,得到的值为1;对非0值取非,得到的值为0。
#include <stdio.h>
int main() {
int year,j,i=1;
while(scanf("%d",&year))
{
if(year%4==0 && year%100!=0 || year%400==0)
{
printf("%d is leap year\n",year);
}else{
printf("%d is not leap year",year);
}
}
j=!!i;
printf("%d",j);
return 0;
}
//运行结果:
440
440 is leap year
444
444 is leap year
1000
1000 is not leap year
100
100 is not leap year
j= 1
m
进程已结束,退出代码为 0
短路运算 逻辑与和逻辑或
int main(){
int i=0;
i&&printf("you can not see me!\n");
return 0;
}
这就是短路运算,i为0就是假的意思,就当他为假的时候就不会输出printf,这样做的好处就是减少代码量。
if(i)
{
printf("you can not see me!\n");
}
他和上面那个if是等价的。
逻辑或与逻辑与是相反的。逻辑与短路运算是当前面一个表达式为假时,后面的表达式不会得到执行,逻辑或短路运算是当前面一个表达式为真时,后面的表达式不会得到执行。
逻辑或
i=1;
i||printf("you can not see me!\n");
赋值运算符
为了理解有些操作符存在的限制,必须理解左值(L-value)和右值 (R-value)之间的区别。这两个术语多年前由编译器设计者创造并沿用至今,尽管它们的定义与C语言并不严格吻合。
左值是那些能够出现在赋值符号左边的东西,右值是那些可以出现在赋值符号右边的东西。例如,
a= b+25;
其中, a是一个左值,因为它标识了一个可以存储结果值的地点; b+25是一个右值,因为它指定了一个值。
它们可以互换吗?比如下面这种写法:
b +25 = a;
因为每个位置都包含一个值,所以原先用作左值的a此时也可以作为右值;然而, b+25不能作为左值,因为它并未标识一个特定的位置(并不对应特定的内存空间)。因此,上面这条赋值语句是非法的。
#include <stdio.h>
//赋值运算符 左值 and 右值
int main() {
int a=2,b=3;
a+25=b;
return 0;
}
error: lvalue required as left operand of assignment
报错:要求左值作为赋值的左操作数
因为a+25不是一个左值,所以报错
复合赋值运算符.
复合赋值运算符操作是一种缩写形式,使用复合赋值运算符能使对变量的赋值操作变得更加简洁。例如,
iNum = iNum + 5;
对变量 iNum 的赋值进行操作,值为这个变量本身与一个整型常量5相加的结果.使用复合语句可以实现同样的操作。例如,上面的语句可以修改为
iNum+=5;//加后赋值
赋值运算符与复合赋值运算符的区别如下:
(1)复合赋值运算符简化了程序,可使程序精炼,提升阅读速度。
(2)复合赋值运算符提高了编译效率。
下例说明了加后赋值与乘后赋值的用法。
#include <stdio.h>
int main() {
int a=1,b=2;
//a=a+5;
//b=b*5;
a+=5;
b*=5;
printf("a=%d\n",a);
printf("b=%d\n",b);
return 0;
}
//运行结果
a=6
b=10
求字节运算符sizeof
很多同学会认为sizeof是一个函数,这种理解是错误的,实际sizeof是一个运算符,不像其他运算符是一个符号,sizeof是字母组成的,用于求常量或变量所占用的空间大小,请看下例:
#include <stdio.h>
int main() {
int i=0;
printf("i size is %d\n", sizeof(i));
return 0;
}
//运行结果
i size is 4,可以求得整型变量占用的空间大小是4个字节。
四.选择循环
4.1选择 if-else
关系表达式与逻辑表达式
程序员控制程序执行逻辑运算需要选择和循环结构,首先我们来讲选择语句,在介绍选择语句前,我们首先练习一下关系表达式与逻辑表达式.在第3大节课中,我们了解到算术运算符的优先级高于关系运算符、关系运算符的优先级高于逻辑与和逻辑或运算符、相同优先级的运算符从左至右进行结合等,那么表达式5>3&&8<4- !0的最终值是多少?其计算过程如下图所示。
最后把1,0当对错看,不是对的就是错的呗。&&运算还是当对错看,只有全部都是对的时候,它总的来讲才是对的,所以1&&0是0
5>3&&8<4- !0 其实我们看到这个表达式通常都会去算 !0 因为他是第二优先级,实际上他是不对的。因为我们要考虑这个短路运算 所以先算的是 5>3然后再是逻辑非
如果是1>3&&8<4-!0 这个表达式结果仍然是0 因为1>3是假 所以后面直接短路,不用再计算
什么是双目运算符?
就是有两个操作数,就比如 +
左操作数+右操作数
单目运算符,就是只有一个操作数,我们前面也见过。 !
!操作数 逻辑非就是单目运算符
if - else 语句
在你打开衣柜拿出最上面的一件衣服时,你会判断这件衣服是不是你想穿的。如果是,那么你就会穿上;如果不是,那么你就会去找其他衣服。在计算机中,我们用if判断语句来实现这样的效果: if判断条件(表达式)为真,就执行某个语句,反之不执行这个语句。当然,也可以if判断条件(表达式)为真,就执行某个语句,反之用else分支执行另一个语句。
当输入值大于0时,打印“ i is bigger than o”,当输入值小于等于0时,打印“i is not bigger than 0”,具体代码如下例所示。注意,在这个例子中,if后面不能加分号,因为如果有else分支语句,那么加分号会导致编译不通过;如果没有else分支语句,那么加分号会导致i无论取何值,都会打印“i is bigger than 0”。
#include <stdio.h>
int main()
{
int i;
while(scanf("%d",&i))
{
if(i>0)
{
printf("%d is bigger than 0\n",i);
}else{
printf("%d is not bigger than 0\n",i);
}
}
return 0;
}
if语句和else 语句也可以多个同时使用(多分支语句),如下图3所示。但是,无论有多少个if语句或else if语句,程序都只会执行其中的一个语句。下面是一个关于用电量的例子:用电量越高,电的单价越高,但最终cost 只会被赋值一次。同时,if语句也支持多层嵌套,在if语句中又包含一个或多个if语句称为if语句的嵌套,如图4所示。
if (number>500)cost=0.15;
else if(number>300)cost=0.10;
else if(number>100)cost=0.075;
else if(number>50)cost=0.05;
else cost=O;
使用if嵌套语句时,要考虑“悬空的else”问题。例如,在下面的例子中, else子句从属于哪个if语句?
if(i>1)
if(i<10)
printf("i>1 and i<10\n");
else
printf(" no,they are not\ n");
和其他绝大多数语言一样,C语言中的 else子句从属于最靠近它的不完整的if语句。上例中的else子句从属于第二个if语句,如果想让它从属于第一个if 语句,那么可以用一个花括号把第二个if语句包含在一个单独的代码块内,如下所示。
if(i>1){
if(i<10)
printf("i>1 and i<10\n");
}
else
printf("no, they are not\n");
在if语句中的语句列表前后加上花括号,可以防止不小心加了一句代码后,使实际未被包含的语句被包含在某个if语句中的错误。
4.2 循环while 、for讲解、continue、break讲解
while 循环
while 语句用来实现“当型”循环结构,其一般形式为“while(表达式)语句;”,当表达式的值非0时,执行 while语句中的内嵌语句。其特点是:先判断表达式,后执行语句,具体流程如下图所示。当表达式的值非О时,就会执行语句,从而实现语句多次执行的效果。为了避免程序进入死循环〈不停地进行循环操作)﹐在语句中需要有让表达式趋近于假的操作来使程序跳出循环。
下面来看使用while语句计算1到100之间所有整数之和的例子,如下例所示.注意, while后面不能加分号,否则虽然编译可以通过,但是执行程序时会发生死循环。通常我们会将while语句用花括号括起来,就算 while语句只有一句,也会用花括号括起来,这么做是因为一个程序往往会经过多次修改,使用花括号可以让程序更加清晰,避免向循环内添加语句时出错
【例】计算1到100之间所有整数之和
#include <stdio.h>
//计算1到100的和
int main() {
int i=1,total=0,n=100;
while (i<=100)
{
total+=i;//total=total+i 把i加到total上
i++;//i++ 等价于 i+=1
}
//高斯公式 等差数列的求和 n *(n+1)/2
printf("total=%d\n",total);
printf("n=%d",n*(n+1)/2);
return 0;
}
运行结果
total=5050
n=5050
进程已结束,退出代码为 0
我们能够改变变量的,在c语言里面,只有两种方式。一种呢是赋值操作,赋值能够改变变量的值,另外一种改变变量的值的是 ++和-- 加加是加一,减减是减一 这个发明也是有原因的。这是历史遗留问题,这是从B语言里面继承过来的。
for 循环
C语言中的for循环语句使用最为灵活,不仅可以用于循环次数已经确定的情况,而且可以用于循环次数不确定而只给出循环结束条件的情况,它完全可以代替while循环语句。其一般形式为
for(表达式1;表达式2;表达式3)语句;
for循环语句的执行过程如下,具体流程如下图所示。
(1)先求解表达式1.
(2)求解表达式2,若其值为真(值为非0),则先执行for语句中指定的内嵌语句,
后执行第(3)步。若其值为假(值为0),则结束循环,转到第(5)步。
(3)求解表达式3。
(4)转回第(2)步继续执行。
(5)循环结束,执行for语句下面的语句。
表达式1 只会执行一次
下面来看一个使用for循环语句计算1到100之间的所有整数之和的例子, for循环语句中必须且只能有两个分号,用于分割表达式1、表达式2和表达式3。表达式1、表达式2、表达式3也可省略,省略写法用得较少。如下例所示,“i=1,total=0”是表达式1,即表达式1可以使用逗号初始化多个变量,表达式3的作用是使表达式2趋近于假。
通过for循环实现从1加到100
#include <stdio.h>
int main() {
int i,total=0;
for(i=1;i<=100;i++)//在for 加分号不会死循环,会结果不对 结果是101 因为total是0 0+101=101
{
total+=i;
}
printf("total=%d\n",total);
return 0;
}
for循环的可读性要比 while循环的好,所以能使用for循环时不要强制改为while循环.
continue 语句
continue语句的作用为结束本次循环,即跳过循环体中下面尚未执行的语句,接着进行是否执行下一次循环的判断。其一般形式为
continue;
下面来看一个对1到100之间的奇数进行求和的例子,如下例所示。这个例子是对上一节中的 for循环的改写,执行“continue;”语句后,执行的语句是“i++;”。当continue用于while和do while循环中时,注意不要跳过让循环趋近于假的语句。
【例】对1到100之间的奇数进行求和
#include <stdio.h>
int main() {
int i,total;
for (i=1,total=0;i<=100;i++) {
if(i%2==0){
continue;//continue 作用,直接跳过循环进行表达式3,continue以下的所有都不会执行
}
total+=i;
}
printf("total=%d\n",total);
return 0;
}
break 语句
break语句的作用是结束整个循环过程,不再判断执行循环的条件是否成立。下例是关于break语句的例子,例子从1开始累加,当累加的和大于2000时,结束 for循环,同时打印此时total 的值和i的值.一旦执行break 语句﹐下一句要执行的就是“printf(“total=%d,i=%d\n” ,total,i);”。 break语句也可用在 while循环和 do while 循环中
起结束对应循环的作用。
【例】break语句实例
#include <stdio.h>
int main() {
int i,total;
for(i=1,total=0;i<=100;i++){
if(total>2000){
break;//当和大于2000时,循环结束
}
total+=i;
}
printf("total=%d,i=%d\n",total,i);
return 0;
}
运行结果
total=2016,i=64
其实i只加到63,因为i到64就break了,还没有来得及加,但i的值是64
#include <stdio.h>
int main()
{
//如何判断他是一个对称数,比如12321 逆置过来还是12321 1234逆置过来就是4321
//两个数不相等,就不是对称数
//关键是我们怎么逆置
//从我们分析中 我们知道我需要三个变量
int a,b,c;
scanf("%d",&a);//读取一个整形数
c=a;//把a最初的值赋值给c,备份一下,后面于逆置的值进行对比
while(a)
{
printf("%d\n",a%10);//拿到个位数
a/=10;
}
return 0;
}
//运行结果
1234
4
3
2
1
进程已结束,退出代码为 0
#include <stdio.h>
int main()
{
//如何判断他是一个对称数,比如12321 逆置过来还是12321 1234逆置过来就是4321
//两个数不相等,就不是对称数
//关键是我们怎么逆置
//从我们分析中 我们知道我需要三个变量
int a,b=0,c;
scanf("%d",&a);//读取一个整形数
c=a;//把a最初的值赋值给c,备份一下,后面于逆置的值进行对比
while(a)
{
b=b*10+a%10;//把b初始化一下,因为不把b初始化一下,值可能不对。组合起来
a/=10;
}
if(b==c)
{
printf("yes\n");
}else{
printf("no\n");
}
return 0;
}
//计算n!
#include <stdio.h>
int main()
{
int n;//定义一个变量
scanf("%d",&n);//取出来一个整形数
int value=1;//定义一个最后结果的值
for (int i = 1; i <=n ; i++) {//定义一个可以遍历的变量
value*=i;//一直循环乘 n=5 1*2*3*4*5
}
printf("%d",value);
return 0;
}
五、一维数组和字符数组
一维数组
1、数组的定义
为了存放鞋子,假设你把衣柜最下面的一层分成了10个连续的格子。此时,让他人帮你拿鞋子就会很方便,例如你可直接告诉他拿衣柜最下面一层第三个格子中的鞋子。同样假设现在我们有10个整数存储在内存中,为方便存取,我们可以借助C语言提供的数组,通过一个符号来访问多个元素。
某班学生的学习成绩、一行文字、一个矩阵等数据的特点如下:
(1)具有相同的数据类型。
(2〉使用过程中需要保留原始数据。
C语言为了方便操作这些数据,提供了一种构造数据类型——数组.所谓数组,是指一组具有相同数据类型的数据的有序集合。
一维数组的定义格式为:
类型说明符 数组名【常量表达式】;
例如:
int a[10];
定义一个整型数组,数组名为a,它有10个元素。
声明数组时要遵循以下规则:
(1)数组名的命名规则和变量名的相同,即遵循标识符命名规则。
(2)在定义数组时,需要指定数组中元素的个数,方括号中的常量表达式用来表示元素的个数,即数组长度。
(3)常量表达式中可以包含常量和符号常量,但不能包含变量。也就是说,C语言不允许对数组的大小做动态定义,即数组的大小不依赖于程序运行过程中变量的值。
以下是错误的声明示例(最新的C标准支持,但是最好不要这么写):
int n;
scanf("%d",&n);//在程序中临时输入程序的大小
int a[n];
数组声明的其他常见错误如下:
float a[0];//数组大小为0没有意义
int b(2)(3);//不能使用圆括号
int k=3,a[k];//不能用变量说明数组大小
2、一维数组在内存中的存存储
语句int mark[ 100];定义的一维数组mark在内存中的存放情况如下图所示,每个元素都是整型元素,占用4字节,数组元素的引用方式是“数组名[下标]",所以访问数组 mark中的元素的方式是mark[0],mark[1],… , mark[99]。注意,没有元素 mark[100],因为数组元素是从0开始编号的。
靠前的元素叫低地址,靠后的元素叫高地址
下面介绍一维数组的初始化方法。
(1)在定义数组时对数组元素赋初值。例如,
int a[10]={0,1,2,3,4,5,6,7,8,9};
不能写成
int a[10];a[10]={0,1,2,3,4,5,6,7,8,9};
(2)可以只给一部分元素赋值。例如,
int a[10]={0,1,2,3,4};
定义a数组有10个元素,但花括号内只提供5个初值,这表示只给前5个元素赋初值,后5个元素的值为0。
(3)如果要使一个数组中全部元素的值为0,那么可以写为
int a[10]={0,0,0,0,0,0,0,0,0,0};
或
int a[10]={0};
(4)在对全部数组元素赋初值时,由于数据的个数已经确定,因此可以不指定数组的长度。例如,
int a[]={0,1,2,3,4};
数组访问越界与数组的传递
1 数组的访问越界
所谓的数组越界,简单地讲就是指数组下标变量的取值超过了初始定义时的大小,导致对数组元素的访问出现在数组的范围之外,这类错误也是C语言程序中最常见的错误之一。
下面借助一个数组的实例来掌握数组元素的赋值、访问越界.下例中给出了该例的全部代码。
【例】一维数组的存储及访问越界。
#include <stdio.h>
int main() {
int a[5]={0,1,2,3,4};
int i=20;
int j=10;
a[5]=6;//访问越界
a[6]=7;//访问越界会造成数据异常
printf("a[5]=%d\n",a[5]);
printf("a[6]=%d\n",a[6]);
printf("i=%d\n",i);
printf("j=%d\n",j);
return 0;
}
运行结果:
a[5]=6
a[6]=7
i=20
j=7
j的值为什么是7 ?
a[5]、a[6]都是访问越界,因为我们定义了数组只有五个。
数组另一个值得关注的地方是,编译器并不检查程序对数组下标的引用是否在数组的合法范围内。这种不加检查的行为有好处也有坏处,好处是不需要浪费时间对有些已知正确的数组下标进行检查,坏处是这样做将无法检测出无效的下标引用。一个良好的经验法则是:如果下标值是通过那些已知正确的值计算得来的,那么就无须检查;如果下标值是由用户输入的数据产生的那么在使用它们之前就必须进行检查,以确保它们位于有效范围内。
2 数组的传递
//main函数就是主函数
#include <stdio.h>
int main(){
int a[5]={1,2,3,4,5};
int i;
for(i=0;i<5;i++){
printf("%d\n",a[i]);
}
return 0;
}
运行效果
1
2
3
4
5
现在我们不都写在main函数里面了
#include <stdio.h>
void prin(int a[5]){
int i;
for(i=0;i<5;i++){
printf("%d\n",a[i]);
}
}
//main函数就是主函数
int main(){
int a[5]={1,2,3,4,5};
prin(a);
//这里只能写a 不要这样写prin(a[5]); 这样写就访问越界了,给我们第一个例子一样了。a[5]这样只能访问单个的元素,而且数组下标是从零开始的。
return 0;
}
运行效果
1
2
3
4
5
void prin(int a[5]){
我们发现无论 数组长度是无法传递的,无论我们写不写a[5] 或a[7] 和里面的数字无关
}
//main函数就是主函数
int main(){
int a[5]={1,2,3,4,5};
//prin(a);
//这里只能写a 不要这样写prin(a[5]); 这样写就访问越界了,给我们第一个例子一样了。a[5]这样只能访问单个的元素,而且数组下标是从零开始的。
int i;
for(i=0;i<sizeof(a)/sizeof(int);i++){
printf("%d\n",a[i]);
}
return 0;
}
运行效果
1
2
3
4
5
写着主函数里面还正常,我们把for循序粘贴到我们的prin函数里面试试
#include <stdio.h>
void prin(int a[5]){
int i;
for(i=0;i<sizeof(a)/sizeof(int);i++){
printf("%d\n",a[i]);
}
}
//main函数就是主函数
int main(){
int a[5]={1,2,3,4,5};
prin(a);
//这里只能写a 不要这样写prin(a[5]); 这样写就访问越界了,给我们第一个例子一样了。a[5]这样只能访问单个的元素,而且数组下标是从零开始的。
return 0;
}
运行效果
1
2
结果异常,无论我们的数组长度是多少,结果都是 1 2 数组长度传递不过去。
调试 clion环境下 折弯箭头 F8 当前函数一步一步往下走,向下箭头 F7 意思是到达某个函数,要进入自己的 子函数时要使用。
子函数作用,把某一个常用的功能封装起来。
当我们调试后发现sizeof=8,所以他才会打印前两个元素。
因为我们的数组在c语言里面他传递到子函数的时候,他不能把数组传递过去,也就是说他没有数组传递的功能。他传递过去的时候,也就是数组名传递到子函数的时候弱化成了一个指针,指针是8个字节。
数组名传递到子函数后,子函数的形参接收到是数组的起始地址,因此不能把数组的长度传递给子函数。
解决办法:
我们不得不自己再起个变量,把数组的变量给他传递过去
#include <stdio.h>
void prin(int a[],int length){
int i;
for(i=0;i<length;i++){
printf("%d\n",a[i]);
}
}
//main函数就是主函数
int main(){
int a[5]={1,2,3,4,5};
prin(a,5);
//这里只能写a 不要这样写prin(a[5]); 这样写就访问越界了,给我们第一个例子一样了。a[5]这样只能访问单个的元素,而且数组下标是从零开始的。
return 0;
}
运行效果
1
2
3
4
5
%3d是C语言中格式化输出字符,代表的意思是指的输出3个字符长度的整数,若当前输出不足3位,则补空格。
可以在“%”和字母之间插进数字表示最大场宽。
printf("%3d",8);
结果:空格空格8
printf("%4d",8);
结果:空格空格空格8
printf("%3d",1234);
结果:1234
例如:%3d表示输出3位整型数,不够3位右对齐。
%9.2f表示输出场宽为9的浮点数,其中小数位为2,整数位为6,小数点占一位,不够9位右对齐。
%8s表示输出8个字符的字符串,不够8个字符右对齐。如果字符串的长度、或整型数位数超过说明的场宽,将按其实际长度输出。
但对浮点数,若整数部分位数超过了说明的整数位宽度,将按实际整数位输出;若小数部分位数超过了说明的小数位宽度,则按说明的宽度以四舍五入输出。
字符数组与scanf读取字符串
1、字符数组初始化及传递
字符数组的定义方法与前面介绍的一维数组类似。例如,
char c[10];
字符数组的初始化可以采用以下方式。
(1)对每个字符单独赋值进行初始化。例如,
c[0]='I';c[1]='';c[2]='a';c[3]='m';c[4]='';c[5]='h';c[6]='a';c[7='p';c[8]='p';c[9]='y';
(2)对整个数组进行初始化。例如,
char c[10]={'i','a','m','h','a','p','p','y'};
但工作中一般不用以上两种初始化方式,因为字符数组一般用来存取字符串。通常采用的初始化方式是 char c[10]= “hello”。因为C语言规定字符串的结束标志为’\0’,而系统会对字符串常量自动加一个’0’,为了保证处理方法一致,一般会人为地在字符数组中添加’\0’,所以字符数组存储的字符串长度必须比字符数组少1字节.例如, char c[10]最长存储9个字符,剩余的1个字符用来存储’\0’。
我们平常这样初始化字符数组
char b[10]="Iamhappy";
字符串常量,这里是八个字符,但实际上是九个字符。因为他还有一个结束字符 \0。比如:
char c[5]="hello";
这样写是不合法的,因为他还有一个\0,这样这个\0就访问越界了,他只能放在数组的后面。正确的写法是
char c[6]="hello";
因为字符串读到\0才会结束,所以要6个。不然很危险,打印出来的东西也会乱码。
那我们怎么输出一个字符串呢?
使用%s来输出一个字符串。
#include <stdio.h>
int main() {
//char c[10]={'i','a','m','h','a','p','p','y'};
char b[10]="Iamhappy";
printf("%s",b);
return 0;
}
运行结果:
Iamhappy
模拟 printf(“%s”,d);操作
#include <stdio.h>
void print(char d[]){
int i=0;
//通过遍历获取
while(d[i]){//当走到0时,循环结束 \0就是结束符
printf("%c",d[i]);
i++;
}
printf("\n");
}
int main() {
char ch[6]="hello\0";
char d[5]="how";
printf("%s\n",ch);
print(d);
return 0;
}
scanf读取字符串
#include <stdio.h>
//scanf函数读取字符串
int main()
{
char c[10];
scanf("%s",c);//字符数组名c中存储了数组的起始地址,因此不需要取地址。
printf("%s\n",c);
return 0;
}
运行结果:
lichenxin
lichenxin
进程已结束,退出代码为 0
前面scanf函数读取东西的时候都有取地址符号,为什么这里没有?
是因为前面整型,浮点型,字符型我们的scanf必须放个地址。我们这里不取地址的原因是因为这个c我们的数组名本来就存储了数组的起始地址。记住就行了。其实我们这里放取地址也是对的。
经过dbug调试,我们发现scanf在读取一个字符串的时候会自动放一个结束符的。
给数组赋值,看接下来的变化。
控制台输入 hello 然后点击单步调试,如下图所示:
输出结果:hello
hello
我们发现输出结果并没有输出7和8而是输出了hello,这是scanf函数在读取一个字符串的时候会自动放一个结束符的。