C程序的基础框架
"最少组成",写任何代码之前先敲好
基础变量的认知
要先定义再使用,例如:int a = 3;
变量的三要素: 变量名 变量值 存储单元(地址)
标识符
由字母数字下划线组成,且只能以下划线或者字母开头,不能以数字开头。
区分大小写
注意:编译系统将大写字母和小写字母认为是两个不同的字符。因此,sum和SUM是两个不同的变量名,同样,Class和class也是两个不同的变量名。一般而言,变量名用小写字母表示,与人们日常习惯一致,以增加可读性。
计算机的数据类型
整数类型:基本整型(int) 短整型(short int) 长整型(long int) 双长整型(long long int)
字符型(char) 布尔型(bool)
浮点类型: 单精度浮点型(float) 双精度浮点型(double) 复数浮点型(float_complex,double_comple ,long long_comple)
枚举类型(enum):
例如一周友7天可以这样声明
enum weekdays{mo,tu,wends,thur,fri,satur,sun};
enum weekdays w;
w = wends;
注意:枚举类型的值默认从零开始
派生类型:指针类型(*) 数组类型( [ ] ) 结构体类型( struct ) 共用体类型( union ) 函数类型
printf()函数中%m.nf输出指定宽度
在C语言的输出中,%m.nf意义:
1、f表示输出的数据是浮点数;
2、n表示输出的数据保留小数点后n位小数,第n+1位四舍五入,若不足n位则补0;
3、m表示输出数据在终端设备上占有m个字符,并右对齐,如果实际的位数小于m时,左边用空格补足,如果实际位数大于7时,向右扩展输出。
比如:
printf("%4f\n",123.4);
printf("%2f\n",123.4);
printf("%.4f\n",123.4);
输出结果为:
123.4
123.4
123.4000
scanf( )函数的使用
1)scanf( )函数中的"格式控制"后面应当是变量地址,而不是变量名。例如,若a和b为整型变量,如果写成
scanf("%f%f%f"a,b,c);
是不对的。应将“a,b,c"改为"&a,&b,&c"。许多初学者常犯此错误。
2)如果在"格式控制字符串"中除了格式声明以外还有其他字符,则在输入数据时在对应得位置上应输入与这些字符相同得字符,如果有
scanf("a=%f,b=%f,c=%f",&a,&b,&c);
在输入数据时,应在对应的位置上输入同样的字符,即输入
a=1,b=3,c=2
3) 在输入数值数据时,如输入空格,回车,Tab键或遇非法字符(不属于数值的字符),认为该数据结束,例如:
scanf("%d%c%f",&a,&b,&c);
1234a123o.26
第1个数据对应%d格式,在输入1234之后遇字符'a',因此系统认为数值1234后已没有数字了,第1个数据应到此结束,就把1234送给变量a。把其后的字符'a'送给字符变量b,由于%c只要求输入一个字符,系统判定该字符已输入结束,因此输入字符a之后不需要加空格。字符'a'后面的数值应送给变量c。如果由于疏忽把1230.26错打成123o.26,由于123后面出现字母o,就认为该数值数据到此结束,将123送给变量c,后面几个字符没有被读入。
其它输入输出的方式
getchar( )从标准输入中获取一个字符
putchar( )将一个字符写入标准输出中
puts( ) 将字符串写入标准输出中
gets( )从标准输入中获取字符串
#include<stdio.h>
int main(void)
{
char c;
puts("请输入一个字符:");
c = getchar();
putchar(c);
putchar('\n');
return 0;
}
puts( )跟printf( )的区别:
1.puts( )自动加入换行符,printf( )需要手动加入换行符
2.printf( )支持多种格式输出,puts( )只支持字符串输出
输入输出练习题
练习:从键盘输入一个大写字母,在显示屏上显示对应的小写字母。
#include<stdio.h>
int main(void)
{
char c;
puts("请输入一个大写字母:");
c = getchar();
puts("对应的小写字母为:");
putchar(c+32);
putchar('\n');
return 0;
}
流程控制if else语句
C语言提供6种关系运算符:
1)< 小于
2)<= 小于等于
3)> 大于
4)>= 大于等于
以上4个关系运算符的优先级相同
5) == (等于)
6) != (不等于)
以上2个关系运算符的优先级相同,但是优先级小于前面四个关系运算符
注意:
关系运算符的值只能是0或1。
关系运算符的值为真时,结果值都为1。
关系运算符的值为假时,结果值都为0。
使用if控制进行代数法交换值
练习:输入三个数,使使之按从小到大的顺序输出(冒泡排序)
#include<stdio.h>
int main(void)
{
int i,j;
int arr[3];
int temp;
puts("请输入三个整数:");
scanf("%d%d%d",&arr[0],&arr[1],&arr[2]);
/*冒泡排序*/
for( i = 0; i < 2; i++)
{
for(j = 0;j < 2 - i; j++)
{
if(arr[j] > arr[j+1])
{
temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
printf("arr[0] = %d, arr[1] = %d, arr[2] = %d\n",arr[0],arr[1],arr[2]);
puts("请输入三个整数:");
scanf("%d%d%d",&arr[0],&arr[1],&arr[2]);
/*选择排序:外层循环指明正在处理数组的哪一个元素,
内层循环决定存放在该元素上的值*/
for(i = 0; i < 2; i++)
{
for(j = i+1;j < 3; j++)
{
if(arr[i] > arr[j])
{
temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
}
printf("arr[0] = %d, arr[1] = %d, arr[2] = %d\n",arr[0],arr[1],arr[2]);
return 0;
}
逻辑判断与或非
C语言有三种逻辑运算符(与或非)
与运算符: && 例如:a && b 如果a和b都为真,则结果为真,否则为假
或运算符: || 例如:a || b 如果a和b有一个以上为真,则结果为真,二者都为假时,结果为假
非运算符: ! 例如: !a 如果a为假,则!a为真;如果a为真,则!a为假
ifelse编程练习
输入一个字符,判断它是否为大写字母,如果是,将它转换成小写字母,如果不是,不转换,然后输出最后得到的字符。
#include<stdio.h>
int main(void)
{
char c;
puts("请输入一个字符:");
c = getchar();
if( c >= 97 && c <= 122)
putchar(c);
else if(c >=65 && c <= 90)
printf("对应的小写字母为:%c",c+32);
else
printf("请输入正确的字符!");
putchar('\n');
return 0;
}
列表选择switchcase语句
if语句只有两个分支可供选择,而实际问题中常常需要用到多分支的选择。例如,学生成绩分类等。当然这些都可以用嵌套的if语句来处理,但如果分支较多,则嵌套的if语句层数多,程序冗长而且可读性降低。C语言提供switch语句直接处理多分支选择。
#include<stdio.h>
int main(void)
{
int idata;
puts("请输入一个整数:");
scanf("%d",&idata);
switch(idata)
{
case 1:
case 2:
puts("满足情况1或者情况2");
break;
case 3:
puts("满足情况3");
break;
default:
puts("不存在该情况!");
break;
}
return 0;
}
注意事项:
1)每个分支都需要加上break;否则会执行多条分支语句
2)默认分支可有可无;注意default不要拼写错误,拼写错误编译器是不会报错的。
3)case的标签可以是整型数,或者字符型(字符型本质也是整型数)。
switch练习
练习:学生成绩的划分,判断score/10的分数属于哪个分支。
#include<stdio.h>
int main(void)
{
int score;
puts("请输入学生的成绩:");
scanf("%d",&score);
switch(score/10)
{
case 1:
case 2:
case 3:
case 4:
case 5:
puts("成绩不合格");
break;
case 6:
puts("成绩合格");
break;
case 7:
case 8:
puts("成绩良好");
break;
case 9:
case 10:
puts("成绩优秀");
break;
default:
puts("成绩非法!");
break;
}
return 0;
}
练习:根据x的值,算出对应y的值
#include<stdio.h>
int main(void)
{
int x;
puts("请输入x的值:");
scanf("%d",&x);
if(x < 1)
{
printf("y = %d\n",x);
}else if(x >= 1 && x < 10)
{
printf("y = %d\n",2*x -1);
}else if( x >= 10)
{
printf("y = %d\n",3*x - 11);
}else
{
printf("输入的x值有误\n");
}
return 0;
}
while循环
while循环语句的形式:
while(循环条件)
{
循环语句
};
只要循环条件为非0,就会一直循环下去;只有当循环条件为0,才能结束循环.
while循环计算1到100的和
#include<stdio.h>
#include<stdlib.h>
int main(void)
{
int sum = 0;
int n = 100;
while(n)
{
sum += n;
n--;
}
printf("0到100的和为:%d\n",sum);
return 0;
}
whlie和do while的区别
do while先循环一次然后再进行循环条件的判断
#include<stdio.h>
#include<stdlib.h>
int main(void)
{
do
{
printf("do-while-\n");
}while(0);
while(0)
{
printf("while\n");
}
return 0;
}
while的表达式及for循环等价引入
1)无限循环的两种写法: while(1); for(;;);
2)for循环 和 while循环的等价形式如下:
for(表达式1;表达式2;表达式3)
{
循环语句
}
无条件等价如下的while循环形式:
表达式1
while 表达式2
{
循环语句
表达式3
}
3个表达式的主要作用是:
表达式1:设置初始条件,只执行一次,可以为零,一个或多个变量设置初值
表达式2:是循环条件表达式,用来判定是否继续循环。在每次执行循环体前先执行此表达式,决定是否继续执行循环。
表达式3:作为循环的调整,例如使循环变量增值,它是在执行完循环体后才进行的。
这样:for语句就可以理解为
for(循环变量赋初值;循环条件;循环变量增值)
语句
while循环和for循环的等价示例:
#include<stdio.h>
#include<stdlib.h>
int main(void)
{
int i = 0;
while(i < 10)
{
printf("i = %d\n",i);
i++;
}
for(int j = 0; j < 10; j++)
{
printf("j = %d\n",j);
}
return 0;
}
循环干涉之break和continue
break关键字的作用是提前结束循环;return的作用结束函数调用,这一点需要区分开来。
练习:在全系1000学生中,征集慈善募捐,当总数达到10万元时就结束,统计此时募捐的人数,以及平均每人捐款的数目。
#include<stdio.h>
int main(void)
{
int total_num;
int total_money = 0;
int money;
for(total_num = 0;total_num < 1000; total_num++)
{
printf("请输入捐款金额:\n");
scanf("%d",&money);
total_money += money;
if(total_money >= 100000)
{
printf("捐款金额达到10万元以上!\n");
break;//跳出循环体
}
}
printf("捐款人数为:%d 捐款的平均金额:%0.2f\n",total_num+1,(float)total_money/(total_num+1));
return 0;
}
关键字continue的作用是提前结束循环,直接进入下一次循环;
练习:
#include<stdio.h>
int main(void)
{
int i;
for(i = 100; i <= 200; i++)
{
if( i%3 == 0)
continue; //如果能被3整数直接跳过进入下一次循环
printf("%d ",i);
}
putchar('\n');
return 0;
}
循环嵌套输出某个规律得数列
输出一下4*5的矩阵:
1 2 3 4 5
2 4 6 8 10
3 6 9 12 15
4 8 12 16 20
#include<stdio.h>
int main(void)
{
int i,j;
for(i = 1; i < 5; i++)
{
for(j = 1; j < 6; j++)
{
printf("%d\t",i*j);
}
putchar('\n');
}
return 0;
}
练习1:水仙花数 ,是指一个三位数,其各位数字的立方和等于该数本身。求出所有水仙花数
思路:需要分离出三位数的百位 ,十位,个位;
#include<stdio.h>
#include<math.h>
int main(void)
{
int i;
int one;
int ten;
int hundred;
for(i = 100; i < 1000; i++)
{
hundred = i/100;
ten = i/10%10;
one = i%10;
if(hundred*hundred*hundred + ten*ten*ten + one*one*one == i)
printf("%d ",i);
}
putchar('\n');
return 0;
}
练习:输入两个正整数m和n,求其最大公约数和最小公倍数
辗转相除法
辗转相除法
又名欧几里得算法(Euclidean algorithm),目的是求出两个正整数的最大公约数。它是已知最古老的算法,其可追溯至公元前300年前。
这条算法基于一个定理:两个正整数 a 和 b(a 大于 b),它们的最大公约数等于 a 除以 b 的余数 c 和 较小数 b 之间的最大公约数。
算法计算过程是这样的:
-
2个数相除,得出余数
-
如果余数不为0,则拿较小的数与余数继续相除,判断新的余数是否为0
-
如果余数为0,则最大公约数就是本次相除中较小的数。
比如数字 25 和 10 ,使用辗转相除法求最大公约数过程如下:
-
25 除以 10 商 2 余 5
-
根据辗转相除法可以得出,25 和 10 的最大公约数等于 5 和 10 之间的最大公约数
-
10 除以 5 商 2 余 0, 所以 5 和 10 之间的最大公约数为 5,因此25 和 10 的最大公约数为 5
#include<stdio.h>
#include<stdlib.h>
int main(void)
{
int m,n;
int temp;
int a;
int min;
printf("请输入m:\n");
scanf("%d",&m);
printf("请输入n:\n");
scanf("%d",&n);
min = m*n;
if(m < n)
{
temp = m;
m = n;
n = temp;
}
while(a = m%n)
{
m = n;
n = a;
}
printf("最大公约数:%d\n",n);
printf("最小公倍数:%d\n",min/n);
return 0;
}
数组的引入及基本用法
要使用数组,必须在程序中先定义数组,即通知计算机;由哪些数据组成数组,数组中有多少元素,属于哪个数据类型,否则计算机不会自动地把一批数据作为数组处理。例如,下面是对数组的定义:
int a[10];
它表示定义了一个整型数组,数组名为a,此数组有10个整型元素。
定义一维数组的一般形式为:
类型符 数组名[常量表达式];
注意:
1)中括号中的数字表示数组中元素的总个数
2)下标法表示数组中的某个元素,从0开始计数
计算数组的大小和初始化数组的几种情况
1)全部初始化
在定义数组时对全部数组元素赋予初值,例如:
int a[0] = {0,1,2,3,4,5,6,7,8,9};
将数组中各元素的初值顺序放在一对花括号内,数据间用逗号分隔,花括号内的数据就称为"初始化列表"。经过上面的定义和初始化之后,a[0] = 0, a[1] = 1, a[2] = 2, a[3] = 3, a[4] = 4,
a[5] = 5, a[6] = 6, a[7] = 7, a[8] = 8, a[9] = 9。
2) 部分初始化
可以只给数组中的一部分元素赋值。例如:
int a[10] = {0,1,2,3,4};
定义a数组有10个元素,但花括号内只提供5个初值,这表示只给前面5个元素赋初值,系统自动给后5个元素赋初值为0。
3) 初始化成0
如果想使一个数组中全部元素值为0,可以写成
int a[10] = {0,0,0,0,0,0,0,0,0,0};
或
int a[10] = {0}; //注意:未赋值的部分元素自动设为0
不指定数组大小的情况
在对全部数组元素赋初值时,由于数据的个数已经确定,因此可以不指定数组长度。例如:
int a[5] = {1,2,3,4,5};
可以写成
int a[ ] = {1,2,3,4,5};
计算数组大小的方法
例如 : 数组a的大小为 size = sizeof(a) / sizeof(a[0]);
sizeof是c语言关键字,也是运算符;以字节的形式给出操作数存储空间的大小
数组编程练习
练习:对10个数组元素依次赋值为0,1,2,3,4,5,6,7,8,9,要求按逆序输出。
#include<stdio.h>
int main(void)
{
int a[10];
for(int i = 0; i < 10; i++)
{
a[i] = i;
}
//逆序输出
for(int i = 9; i >= 0; i--)
{
printf("a[%d] = %d\n",i,a[i]);
}
return 0;
}
练习:输出斐波那契数列前30项:0,1,1,2,3,5,8..(第n项等于 a[n] = a[n-1] + a[n-2])
#include<stdio.h>
int main(void)
{
int a[30];
a[0] = 0;
a[1] = 1;
for(int i = 2; i < 30; i++)
a[i] = a[i-1] + a[i-2];
for(int i = 0; i < 30; i++)
printf("%d ",a[i]);
putchar('\n');
return 0;
}
冒泡排序法
依次比较两个相邻的元素,直至最大(或最小)的元素移至数组的一侧;然后进行第二轮冒泡,一共需要进行n-1次冒泡。
#include<stdio.h>
#include<stdbool.h>
int main(void)
{
int temp;
int a[] = {101,1,4,8,3,2,10,54,84,33,100};
int size = sizeof(a)/sizeof(a[0]);
bool in_order;
for(int i = 0; i < size-1; i++)
{
in_order = true;
for(int j = 0; j < size-1-i; j++)
{
if(a[j] > a[j+1])
{
temp = a[j];
a[j] = a[j+1];
a[j+1] = temp;
in_order = false;
}
}
if( in_order)
break;
}
for(int i = 0; i < size; i++)
{
printf("%d ",a[i]);
}
putchar('\n');
return 0;
}
简单选择排序法
将整个序列看作有序部分和无序部分;每一轮排序有序部分的元素增加一个,需要进行n-1次循环。 外层循环指明正在处理数组的哪一个元素,内层循环找出存放在该元素上的值。
#include<stdio.h>
int main(void)
{
int temp;
int a[] = {101,1,4,8,3,2,10,54,84,33,100};
int size = sizeof(a)/sizeof(a[0]);
for(int i = 0; i < size-1; i++)
{
for(int j = i+1; j < size; j++)
{
if(a[i] > a[j])
{
temp = a[i];
a[i] = a[j];
a[j] = temp;
}
}
}
for(int i = 0; i < size; i++)
{
printf("%d ",a[i]);
}
putchar('\n');
return 0;
}
二维数组
1) 二维数组常称为矩阵,把二维数组写成行和列的排列形式,可以有助于形象化地理解二维数组的逻辑结构。
2)二维数组定义的一般形式为:
类型说明符 数组名[常量表达式][常量表达式];
例如:
float a[3][4],b[5][10];
定义a为3×4(3行4列)的数组,b为5×10(5行10列)的数组。
3)c语言对二维数组采用这样的定义形式,使得二维数组可被看作是一种特殊的一维数组:它的元素又是一个一维数组。例如,可以把a看作是一个一维数组,它有3个元素:
a[0] , a[1] , a[2]
每个元素又是一个包含4个元素的一维数组
a[0] - - - a[0][0] a[0][1] a[0][2] a[0][3]
a[1] - - - a[1][0] a[1][1] a[1][2] a[1][3]
a[2] - - - a[2][0] a[2][1] a[2][2] a[2][3]
二维数组的初始化
全部初始化
1) 分行给二维数组赋初值。例如:
int a[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};
这种赋初值方法比较直观,把第1个花括号内的数据给第1行的元素,第2个花括号内的数据赋给第2行的元素... ... 即按行赋初值。
2)可将所有数据写在一个花括号内,按数组元素在内存中的排列顺序对各元素赋初值。例如:
int a[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};
效果与前面相同,但是以第(1)种方法为好,一行对一行,界限清楚。用第(2)种方法如果数据多,则会写成一大片,容易遗漏,也不易检查。
部分初始化
1)int a[3][4] = {{1},{5},{9}};
它的作用是只对各行第1列(即序号为0的列)的元素赋初值,其余元素值自动为0,赋初值后数组各元素为:
1 0 0 0
5 0 0 0
9 0 0 0
2)如果对全部元素都赋初值(即提供全部初始数据),则定义数组时对第1维的长度可以不指定,但第2维的长度不能省。例如:
int a[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};
与下面的定义等价:
int a[ ][4] = {1,2,3,4,5,6,7,8,9,10,11,12};
二维数组小练习
练习:有一个3×4的矩阵,要求编程求出其中值最大的那个元素的值,以及其所在的行号和列号。
思路: 定义一个变量max,假设max = a[0][0];遍历整个二维数组,判断当前元素是否比max大,如果比max大,则将该元素的值赋给max。
#include<stdio.h>
int main(void)
{
int arr[3][4] = {
{1,5,87,453},
{43,77,23,11},
{99,567,321,43},
};
int row;
int column;
int max = arr[0][0];
for(int i = 0; i < 3; i++)
{
for(int j = 0; j < 4; j++)
{
if( max < arr[i][j])
{
max = arr[i][j];
row = i;
column = j;
}
}
}
printf("最大值:%d 在第%d行 第%d列.\n",max,row,column);
return 0;
}
函数
函数的作用能实现某个功能,具有复用性,避免代码的冗长。如同组装计算机一样,事先生产好各种部件(如电源,主板,硬盘驱动器,风扇等),在最后组装计算机时,用到什么就从仓库里取出什么,直接装上就可以了,绝不会采用手工业方式,在用到电源时临时去生产一个电源,用到主板时临时生产一个主板。这就是模块化程序设计的思路。
按功能划分,每个函数代表一个功能,而函数的名字要体现函数的功能含义,类似变量标识符y=f(x);
函数三要素
函数的三要素:
1) 函数名(函数名要体现函数的功能)
2) 参数列表(可以有0个形式参数,也可以有任意个形式参数)
3)返回值
函数在使用之前需要先定义;即 函数原型,函数调用,函数定义 缺一不可。如果缺少了函数原型或者函数定义,在编译时会产生如下错误:undefined reference to "函数的名字(为定义的函数名字)"。
函数体: 具体执行什么样的功能,涉及的处理代码叫做函数体
注意事项:
在自己定义函数时,要时刻函数三要素,粗心时可能会忘记写返回值,或者参数列表中的类型不匹配,在函数调用时写错函数名都存在。函数名标志性符号就是在函数名后面有一个中括号。
形式参数和实际参数区别
在调用有参函数时,主调函数和被调函数之间有数据传递关系。从前面已知:在定义函数时函数名后面括号中的变量名称为"形式参数"(简称"形参")。在主调函数中调用一个函数时,函数名后面括号中的参数称为"实际参数"(简称"实参")。实际参数可以是常量,变量或表达式。
注意注意:传递参数,传递的是值------形参值和实参值相同,但是地址空间不同。
形参和实参虽然值,名字,类型都相同,但是地址空间不同;所以他们不是同一个变量。
局部变量
在fun1函数中定义了变量a,b,在fun2函数中定义了变量a,c。fun1函数中变量a和fun2函数中的变量a不是同一个对象。它们分别有自己的有效范围。正如同高一甲班有一个同学叫王建国,高一乙班也有一学生叫王建国,二者不是同一个人。不同的班允许有同名的学生,互不干扰。高一甲班点名时,只有该班的王建国喊"到",乙班的王建国不在甲班活动,不会同时喊"到"的。他们的活动范围局限在本班,或者说这些名字的有效范围是局部的(只在本班有效)。
获取两个数中较大值编程示例
使用三目运算符 a < b?a:b;
#include<stdio.h>
int getMax(int a, int b);
int main(void)
{
printf("%d\n",getMax(5,3));
return 0;
}
int getMax(int a, int b)
{
return a<b?b:a;
}
函数总结
1)在定义函数中指定的形参,在未出现函数调用时,它们并不占内存中的存储单元。在发生函数调用时,函数的形参被临时分配内存单元。
2)将实参对应的值传递给形参。例如实参的值为2,把2传递给相应的形参x,这时形参x就得到值2,同理,形参y得到值3。
3)通过return语句将函数值带回到主调函数。执行return语句就把这个函数返回值带回主调函数main。应当注意返回值的类型与函数类型一致。
4)调用结束,形参单元被释放。注意,实参单元仍保留并维持原值,没有改变。如果在执行一个被调函数时,形参的值发生改变,不会改变主调函数的实参的值。这是因为实参与形参是两个不同的存储单元。
调用库函数
1)如果使用库函数,应该在本文件开头用#include指令将调用有关库函数时所需用到的信息"包含"到本文件中来。例如:#include<stdio.h>
其中"stdio.h"是一个"头文件"。在stdio.h文件中包含了输入输出库函数的声明。如果不包含"stdio.h”文件中的信息,就无法使用输入输出库中的函数。同样,使用数学库中的函数,应该用#include<math.h>。h是头文件所用的后缀,表示是头文件(header file).
2)如果使用用户自己定义的函数,而该函数的位置在调用它的函数(即主调函数)的后面(在同一个文件中),应该在主调函数中对被调的函数做声明。声明的作用是把函数名,函数参数的个数和参数类型等信息通知编译系统,以便在遇到函数调用时,编译系统能正确识别函数并检查调用是否合法。
数组和函数
数组名作为实参传递给形参的是指针,即传递的是数组地址(或者说首元素的地址)。
形参和实参实际上是两个不同的变量;虽然它们的类型,名,值相同,但是地址空间不同,所以本质上是不同的变量,修改形参的值并不会改变实参的值。
而数组名作为实参传递的也是值,只不过传递的是地址;通过地址能间接访问数组元素,如果不想数组的值发生变化,可以在形参前加上const关键字。
数组和函数练习
练习:计算不同班级学生的平均分
#include<stdio.h>
#define LEN1 5
#define LEN2 10
void initClass(int *p, int n);
void printClass(const int *p, int n);
float getAverage(const int *p, int n);
int main(void)
{
int class1[LEN1];
int class2[LEN2];
initClass(class1,LEN1);
initClass(class2,LEN2);
printClass(class1,LEN1);
printClass(class2,LEN2);
printf("一班平均分为:%0.2f\n",getAverage(class1,LEN1));
printf("二班平均分为:%0.2f\n",getAverage(class2,LEN2));
return 0;
}
void initClass(int *p, int n)
{
int i;
for(i = 0; i < n; i++)
{
printf("请输入%d个学生的成绩:\n",i+1);
scanf("%d",&p[i]);
}
printf("\n初始化完毕!\n");
}
void printClass(const int *p, int n)
{
int i;
printf("总人数%d个\n",n);
for(i = 0; i < n; i++,p++)
{
printf("%d ",*p);
}
putchar('\n');
}
float getAverage(const int *p, int n)
{
int i;
int sum = 0;
for(i = 0; i < n; i++,p++)
{
sum +=*p;
}
return (float)sum/n;
}
二维数组和函数
二维数组是由若干个一维数组组成的,在内存中,数组是按行存放的,因此,在定义二维数组时,必须指定列数(即一行中包含几个元素),由于形参数组与实参数组类型相同,所以它们是由具有相同长度的一维数组所组成的。不能只指定第1维(行数)而省略第2维(列数),下面的写法是错误的:
int array[3][ ];
在第2维大小相同的前提下,形参数组的第1维可以与实参数组不同,例如,实参数组定义为:
int score[5][10];
而形参数组定义为:
int array[ ] [10];
或
int array[8][10];
均可以,这时形参数组和实参数组都是由相同类型和大小的一维数组组成的。C语言编译系统不检查第一维的大小。
练习:有3×4矩阵,初始化它并输出,然后求最大值并输出
#include<stdio.h>
void initArray(int (*p)[4], int n);
void printArray(const int (*p)[4], int n);
void getMaxValue(const int (*p)[4], int n);
int main(void)
{
int arr[3][4];
initArray(arr,3);
printArray(arr,3);
getMaxValue(arr,3);
return 0;
}
void initArray(int (*p)[4], int n)
{
int i;
int j;
for(i = 0; i < n; i++)
{
for(j = 0; j < 4; j++)
{
printf("请输入第%d行 第%d列的数据:\n",i,j);
scanf("%d",*(p+i)+j);
}
}
}
void printArray(const int (*p)[4], int n)
{
int i,j;
for(i = 0; i < n; i++)
{
for(j = 0; j < 4; j++)
{
printf("%d\t",*(*(p+i)+j));
}
putchar('\n');
}
}
void getMaxValue(const int (*p)[4], int n)
{
int i,j;
int maxValue = **p;
int max_i,max_j;
for(i = 0; i < n; i++)
{
for(j = 0; j < 4; j++)
{
if(maxValue < *(*(p+i)+j))
{
maxValue = *(*(p+i)+j);
max_i = i;
max_j = j;
}
}
}
printf("最大数:%d 在第%d列 在第%d行!\n",maxValue,max_i,max_j);
}
外部变量和全局变量
在函数内部定义的变量,称为局部变量;局部变量具有块作用域,自动存储期。
在函数外部定义的变量,称为全局变量;全局变量具有文件作用域,静态存储期。全部变量根据链接属性又分为内部链接变量,外部链接变量。内部链接变量只能在一个翻译单元使用(在一个文件中使用,使用关键字static进行修饰)。外部链接变量可以在多个文件中使用。
全局变量也是外部变量,只不过全局变量写在文件最开头。
练习:班上10个学生,封装一个函数,调用该函数后获得班上的平均分,最高分,最低分。
#include<stdio.h>
int max;
int min;
float getResult(const int *p, int n);
int main(void)
{
int arr[] = {32,44,15,76,87,33,55,89,11,45};
int size = sizeof(arr)/sizeof(arr[0]);
float average;
average = getResult(arr, size);
printf("最高分:%d 最低分:%d\n 班级平均分:%0.2f\n",max,min,average);
return 0;
}
float getResult(const int *p, int n)
{
int i;
int sum = 0;
max = min = *p;
for(i = 0; i < n; i++,p++)
{
if(max < *p)
max = *p;
if(min > *p)
min = *p;
sum += *p;
}
return (float)sum/n;
}
封装冒泡排序和选择排序
#include<stdio.h>
#include<stdbool.h>
void printArray(const int arr[], int size);
void bubbleSort(int *arr, int size);
void selectSort(int *arr, int size);
int main(void)
{
int arr[] = {1,2,65,43,78,44,33,102,453,239};
int size = sizeof(arr)/sizeof(arr[0]);
printArray(arr,size);
bubbleSort(arr,size);
printArray(arr,size);
int arr1[] = {1,2,65,43,78,44,33,102,453,239};
int size1 = sizeof(arr1)/sizeof(arr1[0]);
printArray(arr1,size1);
bubbleSort(arr1,size1);
printArray(arr1,size1);
return 0;
}
void printArray(const int arr[], int size)
{
int i;
for(i = 0; i < size; i++)
{
printf("%d ",arr[i]);
}
putchar('\n');
}
void bubbleSort(int *arr,int size)
{
int i,j;
int temp;
bool in_order;
for(i = 0; i < size-1; i++)
{
in_order = true;
for(j = 0; j < size-1-i; j++)
{
if(arr[j] < arr[j+1])
{
temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
in_order = false;
}
if(in_order)
break;
}
}
}
void selectSort(int *arr, int size)
{
int i,j;
int temp;
for(i = 0; i < size-1; i++)
{
for(j = i+1; j < size; j++ )
{
if(arr[i] < arr[j])
{
temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
}
练习:要求输入10个数,找出最大数以及最大数的下标
#include<stdio.h>
#include<stdbool.h>
void printArray(const int arr[], int size);
void initArray(int *arr, int size);
int getMax(const int *arr, int size, int *max_i);
int main(void)
{
int arr[10];
int max_i;
int max;
initArray(arr,10);
printArray(arr,10);
max = getMax(arr,10,&max_i);
printf("最大值为:%d 下标为:%d\n",max,max_i);
return 0;
}
void printArray(const int arr[], int size)
{
int i;
for(i = 0; i < size; i++)
{
printf("%d ",arr[i]);
}
putchar('\n');
}
void initArray(int *arr, int size)
{
int i;
for(i = 0; i < size; i++,arr++)
{
printf("请输入第%d个元素的值:\n",i);
scanf("%d",arr);
}
}
int getMax(const int *arr, int size, int *max_i)
{
int max = *arr;
int i;
for(i = 0; i < size; i++,arr++)
{
if( max < *arr)
{
max = *arr;
*max_i = i;
}
}
return max;
}
递归函数
有5个学生坐在一起,问第一个学生多少岁,他说比第4个学生大2岁,问第4个学生岁数,他说比第3个学生大2岁,问第3个学生,又说比第2个学生大2岁,问第2个学生,说比第1个学生大2岁,最后问第1个学生,他说是10岁,请问第5个学生多大。
思路:要知道第5个多大,就得知道第4个多大...使用递归函数来求解
#include<stdio.h>
int getAge(int num);
int main(void)
{
int num;
printf("请输入人数:\n");
scanf("%d",&num);
printf("第%d个人的年龄是:%d\n",num,getAge(num));
return 0;
}
int getAge(int num)
{
int age;
if(num == 1)
{
return 10;
}
age = getAge(num-1) + 2;
return age;
}
递归求阶乘
#include<stdio.h>
int factorial(int i);
int main(void)
{
int i;
printf("请输入阶乘:\n");
scanf("%d",&i);
printf("%d的阶乘为:%d\n",i,factorial(i));
return 0;
}
int factorial(int i)
{
int result;
if(i == 0)
{
return 1;
}
result = factorial(i-1)*i;
return result;
}
使用递归函数封装十进制数转二级制数
对于二进制数来说,偶数的余数是0,奇数的余数是1。因此可以通过对2取余来计算最后一位数。第一算出来的反而是最后一位,这种情况就适合使用递归函数。只要十进制数大于等于二,就说明还有余数,因此可以用该条件作为递归终止的条件。
#include<stdio.h>
void to_binary(int num)
{
int r;
r = num%2;
if(num >= 2)
{
to_binary(num/2);
}
printf("%d",r);
}
int main(void)
{
int num;
printf("请输入十进制数:\n");
scanf("%d",&num);
printf("对应的二进制数为:\n");
to_binary(num);
putchar('\n');
return 0;
}