【C/C++】系列文章目录
文章目录
目录
前言
本章节旨在初步认识语句,函数,数组,运算符,关键字。
一、选择、循环语句
1.选择(分支)语句
表示分支(选择)结构的语句有if语句和switch语句。
1)if语句
if语句用于表达根据一定的条件在两条流程中选择一条执行的情况。
if(表达式p)
语句1;
else
语句2;
当给定的条件p满足(即表达式p的值不为0)时,执行语句1,否则执行语句2。
C规定:else子句总是与离他最近且没有else相匹配的if语句配对。
int main()
{
int a = 0;
int input = 0;
scanf("%d", &input);//input 输入 1
if (input > 2)
if (input < 5)
a = a + 1;
else
a = a + 2;
printf("a = %d\n", a);
return 0;
}
以上这段代码,input输入1,输出a是多少呢?
该选择语句的含义是:当input大于2且小于5时,执行a+1;若input大于等于5,执行a+2,else语句跟随着if(input < 5)语句。而if(input > 2)并没有与之对应的else语句,所以条件不满足时,不进行操作。
2)switch语句
switch语句用于表示从多分支的执行流程中选择一个来执行。
switch(表达式p) {
case 常量表达式1:
语句1;
case 常量表达式2:
语句2;
……
case 常量表达式n:
语句n;
default:
语句n+1;
}
switch语句执行过程:首先计算表达式p的值,然后自上而下地将其结果值依次与每一个常量表达式的值进行匹配(常量表达式的值得类型必须与表达式p的类型相同)。匹配成功则执行该常量表达式后的语句。
遇到break时立即结束switch语句的执行,否则按顺序执行到花括号中的最后一条语句。
default是可选的,如果没有常量表达式的值与表达式p的值匹配,则执行default的语句。
注:表达式p的值必须是字符型或整型。
int main()
{
int rank = 0;
scanf("%d", &rank);
switch (rank)
{
case 1:printf("excellent 1\n"); break; //语句1
case 11:printf("excellent 11\n"); //语句2
case 12:printf("excellent 12\n"); break; //语句3
case 13:printf("excellent 13\n"); //语句4
default:printf("excellent default\n"); //语句5
}
return 0;
}
以上代码输入示例:
输入1,执行语句1,遇到break,结束执行switch语句。最终输出 excellent 1;
输入11,执行语句2,没有break,顺序向下执行语句3,在语句3遇到break,结束执行。最终输出 excellent 11, excellent 12;
输入0,没有匹配成功,执行default语句(语句5),最终输出 excellent default。
2.循环语句
1)while语句
while (表达式p)
循环体语句;
while语句含义:先计算表达式p的值,如果其值不为0(即为真),则执行循环体语句。这个过程重复进行,直到表达式p的值为0(假)时结束循环。
int main()
{
//打印数字1-10。
int i = 1;
while (i <= 10)
{
printf("%d ", i);
i++;
}
return 0;
}
代码执行过程:
- i=1,计算表达式p,满足
- 执行循环体语句,先打印,然后进行i++即i = i+1)
- 重复1,2,直到i=10执行完i++后,i变为11,不满足表达式i<=10,结束循环。
2)do…while语句
do
循环体语句;
while (表达式p);
do…while语句含义:先执行一次循环体语句,然后计算表达式p,如果表达式p的值不为0,则继续执行循环体语句,否则终止循环。
使用场景:猜数字。设置一个数字68,在1-100范围内让用户去猜。用户要先猜一个数字,判断是不是68,不是68则循环继续猜,是68,则猜对了,跳出循环。
3)for语句
for(表达式1;表达式2;表达式3)
循环体语句;
for语句含义:
- 计算表达式1(循环体初值)。
- 计算表达式2(循环条件),如果其结果不为0,则执行循环体语句(循环体),否则循环终止。
- 计算表达式3(循环增量)。
- 重复2和3。
如之前打印数字1-10的代码用for循环语句写如下:
int main()
{
//打印数字1-10。
int i = 1;
for (i = 1; i <= 10; i++)
{
printf("%d ", i);
}
return 0;
}
所以,for循环与下面的while循环等价:
表达式1;
while(表达式2){
循环体语句;
表达式3;
}
二、函数
函数是一个功能模块,用来完成特定任务。函数是C程序中的子程序。函数的特点就是简化代码,代码复用。
函数包含:
- 库函数—已经定义并随着编译系统发布的、可供用户调用的标准函数。
- 自定义函数——用户根据需要自己定义的。
1.函数的定义
定义形式如下:
返回类型 函数名 (参数表列)
{
语句系列;
return 表达式;
}
例:定义一个计算两值相加求和的函数。
int Add(int x, int y)
{
int result = 0;
result = x + y;
return result;
}
定义函数Add,参数为整型int x,和整型int y,返回值为整型int类型。
- 1—函数返回值类型
- 2—函数名称
- 3—参数列
- 4—执行的操作语句
- 5—return返回值语句
2.函数声明
返回类型 函数名(参数表列);
int Add(int, int);
或 int Add(int x, int y);
void print(int n);
3.函数调用
调用格式:函数名(实参表);
int Add(int x, int y)
{
int result = 0;
result = x + y;
return result;
}
int main()
{
int num1 = 0;
int num2 = 0;
int sum = 0;
printf("输入两个操作数:>");
scanf("%d %d", &num1, &num2);
sum = Add(num1, num2); //调用函数
printf("sum = %d\n", sum);
return 0;
}
一个函数中可以有多个return语句,在函数执行过程中,遇到任一个return语句将立即停止函数的执行,并返回到调用函数。
例:输入值大于0,返回1,小于等于0,返回-1。
int judgeNum(int n)
{
if (n > 0)
{
return 1;
}
else
{
return -1;
}
}
int main()
{
int input = 0;
scanf("%d", &input);
int res = judgeNum(input);
printf("返回的是:%d\n", res);
return 0;
}
三、数组
数组是一种集合数据类型,它由多个元素组成,每个元素都有相同的数据类型,占有相同大小的存储单元,且在内存中连续存放。
1.一维数组
1)数组定义形式
类型说明符 数组名[常量表达式];
类型说明符指定数组元素的类型,常量表达式指定数组元素的个数,必须是一个整数。
int main()
{
int temp[10];//定义数组temp,有10个元素,元素类型是整型int
char str[20];//定义数组str,有20个元素,元素类型是字符型char
float arr[5];//定义数组arr,有5个元素,元素类型是浮点型float
return 0;
}
数组下标:
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
printf("数组第6个元素的值是:%d", arr[5]);//6
return 0;
}
2)内存中存储形式
元素是连续存储的。
3)数组的初始化
定义数组时就给出数组元素的初值,成为初始化。初值放在一对画括号中,各初值之间用逗号隔开,称为初始化表。
完全初始化:
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
不完全初始化:
int primes[10] = { 1,2,3,4,5 };//前五个元素的初值分别为1,2,3,4,5,后5个元素的初值都为0.
char ch[5]={'a','b','c'};//剩余的默认是‘\0'(即0,’\0'本质就是0)
int tem[] = {1,3,5,7,9}
对于没有给出元素个数而给出了初始化表的数组定义,编译器会根据初值的个数和类型,维数组分配相应大小的内存空间。初始化表中的个数必须小于或等于数组元素个数。
2.二维数组
二维数组可视为一个矩阵。
1)定义形式:
类型说明符 数组名[常量表达式1][常量表达式2];
类型说明符指定数组元素的类型,常量表达式1指定数组行数,常量表达式2指定数组列数。
double twoDim[3][4];//定义数组towDim,为3行4列,数组元素是double类型。
这个数组在内存中占用能放12个double元素且地址连续的存储单元。
C语言中二位数组在内存中按行顺序存放。
int main()
{
int a[3][4] = { 1,2,3,4,5,6,7,8,9,10,11,12 };
printf("数组第2行第3个元素的值是:%d", a[1][2]);//7
return 0;
}
2)内存中存储形式
3)数组的初始化
完全初始化:
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 };
对于不给定数组个数的二维数组初始化,由二维数组元素在内存中的连续存放方式得出,定义二维数组的时候,行数可省略,列数不可省略,以防计算机无法计算应分配的内存空间大小。
四、运算符
1.算数运算符
运算符 | 说明 | 例 |
---|---|---|
+ | 加 | 3+2=5 |
- | 减 | 3-2=1 |
* | 乘 | 3*2=6 |
/ | 除 | 6/3 = 2 int a = 9 / 2; //4 float b = 9 / 2;//无论前面是什么输入类型,都进行的整数部分的结果 4.000000 float c = 9 / 2.0;//当两边有一个数是小数就能求得算数的结果4.500000 |
% | 取余 | 7%2 = 1(7除以2,商是3余数是1),取余得1 |
2.位运算符
位运算符要求操作数是整型数,并按二进制位的顺序来处理它们。
各种数据在计算机中表示的形式为机器数,特点是采用二进制计数制,即数的符号用0和1表示。对于带符号数,机器数的最高位是表示正负的符号位,其余为则表示数值。
带符号的机器数可采用原码,反码,补码,移码等不同编码方法。
在计算机中,采用补码进行运算。最高位符号位:0表示正号,1表示负号。
以+1(正数1),-1(负数1),int类型为例:
int类型大小为4字节,即32个bit位。则:
+1:
原码:00000000 00000000 00000000 00000001 //标红的最高位为符号位,0表示正号
反码:00000000 00000000 00000000 00000001
补码:00000000 00000000 00000000 00000001
-1:
原码:10000000 00000000 00000000 00000001 //标红的最高位为符号位,1表示负号
反码:11111111 11111111 11111111 11111110 //符号位不变,其他数值位按位取反
补码:11111111 11111111 11111111 11111111 //在反码的基础上加1
综上:正数的原码,反码,补码相同,符号位为0。负数的原码符号位为1,反码在原码的基础上,符号位不变,数值位按位取反(即0变成1,1变成0),补码在反码的基础上加1。
运算符 | 说明 | 示例 | 结果 | 备注 |
---|---|---|---|---|
~ | 取反 | char a = 0; ~a | -1 | 0的补码0000 0000 按位取反后变为 1111 1111 最高位为符号位1,所以是负数 按照负数的原码,反码,补码计算方式推出 反码为1111 1110, 进而推出原码为1000 0001, 所以结果为-1 |
& | 逐位与 ( 0&0=0;0&1=0; 1&1=1 ) | 5&2 | 0 | ![]() |
| | 逐位或 ( 0|0=0; 0|1=1; 1|1=1 ) | 5|2 | 7 | ![]() |
^ | 逐位异或 ( 0|0=0; 0|1=1; 1|1=0 ) | 2^7 | 5 | ![]() |
<< | 逐位左移 | 5<<2 | 20 | ![]() |
>> | 逐位右移 | 5>>2 | 1 | ![]() |
3.赋值运算符
将一个表达式的值赋给一个变量。
运算符 | 说明 | 示例 | 结果 |
---|---|---|---|
= | 赋值 | char a = 1; | 变量a赋值为1 |
+= | 加等 | char a = 1; a+=2; | a=a+2;//3 |
-= | 减等 | char a = 3; a-=2; | a=a-2;//1 |
*= | 乘等 | char a = 3; a*=2; | a=a*2;//6 |
/= | 除值 | char a = 6; a/=2; | a = a/2;//3 |
&= | 按位与等 | char a = 5; a&=2; | a=a&2;//0 |
|= | 按位或等 | char a = 5; a|=2; | a=a|2;//7 |
^= | 按位异或等 | char a = 2; a^=7; | a=a^7;//5 |
<<= | 按位左移等 | char a = 5; a<<=2; | a=a<<2;//20 |
>>= | 按位右移等 | char a = 5; a>>=2; | a=a>>2;//1 |
4.关系运算符
关系运算符用于数值之间的比较。结果的值为1表示关系成立,为0表示关系不成立。
不能用关系运算符对字符串进行比较,因为被比较的不是字符串的内容本身,而是字符串的地址,这没有意义。
运算符 | 说明 | 示例 | 结果 |
---|---|---|---|
== | 等于 | char a = 1; char b = 2; a == b; | 0 |
!= | 不等于 | char a = 1; char b = 2; a != b; | 1 |
< | 小于 | char a = 1; char b = 2; a < b; | 1 |
<= | 小于等于 | char a = 1; char b = 2; a <= b; | 1 |
> | 大于 | char a = 1; char b = 2; a > b; | 0 |
>= | 大于等于 | char a = 1; char b = 2; a >= b; | 0 |
5.逻辑运算符
在C语言中,0表示假,非0就表示真。
逻辑运算符的运算结果为1—表示true,为0—表示false。
运算符 | 说明 | 示例 | 结果 |
---|---|---|---|
&& | 逻辑与 当且仅当两个操作数的值都为true时,逻辑与运算的结果为true | 10 && 5 10 && 0 | 1 0 |
|| | 逻辑或 当且仅当两个操作数的值都为false时,逻辑或运算的结果为false | 10 || 5 10 || 0 0 || 0 | 1 1 0 |
! | 逻辑非 将操作数的逻辑值取反 | !20 | 0 |
6.条件运算符
条件运算符是C中唯一的三目运算符,也称三元运算符,它有3个操作数:
操作数1 ?操作数2 :操作数3
int main()
{
int n = 0;
int res = 0;
scanf("%d", &n);
if (n > 0)
{
res = 1;
}
else
{
res = -1;
}
printf("%d\n", res);
return 0;
}
以上if……else……选择语句可使用三目操作符改为以下语句,效果一致。
int main()
{
int n = 0;
int res = 0;
scanf("%d", &n);
res = n > 0 ? 1 : -1;
printf("%d\n", res);
return 0;
}
7.逗号运算符
多个表达式可以用逗号组合成一个表达式,即逗号表达式。逗号表达式的一般形式是:
表达式1,表达式2,……,表达式n,它的值是表达式n的值。
逗号运算符的用途仅在于解决只能出现一个表达式的地方却要出现多个表达式。
int main()
{
int a = 0;
int b = 3;
int c = 5;
int d = (a = b + 2, c = a - 4, b = c + 2);
printf("%d\n", d);//3
printf("%d\n", a);//5
return 0;
}
8.其他运算符
1.前置++,后置++
运算符的作用是将数值变量的值增加1,只能作用于变量而不能作用于常量或表达式。
前置++即++vale:先将变量的值增加1,然后取变量的新值参与表达式的运算。
后置++即value++:先取变量的值参与表达式的运算,然后再讲变量的值增加1。
前置++:
int main()
{
int a = 10;
int b = ++a;
printf("%d\n", b); // 11
printf("%d\n", a); // 11
return 0;
}
后置++:
int main()
{
int a = 10;
int b = a++;
printf("%d\n", b); // 10
printf("%d\n", a); // 11
return 0;
}
2.前置--,后置--
前置--,后置--分别与前置++,后置++一样,只是增加1变为减少1.
3.显示类型转换
(类型名)(表达式)
(int)(x+y):将(x+y)的值转换为int型,(int)x+y:是将x转换为int型后再与y相加。
类型转换:
4.数组引用[]、函数调用()、结构成员.和->
由数组模块了解到使用[元素下标]访问数组元素。
由函数模块了解到使用函数名(参数列)调用函数。
访问结构体成员使用.或者->。
5.sizeof
sizeof用于计算表达式或数据类型的字节数,其运算结果与系统有关。
由上图可得出:
- sizeof是一个运算符,不是函数。
- sizeof 计算变量时可去掉括号,计算类型时不能去掉括号。
计算数组元素个数:
int main()
{
int a[5] = { 0 };
int sz = sizeof(a) / sizeof(a[0]);//int sz = sizeof(a) / sizeof(int);
printf("数组个数为:%d\n", sz);
return 0;
}
6.其他
运算符 | 说明 |
- | 负值 |
+ | 正值 |
& | 取地址 |
* | 间接访问操作符(解引用操作符) |
五、关键字
if,else,switch,while,do,for
break,case,continue,default,return,goto
char,short,int,long,float,double,signed,unsigned,void,struct,union,enum
auto,const,extern,register
sizeof,static,typedef,volatile
1.signed,unsigned
signed有符号的
unsigned无符号的
以char类型的-1在计算机中补码为:11111111
如果这段补码是无符号的,那么最高位标红的1不是符号位了,也变成了数值位,相当于补码:11111111 这是一个正数,根据正数的原码,反码,补码相同的计算规则,则这个无符号的数是255。
2.关键字typedef
typedef 顾名思义是类型定义,这里应该理解为类型重命名。
//将unsigned int 重命名为uint_32, 所以uint_32也是一个类型名
typedef unsigned int uint_32;
int main()
{
//观察num1和num2,这两个变量的类型是一样的
unsigned int num1 = 0;
uint_32 num2 = 0;
return 0;
}
3.auto
auto自动的。
int main()
{
auto int a = 1;
//等价于 int a = 1;//auto被省略了
return 0;
}
局部变量都是auto修饰的,所以auto就可以省略了。
4.关键字static
1)修饰全局变量-静态全局变量
由之前章节可知,extern 声明外部源文件的变量,即可在本源文件中使用声明过的变量。如下图:
当static修饰global_v.c中的变量num以后,test.c中无法识别变量num了。
结论:
一个全局变量被static修饰,使得这个全局变量只能在本源文件内使用,不能在其他源文件内使用。
全局变量具有外部链接属性,但是被static修饰之后,就变成了内部链接属性,其他源文件就不能链接到这个静态的全局变量了。
2)修饰局部变量-静态局部变量
void test()
{
int a = 1;
a++;
printf("%d ", a);
}
int main()
{
int i = 0;
while (i < 10)
{
test();
i++;
}
printf("\n");
return 0;
}
// 结果输出 2 2 2 2 2 2 2 2 2 2
以上代码输出结果为2 2 2 2 2 2 2 2 2 2。经static修饰之后,
void test()
{
static int a = 1;
a++;
printf("%d ", a);
}
int main()
{
int i = 0;
while (i < 10)
{
test();
i++;
}
printf("\n");
return 0;
}
输出结果为:2 3 4 5 6 7 8 9 10 11
可以看出static修饰的局部变量,改变了局部变量的生命周期。在a离开test函数的范围后没有被销毁。究其原因是计算机内存划分为栈区、堆区,静态区,各区存放数据如下:
所以 static修饰的局部变量改变了局部变量的生命周期,本质上是改变了变量的存储类型。
既然改变了它的存储类型,那他会像全局变量一样吗?
结论:
static修饰局部变量虽然改变了局部变量的存储类型,放在了静态区,延长了它的生命周期,让它在出了test函数大括号范围内没有被销毁。但它并不能想全局变量一样哪里都可以使用,可用范围只在定义它的函数内。
3)修饰函数-静态函数
可执行,打印出5.static修饰后如下:
报错,无法解析外部符号Add。
结论:
一个函数被static修饰,使得这个函数只能在本源文件内使用,不能在其他源文件内使用。
5.#define 定义常量和宏
1)#define定义标识符常量
#define MAX 1000
2)#define定义宏
宏是一种替换。宏定义是用宏名来表示一个字符串,在宏展开时又以该字符串替换宏名,这只是一种简单的替换。
#define ADD(x, y) x + y
int main()
{
int sum = ADD(2, 3);
printf("sum = %d\n", sum); //5
sum = 10 * ADD(2, 3);
printf("sum = %d\n", sum); // 23
return 0;
}
第二个sum结果是23,因为宏在展开替换时替换为了:
sum = 10 * 2 + 3
想要得到先计算加和,须重新定义宏为:
#define ADD(x,y) ((x)+(y))
注:define,include不是关键字,是预处理命令。
6.register
register寄存器关键字。
int main()
{
register int a = 1;//建议将变量a存储到寄存器中
return 0;
}
这个关键字请求编译器尽可能的将变量存在CPU内部寄存器中,但具体存储位置还是编译器决定。
计算机中存储器结构有:寄存器-主存-外村和寄存器-高速缓存-主存-外存结构。
高速缓存cache的出现是由于CPU的速度和性能提高很快而主存速度慢。
总结
初识if…else…,switch分支语句,while,do…while,for循环语句,数组定义,数组元素的访问,一维数组,二维数组,数组下标,函数定义,函数声明,函数调用,操作符(算术操作符,逻辑操作符,位操作符,赋值操作符,关系操作符,三目操作符等),关键字(typedef,auto,extern,static,register,#define定义宏等)。