我们学习不仅仅是要把难的学会,也要注重基础,注重内功。
接下来我们继续先从基础知识开始:
1. 字符串+字符常量+注释
1.1 字符串
如:“abc”
①定义:由双引号引起来的一串字符称为字符串。
②C语言规定,每个字符串的末尾隐藏放了一个‘\0’
③字符串的结束标志是一个‘\0’。(注:‘\0’是结束标志,不算作字符串内容)
代码实例:
1. int main() 2. { 3. char arr1[] = "abc";//字符串数组 4. char arr2[] = { 'a','b','c' };//字符数组 5. return 0; 6. }
调试:
加油站:字符串的相关知识点
①%s:打印字符串,直到遇到‘\0’才停止打印(‘\0’是结束标志,不算作字符串内容)
代码实例:
#include<stdio.h> int main() { char arr1[] = "abc";//字符串数组 char arr2[] = { 'a','b','c' };//字符数组 //打印arr1 printf("%s\n", arr1); //打印arr2 printf("%s\n", arr2); return 0; }
运行结果:
图解:
②strlen:C语言提供的库函数,可以计算字符串的长度,统计的‘\0’之前字符的长度(‘\0’是结束标志,不算字符串内容)
补充:strlen:string字符串,length长度 它的头文件“string.h”(如使用别人的东西你要打招呼)
代码实例:
#include<stdio.h> #include<string.h>//预处理,对strlen的声明 int main() { char arr[] = "abc"; //调用库函数strlen,计算字符串的长度 int len = strlen(arr); //打印长度 printf("%d\n", len); return 0; }
运行结果:
注意:
1、字符串的占用空间VS长度
占用空间要加上'\0',长度不加‘\0’
2、不能把一个字符串赋给一个字符变量。例:c="a";是错的
3、C语言没有字符串变量,但可用字符数组来表示
1.2 字符常量
如:‘a’
1、分类:C语言中,字符常量分为普通字符常量和转义字符两种
①普通字符:用单引号括起来的一个字符,该字符是除单引号和反斜杠以外所有可显示字符。
②转义字符:a.转义字符以反斜杠开头;b.转义字符具有特定的含义,不同于字符原来的意义(就是转变原来的意义),故称“转义”字符
常用:\n换行
\t水平制表符,用来占位,一般相当于四个空格,或者tab键的功能
重点:\ddd ddd表示1~3个八进制的数字。如:\130
\xdd dd表示1~2个十六进制数字。如:\x30
1.3 字符串和字符常量的区别
①从表示形式上看:字符常量是用单引号括起来的,字符串是用双引号括起来的
②从字符的个数上看:字符常量只能是单个字符,字符串常量可以是0个或多个字符
③有字符变量,但没有字符串变量。C语言没有专门的字符串类型变量,而是使用字符型数组或字符型指针来存储字符串
④字符常量在内存中占用1个字符,字符串常量在内存的字节数是字符个数加1。(每一个字符串的末尾隐藏存在一个字符串结束标志‘\0’)
1.4 字符型数据在内存的存储形式
①在C语言中字符型数据包括字符和字符串两种。
②字符型数据在内存中存储的是字符的ASCII码值的二进制,一个字符的存储占用一个字节。
常用的ASCII值:
0-ASCII码值48
A-ASCII码值65(小写字母=大写字母+32)
③%c-打印字符-ASCII值(十进制)
1.5 注释
(1)注释有两种风格:
①C语言风格的注释/*xxxxxx*/
段注释
缺陷:不能嵌套注释
②C++风格的注释//xxxxxx
行注释
(2)理解
①注释可以出现在程序中的任意位置
②注释不参与程序的运行,主要用于对程序的某些关键部分进行说明,其目的是提高程序的可读性
2.printf的输出格式
2.1 说明符
说明符用于规定输出数据的类型,含义如下:
2.2 flags(标志)
标志(flags)用于规定输出样式,含义如下:
2.3 width(最小宽度)
最小宽度(width)用于控制显示字段的宽度,取值和含义如下:
2.4 .precision(精度)
精度(.precision)用于指定输出精度,取值和含义如下:
我们上机试试:
代码实例:
#include<stdio.h> int main() { //标志用来规定输出样式 //%-5d 占5个终端位宽 左对齐,不足右边填充空格 printf("#%5d#\n", 123); //%05d 占5个终端位宽 右对齐,不足用0补齐 printf("#%05d#\n", 123); //%+d 输出正负号 printf("#%+d# #%+d#\n", 123, -123); //% d 正号用空格替代,负号输出 printf("#% d# #% d#\n", 123, -123); // %#x 输出进制的前导符 printf("%x %X %#x\n", 12, 12, 12 ); //最小宽度用来控制显示字段的宽度 //输出的字段长度(实际数据)大于最小宽度,不会截断输出 printf("%2d\n", 123); //%10d 123 输出的字段长度小于最小宽度,默认右对齐,左边补空格 printf("#%10d#\n", 123); //%*d 指示下一个参数是宽度 printf("%*d\n", 3, 123);//等价于printf("%3d\n",123) //精度用来指定输出精度 //对于f说明符:要在小数点后输出的小数位数 //若小数实际位数大于指定的精度n, //则截取n位小数,并自动对后面的数进行四舍五入 printf("%.2f\n", 3.45); printf("%.1f\n", 3.45); printf("%.5f\n", 3.45); return 0; }
3. 表达式求值
表达式求值的顺序一部分是由操作符的优先级和结合性决定。
同时需注意:有些表达式的操作数再求值的过程中可能需要转换为其他类型。
操作数在求值过程中是否需要转换为其他类型是我们现在主讲的重点。
3.1 隐式类型转换
C语言的整形算术运算总是至少以缺省(缺省就是默认的意思)整形类型的精度来进行的。
为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整形,这种转换称为整形提升。
整形提升的意义:
表达式的整形运算要在CPU的相应运算器件内执行,CPU内整形运算器(ALU)的操作数的字节长度一般就是int的字节长度,同时也是CPU的通用寄存器的长度。
所以,表达式中各种长度可能小于int长度的整形值,都必须先转换为int或unsigned int,然后才能送入CPU去执行运算。
代码实例:
代码1:
//C的整形算术运算总是至少以缺省整形类型的精度来进行的。 //为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整形, //这种转换称为整形提升 //整形提升是针对类型小于整形的 //char short int …… // 1 2 4 #include<stdio.h> int main() { char ch = 'a'; short data = 20; //char short类型在使用之前整形提升 printf("%d\n", sizeof(ch + data));//4 printf("%d\n", sizeof(ch + ch));//4 printf("%d\n", sizeof(data + data));//4 return 0; }
整形提升是针对于类型小于整形的,如char short类型操作数在 参与表达式运算之前 转换为int型
代码2:
如何进行整形提升呢?
整形提升是按照变量的数据类型的符号位来提升的
int main() { //负数的整形提升 char ch1 = -1; //变量ch1的二进制位(补码)中只有8个比特位 //11111111 //因为char为有符号的char //所以整形提升的时候,高位补充符号位,即为1 //提升之后的结果为: //1111111111111111111111111111111 //正数的整形提升 char ch2 = 1; //ch2的补码: //00000001 //符号位为0,提升之后的结果为: //00000000000000000000000000000001 //无符号整形提升,高位补0 return 0; }
代码3:char之间的运算
#include<stdio.h> int main() { //VS编译器char---->signed char char a = 3; //整形的二进制:00000000000000000000000000000011 //截断 //char存储:00000011 char b = 127; //整形的二进制:00000000000000000000000001111111 //截断 //char存储:01111111 char c = a + b; //a-00000011 //b-01111111 //整形提升 //a-00000000000000000000000000000011 //b-00000000000000000000000001111111 //a+b:00000000000000000000000010000010 //截断 //c-10000010 printf("%d\n", c); //%d是打印十进制的整数 //c-10000010 //整形提升 //补码:11111111111111111111111110000010 //过程:11111111111111111111111110000001 //原码:10000000000000000000000001111110 return 0; }
运行结果是多少呢?
-126
tip:我们了解一下char的取值范围:
3.2 算术转换
如果某个操作符的各个操作数属于不同的类型,那么除非其中一个操作数转换为另一个操作数的类型,否则操作就无法进行。下面的层次体系称为寻常算术转换。
long double
double
float
unsigned long int
long int
unsigned int
int
由低到高转换(从下向上)
注意:算术转换讨论的类型都是大于或等于int型的
警告:但是算术转换要合理,要不然会有一些潜在的问题。(就高不就低,否则可能精度丢失)
例:
float f=3.14;
int num = f;//隐式转换,会有精度丢失
代码1:有符号和无符号的转换
#include<stdio.h> int main() { int data1 = -20; unsigned int data2 = 10; //有符号data1和无符号data2参加计算的时候 //会先将data1转换成无符号(-20的补码很大的整数) //if多分支语句判断 if ((data1 + data2) > 0) { printf(">0\n"); } else if ((data1 + data2) < 0) { printf("<0\n"); } return 0; }
运行结果:
代码2:int和double的转换
1. #include<stdio.h> 2. 3. int main() 4. { 5. int data = 10; 6. printf("%d\n", sizeof(data + 3.14)); 7. return 0; 8. }
运行结果:
总结:
①低于int型的char,short为整形提升
②等于或大于int型的为算术转换(从低字节到高字节)
③只要参与表达式运算,就有可能发生
4.C语言的三种基本结构
C语言是一门结构化的程序设计语言
C语言支持三种结构:
4.1 顺序结构
4.2 选择结构
4.3 循环结构
想一想,我们生活的所有事是不是都是这三种结构组成的,有了这三种结构我们就可以解决生活中的任何事!!!
4.1 顺序结构
顺序结构的程序设计是最简单的,只要按照解决问题的顺序写出相应的语句就行,它的执行顺序是自上而下,依次执行。(从主函数main开始)
4.2 选择结构(分支语句)
选择结构用于判断给定的条件;根据判断的结果判断某些条件,根据判断的结果来控制程序的流程。
C语言中用if语句和switch语句来表达选择结构。
4.2.1 if语句的三种形式
(1)单分支if语句
①语法结构:
if(条件表达式)
{
语句;
}
②流程图:
③功能:如果表达式的值为真,则执行其后面的语句,否则不执行该语句。
(2)双分支if语句
①语法结构:
if(条件表达式)
{
语句1;
}
else
{
语句2;
}
②流程图:
③功能:如果表达式的值为真,则执行语句1,否则执行语句2。
④if……else……等同条件操作符
注:if……else……是一个语句,不要想成两个语句了。else子句不能作为语句单独使用,它必须是if语句的一部分,与if配对使用。
(3)多分支if语句
①语法结构:
if(条件表达式1)
{
语句1;
}
else if(条件表达式2)
{
语句2;
}
……
else if(条件表达式n)
{
语句n;
}
else
{
语句n+1;
}
②流程图:
③功能:依次判断表达式的值,当出现一个表达式的值为真时,则执行其对应的语句,然后跳转到整个if语句之后继续执行程序;如果所有表达式均为假,则执行else后的语句,即语句n+1.
④else if(表达式):表达式如果当且仅当只有一种条件时可以省略if
⑤单分支if语句VS多分支if语句:多分支更好,因为单分支if语句,每个都要判断,多分支if语句判断为真就跳到整个if语句之后了。即多分支if语句效率更高。
加油站:
①注意:在if和else后面只能控制一条语句--->要控制多条语句,此时要用花括号“{}”将这几条语句括起来成为一个复合语句(也即代码块,这里的一对{}就是一个代码块)
②if语句的嵌套:当if语句的操作语句中包含其他if语句时,称为嵌套的if语句。
③else的匹配(悬空else):else总是与它前面最近的一个尚未匹配的if相匹配。
④if语句允许嵌套,但不建议嵌套层数太多(一般为7层)
4.2.2 switch语句
switch语句也是一种分支语句,是多分支选择语句。
有了if语句可以解决多分支选择,那为什么还要加上switch语句呢?
因为:如果分支较多,则if语句层次多,程序冗长且可读性降低。(即太复杂)
那我们就得有不一样的语法形式。
这就是switch语句。
1、语法结构:
switch(整形表达式)
{
case 整形常量表达式1:
语句1;
//break;//实现真正的分支
……
default:
语句n;
//break;
}
2、功能:当表达式的内容与某个case标签后的常量相等后,就执行该case下的语句,break表示该case以后的内容不会执行,如果没有跟break,会继续执行当前case之后的case分支。
3、在switch语句中的break:
①作用:在switch语句中,我们没有办法直接实现分支,搭配break使用才能实现真正的分支。
②那是不是我们case后面一定加break呢?
不是,我们要根据实际问题选择是否加break。(如:多个case共用一组执行语句时,共用的执行语句写在最后一个,其余可省略。)
③break只能跳出本次的switch语句(注:像打游戏一样只能一关一关过,一次只能过一关)
4、default子句:
①功能:当表达式的值与所有的case标签的值都不匹配时,这个default子句后面的语句就会执行。(注:每个switch语句中只能出现一条default子句)
②虽然default子句可以出现在switch中的任意位置,但是建议放在最后。(习惯上我们前面处理正确的,后面处理异常的)
③虽然不加default子句,switch语句也是对的,但是每个switch语句中都放一条default子句是个好习惯,甚至可以在后面再加一个break。
5、注意:
①switch后的表达式为整形,如:int 、long、short、char(字符内存是二进制的ASCII值)
②每一个case的常量表达式的值,必须互不相同,否则就会出现互相矛盾的现象。
6、switch语句可以嵌套使用。
4.3 循环结构
循环结构是指在程序中需要反复执行某个功能而设置的一种程序结构。它由循环体中的条件,判断继续执行某个功能还是退出循环。根据判断条件,循环又可细分为以下两种形式:先判断后执行的循环结构和先执行后判断的循环结构。
结构理解:
①循环结构可以看成是一个条件判断语句和一个向回转向语句的结合。
②循环结构包含的三要素:循环变量、循环体和循环终止条件。
4.3.1 在C语言中常用的三种循环:
循环的使用:for>while>do……while(具体按实际情况使用)
(1)while语句--->先判断,在执行
1、语法结构:
while(条件表达式)
{
循环体;
}
2、流程图
3、功能:条件表达式为真,才执行后面的循环语句,否则不执行。
(2)for语句--->与while一样的效果,但是形式更优
1、语法结构:
for(初始化部分;循环条件部分;调整部分)
{
循环体;
}
2、流程图:
3、理解for的三个部分
①初始化部分:用来初始化循环变量,注只能在循环开始时,执行一次
②循环条件部分:用来判断循环什么时候终止(每次循环都要执行,如果循环条件为true,进入循环体,如果为false,退出循环)
③调整部分:用于循环条件的调整,每次循环结束的时候执行(使循环条件趋于false的语句)
4、一些for循环的变种
例如:for(;;)
①for循环中的初始化部分,判断部分,调整部分是可以省略的,但是不建议初学者省略,容易导致问题。
②省略掉判断部分,判断就恒为真,循环就死循环。
5、循环嵌套:外层循环的次数 * 内层循环的次数 = 总循环次数
(3)do……while语句--->先执行,再判断(至少执行一次)
1、语法结构:
do
{
循环体;
}while(条件表达式);
2、流程图:
3、注意:while(表达式)后面有‘;’
4、特点:先执行,再判断--循环至少执行一次,使用的场景有限,所以不常用。
4.3.2 break和continue
(1)break的作用和应用
①break只在switch和循环中应用
②作用:在switch中break的作用是实现分支;在循环中break的作用是终止循环
③应用在某种情况(条件)满足时,想让循环提前终止
④注意:break只能跳出离它最近的一层循环。
(2)continue的作用和应用
①continue只在循环中应用
②作用:终止本次循环,进入下一次循环
③应用:当某种情况(条件)满足时,这一次循环后面的代码我不想它执行
4.3.3 goto语句(跳转)
goto语句为无条件转向语句。
1、语法结构:
goto 语句标号;
2、goto语句的一般有两个用法:
①与if语句一起构成循环结构
②最常见的用法就是终止程序在某些深度嵌套的结构的处理过程。
例如:一次跳出两层循环或多层循环。
多层循环这种情况使用break是达不到目的的,它只能从最内层循环退出到上一层的循环。(注:break一次只能跳出一层循环)
案例:求1~100的和
代码1:for循环实现
#include<stdio.h> int main() { int i = 0;//循环变量 int sum = 0;//和 //从1遍历到100, for (i = 1; i <= 100; i++) { sum += i;//累加 } //输出 printf("sum=%d\n", sum); return 0; }
代码2:累加跳过50
#include<stdio.h> int main() { int i = 0; int sum = 0; for (i = 1; i <= 100; i++) { if (50 == i) { continue; }//当i=50时终止本次循环循环 sum += i; } //输出 printf("%d\n", sum); return 0; }
代码3:累加到9时,结束循环
#include<stdio.h> int main() { int i = 0; int sum = 0; for (i = 1; i <= 100; i++) { if (10 == i) { break; }//当i=10时终止循环 sum += i; } printf("%d\n", sum); return 0; }