C语言之基础结构

选择结构和循环结构

选择结构

例一 if结构

1
2
3
4
5
6
7
8
9
if(条件式)
{
语句1
语句2

...

语句n
}

最基础的选择结构,没什么好说的,满足条件式就执行结构中的语句

例二 if-else结构

1
2
3
4
5
6
7
8
if(条件式)
{
语句1
语句2
}
else{
语句3
}

若条件式中的语句不成立,则跳过执行else中的语句3

例三if-else_if-else结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
if(条件式1)
{
语句1
}
else if(条件式2)
{
语句2
}
else if(条件式3)
语句3
}
.....
else
{
语句n;
}

先从条件式1开始执行,若成立则执行语句1,若不成立则判断条件式2,若成立则执行语句2,以此类推,直到满足任意一个条件

若没有满足任一一个条件,则执行语句n,如若没有最后的else{语句n},则不执行任何一个语句,继续执行main函数

对于三种或更多的结构,c语言提供了switch选择结构

例四 switch结构

1
2
3
4
5
6
7
8
9
10
switch(表达式)/*首先计算表达式的值*/
{
case 常量表达式1:语句1
case 常量表达式2:语句2
case 常量表达式3:语句3
case 常量表达式4:语句4
case 常量表达式5:语句5
........
default:语句n
}

这段代码的含义是先计算表达式的值,如若满足常量表达式则执行对应的语句

1
2
3
4
5
6
7
switch(value)
{
case 1:printf("one");
case 2:printf("two");
case 3:printf("three");
default:printf("other");
}

需要注意的是,switch中每个条件(case)是比较相等与否,而else if中的条件除了可以比较相等以外,还可以满足某个区间,这是两者的使用范畴不一样,请多多上机,尝试修改代码,多多体会!

tips: break函数可以跳出选择或者循环结构,常用于从子选择(循环)跳到父选择(循环)

循环结构

C语言提供三种循环结构,分别为while循环do while循环for循环

例一 while循环

while语句创建一个循环,该循环在判断表达式为假(或0)之前重复执行。while语句是一个入口条件(entry-condition)循环,在进行一次循环之前决定是否要执行循环。因此有可能一次也不执行。循环的语句部分可以是一个简单语句或一个复合语句

1
2
3
while(条件式){
循环体语句
}

while循环是常用的循环之一,与for循环相比,它只需要满足一个条件式

下面给出实例

1
2
3
4
5
6
7
8
9
10
11
12
13
#include<stdio.h>
int main()
{
int a;
int i=0;
scanf("%d",&a);
while(i<10){
int b = a +100;
printf("%d",b);
i++;
}
return 0;
}

这是一种常用的while循环结构,通过控制变量i的值间接控制循环次数

例二 do while循环

do while语句创建一个循环,它在判断表达式为假(或0)之前重复执行。与while循环不同,do while语句是一个退出条件循环,在执行一次循环之后才决定是否要再次执行循环,因此循环至少要被执行一次。循环的语句部分可以是一个简单语句或一个复合语句

1
2
3
4
5
do{

循环体语句

}while(表达式);//这里的while后面一定要加分号!!

这里给出一个实例

1
2
3
4
5
6
7
8
9
10
#include<stdio.h>
int main()
{
int i=0;
do
{
printf("count %d\n",i);
}while(i++<20);
return 0;
}

相信聪明的你一定知道这段代码的运行结果

例三 for循环

for语句使用由分号隔开的三个控制表达式来控制循环过程。初始化表达式只在开始执行循环语句之前执行一次。如果判断表达式为真(或非0)就执行一次循环。然后计算更新表达式并再次检查判断表达式的值。for语句是一个入口条件循环,在进行一次循环之前决定是否要执行循环,因此有可能循环一次也不执行。循环的语句部分可以是一个简单语句或一个复合语句

for循环一般表达式如下

1
2
3
4
for(初始化表达式;判断表达式;更新表达式)
{
循环体语句
}

下面来看一则实例

1
2
3
4
5
6
7
8
9
10
#include<stdio.h>
int main()
{
int i;
for(i=0;i<20;i++)
{
printf("count is %d\n",i);
}
return 0;
}

这个实例是不是有点熟悉,和上面的do while循环的实例是不是有点像

仔细观察可以发现,for循环完全可以替换whiledo while循环,由于循环的三大条件:初始值增量循环条件都写在一起,避免了while循环do while循环容易遗忘初始化、增量的情况,并且书写起来更简洁、直观,控制能力更强,请理解并学会for循环,并且通过for循环理解循环的必要条件,更深刻的理解循环的本质。

循环的嵌套

和选择结构一样,循环结构也支持嵌套,具体的运行规则类似下面的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
#include<stdio.h>
int main()
{
int i,j;
for(i=0;i<10;i++)
{
for(j=0;j<10;j++)
{
printf("i=%d,j=%d\n",i,j);
}
}
return 0;
}

这是一个for循环嵌套可以观察到父循环和子循环,子循环执行的次数由父循环决定(注意,这里的次数并非是子循环中的语句,而是子循环本身,倘若子循环中的表达式一直不被满足,子循环中的语句也是不会执行的,换句话讲,子循环就是父循环的循环体语句)这种情况下,内部的循环全部执行完(即j从0执行到10),外部的循环才会执行一次(即i会增加1),这就是循环嵌套的最大的规律和理解关键,即外部循环执行一次,内部循环执行一轮!

C语言与函数

函数的定义

C源程序是由函数组成的。最简单的程序有一个主函数main(),但实用程序往往由多个函数组成,由主函数调用其他函数,其他函数也可以互相调用。函数是C源程序的基本模块,程序的许多功能是通过对函数模块的调用来实现的,学会编写和调用函数可以提高编程效率。

函数的定义通常包含以下内容:

1
2
3
4
5
返回值类型 函数名(形参表说明) /*函数首部*/
{
说明语句 /*函数体*/
执行语句
}

对上面的定义形式进行以下说明:

(1)“返回值类型”是指函数返回值的类型。函数返回值不能是数组也不能是函数,除此之外任何合法的数据类型都可以是函数的类型,如:int,long,float,char等。函数类型可以省略,当不指明函数类型时,系统默认的是整型。

(2)函数名是用户自定义的标识符,在C语言函数定义中不可省略,须符合C语言对标识符的规范,用于标识函数,并用该标识符调用函数。另外函数名本身也有值,它代表了该函数的入口地址,使用指针调用函数时,将用到此功能。

(3)形参又称为“形式参数”。形参表是用逗号分隔的一组变量说明,包括形参的类型和形参的标识符,其作用是指出每一个形参的类型和形参的名称,当调用函数时,接收来自主调函数的数据,确定各参数的值。

(4)用{ }括起来的部分是函数的主体,称为函数体。函数体是一段程序,确定该函数应完成的规定的运算,应执行的规定的动作,集中体现了函数的功能。函数内部应有自己的说明语句执行语句,但函数内定义的变量不可以与形参同名。花括号{ }是不可以省略的。

根据函数定义的一般形式,可以定义一个最简单的函数:

1
2
3
4
int add()
{
return 0;
}

这是C语言中一个合法的函数,函数名为add。它的函数返回值为int,没有形参表,同时函数体内只有一条因为返回值为int而必须有的返回值语句。实际上函数add不执行任何操作和运算,它是一个空函数,在一般情况下是没有用途的,但在程序开发的过程中有时是需要的,常用来代替尚未开发完毕的函数

函数的调用

主调函数使用被调函数的功能,称为函数调用。在C语言中,只有在函数调用时,函数体中定义的功能才会被执行。C语言中,函数调用的一般形式为:

1
函数名(类型 形参,类型 形参...);

对无参函数调用时则无实际参数表。实际参数表中的参数可以是常数、变量或其他构造类型数据及表达式,各实参之间用逗号分隔。

无参函数例如

1
2
3
4
void printWelcome()
{
printf("Welcome to the world of C programming!\n");
}

在C语言中,可以用以下几种方式调用函数

(1)函数表达式:函数作为表达式中的一项出现在表达式中,以函数返回值参与表达式的运算。这种方式要求函数是有返回值的,例如:

1
z=max(x,y);

是一个赋值表达式,把max的返回值赋予变量z。

(2)函数语句:函数调用的一般形式加上分号即构成函数语句,例如:

1
2
printf("%d",a);
scanf("%d",&b);

都是以函数语句的方式调用函数。

(3)函数实参:函数作为另一个函数调用的实际参数出现。这种情况是把该函数的返回值作为实参进行传送,因此要求该函数必须是有返回值的,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include<stdio.h>
int max(x,y){
scanf("%d%d",&x,&y);
if(x>y)
{
return x;
}
else
{
return y;
}
}
int main()
{
int a,b;
printf("%d",max(a,b));/*把max调用的返回值作为printf函数的实参*/
}

在主调函数中调用某函数之前应对该被调函数进行声明,在主调函数中对被调函数进行声明的目的是使编译系统知道被调函数返回值的类型,以便在主调函数中按此种类型对返回值进行相应的处理。其一般形式为:

1
类型说明符 被调函数名(类型 形参,类型 形参...);

需要注意的是,函数的声明函数的定义有本质上的不同,主要区别在以下两个方面:

(1)函数的定义是编写一段程序,应有函数的具体功能语句——函数体,而函数的声明仅是向编译系统的一个说明,不含具体的执行动作。
(2)在程序中,函数的定义只能有一次,而函数的声明可以有多次。

有关变量的一些点

在C语言中,变量是对程序中数据所占内存空间的一种抽象定义,定义变量时,用户定义变量的名、变量的类型,这些都是变量的操作属性。不仅可以通过变量名访问该变量,系统还通过该标识符确定变量在内存中的位置。

在计算机中,保存变量当前值的存储单元有两类,一类是内存,另一类是CPU的寄存器。变量的存储类型关系到变量的存储位置,C语言中定义了4种存储属性,即自动变量(auto)、外部变量(extern)、静态变量(static)和寄存器变量(register),它关系到变量在内存中的存放位置,由此决定了变量的保留时间和变量的作用范围。

变量的保留时间又称为生存期,从时间角度,可将变量分为静态存储动态存储两种情况:

(1)静态存储是指变量存储在内存的静态存储区,在编译时就分配了存储空间,在整个程序的运行期间,该变量占有固定的存储单元,程序结束后,这部分空间才释放,变量的值在整个程序中始终存在。

(2)动态存储是指变量存储在内存的动态存储区,在程序的运行过程中,只有当变量所在的函数被调用时,编译系统才临时为该变量分配一段内存单元,函数调用结束,该变量空间释放,变量的值只在函数调用期存在。

关于动态储存,有一个例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include<stdio.h>
void spawn(int x,int y)
{
int s;
s=x;
x=y;
y=s;
}
int main()
{
int a=10,b=20;
spawn(a,b);
printf("%d %d",a,b);
return 0;
}

看似合理的代码,运行起来却完全不是我们设想的交换a,b的值

因为spawn函数交换的仅仅是这个函数内a,b的数值,main函数内的值不会改变,这里涉及到栈与栈空间,请看我的博客

https://shanlinchuanze.github.io/2024/09/29/%E6%88%91%E4%B8%AA%E4%BA%BA%E5%AF%B9%E4%BA%8E%E6%A0%88%E7%A9%BA%E9%97%B4%E7%9A%84%E7%90%86%E8%A7%A3/

变量的作用范围又称为作用域,从空间角度,可以将变量分为全局变量局部变量

(1)局部变量是在一个函数或复合语句内定义的变量,它仅在函数或复合语句内有效,编译时,编译系统不为局部变量分配内存单元,而是在程序运行过程中,当局部变量所在的函数被调用时,编译系统根据需要,临时分配内存,调用结束,空间释放。

(2)全局变量是在函数之外定义的变量,其作用范围为从定义处开始到本文件结束,编译时,编译系统为其分配固定的内存单元,在程序运行的自始至终都占用固定单元。

1.自动变量
函数中的局部变量,如不专门声明为static存储类别,都是动态地分配存储空间的,数据存储在动态存储区中。函数中的形参和在函数中定义的变量(包括在复合语句中定义的变量)都属此类,在调用该函数时系统会给它们分配存储空间,在函数调用结束时就自动释放这些存储空间,这类局部变量称为自动变量

自动变量用关键字auto进行存储类别的声明,例如声明一个自动变量:

1
2
3
4
int fun(int a)
{
auto int b,c=3;/*定义b,c为自动变量*/
}

是函数fun()的形参,b、c是自动变量,并对c赋初值3。执行完fun()函数后,自动释放a、b、c所占的存储单元。

2.外部变量,即全局变量,是在函数的外部定义的,它的作用域为从变量定义处开始,到本程序文件的末尾。

如果外部变量不在文件的开头定义,其有效的作用范围只限于定义处到文件末尾。如果在定义点之前的函数想引用该外部变量,则应该在引用之前用关键字extern对该变量进行“外部变量声明”,表示该变量是一个已经定义的外部变量。有了此声明,就可以从“声明”处起,合法地使用该外部变量。

通常的,用extern声明外部变量,扩展程序文件中的作用域

3.静态全局变量

有时希望函数中的局部变量的值在函数调用结束后不消失而保留原值,这时就应该指定局部变量为静态局部变量,用关键字static进行声明。

通过用static类型声明后的变量,其变量的内存空间位于内存的全局静态区,仅会初始化一次,这是一个非常重要的特性,请大家通过如下的程序理解:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include<stdio.h>
static a=5;
int fun()
{
a=a*2;
printf("a=%d\n",a);
return 0;
}
int main()
{
int i=0;
for(i=0;i<10;i++)
{
fun();
}
return 0;
}

上机运行如上代码,可以观察a的值输出如下:

1
2
3
4
5
6
7
8
9
10
a=10
a=20
a=40
a=80
a=160
a=320
a=640
a=1280
a=2560
a=5120

这个结果的原因在于,每次a的值是接着上一次函数调用的值继续运算的,并非每次都初始化从5开始计算,这就是static的特性

4.寄存器变量

为提高效率,C语言允许将局部变量的值存放在CPU的寄存器中,这种变量叫做寄存器变量,用关键字register声明。使用寄存器变量需要注意以下几点:
(1)只有局部自动变量和形式参数可以作为寄存器变量。
(2)一个计算机系统中的寄存器数目有限,不能定义任意多个寄存器变量
(3)不能使用取地址运算符“&”求寄存器变量的地址

例如下列定义

1
register int a=0;//将变量a存储在寄存器上

以上内容大部分摘自C语言网,因为我本人第一次接触C语言就是从C语言网上学习,本人只是做总结和补充

C语言网地址:https://www.dotcpp.com/