第六周
1. 数组
1.1 如何写一个程序计算用户输入的数字的平均数?
// 之前的写法
#include<stdio.h>
int main()
{
int x;
double sum=0;
int cnt=0;
scanf("%d",&x);
while(x!=-1) {
sum=sum+x;
cnt++;
scanf("%d",&x);
}
if(cnt>0) {
printf("%f\n",sum/cnt);
}
return 0;
}
1.2 如何写一个程序计算用户输入的数字的平均数,并输出所有大于平均数的数?
// 利用数组`int number[i]`
#include<stdio.h>
int main()
{
int x;
double sum=0;
int cnt=0;
int number[100];
scanf("%d",&x);
while(x!=-1) {
number[cnt]=x;
/*
{
int i;
printf("%d\t",cnt);
for(i=0;i<=cnt;i++)
{
printf("%d\t",number[i]);
}
printf("\n");
}
*/
sum=sum+x;
cnt++;
scanf("%d",&x);
}
if(cnt>0) {
printf("%f\n",sum/cnt);
int i;
for(i=0;i<cnt;i++) {
if(number[i]>sum/cnt) {
printf("%d\n",number[i]);
}
}
}
return 0;
}
整理后
// 利用数组`int number[i]`
#include<stdio.h>
int main()
{
int x;
double sum=0;
int cnt=0;
int number[100]; // 定义数组
scanf("%d",&x);
while(x!=-1) {
number[cnt]=x; // 对数组元素赋值
sum+=x;
cnt++;
scanf("%d",&x);
} // while
if(cnt>0) {
int i;
double average=sum/cnt;
for(i=0;i<=cnt;i++) { // 遍历数组
if(number[i]>sum/cnt) {
printf("%d\n",number[i]); // 使用数组的元素
} // if
} // for
} // if
return 0;
}
// 存在超过数组范围的风险`cnt>number[100]`
1.3 定义数组
数组:
- 其中所有的元素具有相同的数据类型;
- 一旦创建不能改变大小;
- 数组中的元素在内存中是连续一次排列的
提前输入数字数量
// 利用数组`int number[i]`
#include<stdio.h>
int main()
{
int x;
int cnt;
printf("Enter the number of the list:\n");
scanf("%d\n".&cnt);
double sum=0.0;
if(cnt>0) {
int number[cnt];
scanf("%d",&x);
while(x!=-1) {
number[cnt]=x; // 对数组中元素赋值
sum+=x;
cnt++;
scanf("%d",&x);
} // while
} // if
if(cnt>0) {
int i;
double average=sum/cnt;
for(i=0;i<=cnt;i++) { // 遍历数组
if(number[i]>sum/cnt) {
printf("%d\n",number[i]); // 使用数组的元素
} // if
} // for
} // if
return 0;
}
// 存在超过数组范围的风险`cnt>number[100]`
1.4 写一个程序,输入数量不确定的[0,9]范围内的整数,统计每一种数字出现的次数,输入-1表示结束。
#include<stdio.h>
int main(void)
{
const int number=10; // C99 数组的大小
int x;
int count[number]; // 定义数组
int i;
for(i=0;i<number;i++) { // 初始化数组
count[i]=0;
}
scanf("%d",&x);
while(x!=-1) {
if(x>=0 && x<=9) {
count[x]++; // 数组参与运算
}
scanf("%d",&x);
}
for(i=0;i<number;i++) { // 遍历数组输出
printf("%d:%d\n",i,count[i]);
}
return 0;
}
2. 函数的定义与使用
2.1 求素数和
#include<stdio.h>
int main()
{
int m,n;
int sum=0;
int cnt=0;
int i;
scanf("%d %d",&m,&n);
// m=10,n=31;
if(m==1) {
m=2;
}
for(i=m;i<=n;i++) { // 判断`i`是否是素数
int isPrime=1;
int k;
for(k=2;k<i-1;k++) {
if(i%k==0) {
isPrime=0;
break;
} // if
} // for
if(isPrime) { // 代表`isPrime==1`
sum+=i;
cnt++;
} // if
} // for
printf("%d %d\n",cnt,sum);
return 0;
}
可以变成下面这样👇
#include<stdio.h>
int isPrime(int n) // 定义了一个自定函数
{
int res=1;
int k;
for(k=2;k<i-1;k++) {
if(i%k==0) {
isPrime=0;
break;
}
}
return res;
}
int main()
{
int m,n;
int sum=0;
int cnt=0;
int i;
scanf("%d %d",&m,&n);
// m=10,n=31;
if(m==1) {
m=2;
}
for(i=m;i<=n;i++) { // 判断`i`是否是素数
if(isPrime(i)) {
sum+=i;
cnt++;
}
}
printf("%d %d\n",cnt,sum);
return 0;
}
2.2 求和:求出1~10、20~30、35~45的三个和
// `Command+C` `Command+V`->代码复制->不良代码
// 所以我们写自己的函数
#include<stdio.h>
void sum(int begin, int end)
{
int i;
int sum=0;
for(i=begin;i<=end;i++) {
sum+=i;
}
printf("%d到%d的和是%d\n",begin,end,sum);
}
int main()
{
sum(1,10);
sum(20;30);
sum(35,45);
return 0;
}
2.3 函数定义
以下面的代码为例
void sum(int begin, int end)
// 第一行为函数头,`void`为返回类型,`sum`为函数名,`int begin, int end`为参数表
{
int i;
int sum=0;
for(i=begin;i<=end;i++) {
sum+=i;
}
printf("%d到%d的和是%d\n",begin,end,sum);
}
// `{}`内部为函数体
2.4 从函数中返回值
int max(int a, int b)
{
int ret;
if(a>b) {
ret=a;
} else {
ret=b;
}
return ret;
}
// “单一出口理念”
int max(int a, int b)
{
if(a>b) {
return a;
} else {
return b;
}
}
// 不符合“单一出口”理念
2.5 没有返回值的函数
viod 函数名(参数表)
3. 函数的参数和变量
3.1 函数先后关系
#include<stdio.h>
void sum(int begin, int end);
// 函数原型
int main()
{
sum(1,10);
sum(20;30);
sum(35,45);
return 0;
}
void sum(int begin, int end)
// 真正的函数头
{
int i;
int sum=0;
for(i=begin;i<=end;i++) {
sum+=i;
}
printf("%d到%d的和是%d\n",begin,end,sum);
}
#include<stdio.h>
int main()
{
void sum(int begin, int end);
// 旧版本的函数原型,现阶段不使用
sum(1,10);
sum(20;30);
sum(35,45);
return 0;
}
void sum(int begin, int end)
// 真正的函数头
{
int i;
int sum=0;
for(i=begin;i<=end;i++) {
sum+=i;
}
printf("%d到%d的和是%d\n",begin,end,sum);
}
3.2 参数传递
调用函数给的值与参数的类型不匹配是C语言的传统上最大的漏洞,因此需要自己对于编程上的参数进行匹配。
3.2.1 传值
- 每个函数有自己的变量空间,参数也位于着独立的空间中,和其他函数没有关系;
- 过去,对于函数参数表中的参数,叫做“形式参数”,调用函数时给的值叫做“实际参数“;
#include<stdio.h>
void swap(int a, int b); // 形参(Formal Actor)->参数
int main()
{
int a=5;
int b=6;
swap(a,b); // 实参(Real Actor)->值
return 0;
}
void swap(int a, int b) // 形参(Formal Actor)->参数
// 真正的函数头
{
int t=a;
a=b;
b=t;
}
// 不建议使用这种“古老的语言去形容这些变量”
// 我们认为是“参数”和“值”
3.3 本地变量(Local Variables)
3.3.1 定义
- 函数的每次运行,就产生了一个独立的变量空间,在这个空间中的变量,是函数的这次运行所独有的,乘坐本地变量;
- 定义在函数内部的变量就是本地变量;
- 参数也是本地变量。
3.3.2 变量的生存期和作用域
- 生存期:什么时候这个变量开始出现了,到什么时候消亡了;
- 作用域:在代码的什么范围内可以访问这个变量(即该变量可以起作用);
- 对于本地变量来说,上述两个概念是一样的,即
{}
内部的“块”。
以swap
语句为例👇
#include<stdio.h>
void swap(int a, int b);
int main(void)
{
int a=5;
int b=6;
swap(a,b); // 这时`C`离开了`main`的变量空间,去到了下方`void`语句的变量空间
return 0;
}
void swap(int a, int b)
{
int t=a;
a=b;
b=t;
}
3.3.3 本地变量的规则
- 本地变量是定义在块内的;
1.1. 可以定义在函数的块内;
1.2. 也可以定义在语句的块内;
1.3. 甚至可以随机放在一组{}
内; - 程序运行进入这块之前,其中的变量不存在,离开这个块,其中的变量就消失了;
- 块外面定义的变量在块内仍然有效;
- 块内部定义的和外部同名的变量则视内部变量有效;
- 不能在一个块内定义同名变量;
- 本地变量不会被默认初始化;
- 参数在进入函数的时候被初始化。
3.4 其他细节
3.4.1 没有参数
void f(void) 与 void f()的区别
不要写下面的原型
void swap();❌
3.4.2 逗号运算符
f(a,b)
f((a,b))
3.4.3 函数里的函数?
C语言不允许函数嵌套定义
3.4.4 一些定义(不建议)
int i,j,sum(int a, int b);
return (i);
不建议🙅
3.4.5 关于main
int main()
也是一个函数;int main(void)
与int main()
:void
表示“不输出”;return 0;
是有意义的
4. 二维数组
4.1 二维数组
4.1.1 定义
int a[3][5]
通常代表一个3行5列的矩阵;
4.1.2 二维数组的遍历
for( i=0; i<3; i++ ) {
for ( j=0; j<5; j++ ) {
a[i][j] = i*j;
}
}
请注意a[i,j]=a[j]
4.1.3 二维数组的初始化
- 列数必须给出的,行数可以由编译器来数;
- 每行一个
{},
逗号分隔每个元素; - 最后的逗号的存在有古老的传统;
- 如果省略,可以补零;
- 也可以使用定位(仅在C99中使用);
4.1.4 Tic-Tac-Toe
- 读入一个3X3的矩阵,矩阵中的数字为1表示该位置上有一个X,为0表示为O;
- 有程序判断这个矩阵中是否有获胜的一方,输出表示获胜一方的字符X或O,或输出无人获胜。
#include<stdio.h>
int main()
{
const int size = 3;
int board[size][size];
int i,j;
int numOfX;
int numOfO;
int result = -1; // -1: 平局; 1: X赢; O:O赢
// 读入矩阵
for( i=0; i<size; i++ ) {
for( j=0; j<size; j++ ) {
scanf("%d",&board[i][j]);
}
}
// 检查行
for( i=0; i<size && result == -1; i++ ) {
numOfO == numOfX = 0;
for( j=0; j<size; j++ ) {
if( board[i][j] == 1 ) {
numOfX ++;
} else {
numOfO ++;
}
}
if( numOfO == size ) {
result = 0;
} else if( numOfX == size ) {
result = 1;
}
}
// 检查列
if( result == -1) {
for( j=0; j<size && result == -1; j++ ) {
numOfO == numOfX = 0;
for( i=0; i<size; i++ ) {
if( board[i][j] == 1 ) {
numOfX ++;
} else {
numOfO ++;
}
}
if( numOfO == size ) {
result = 0;
} else if( numOfX == size ) {
result = 1;
}
}
}
// 可以发现 检查行列的代码几乎一致,如何合并?
// 检查对角线
// 检查正对角线
numOfO = numOfX = 0;
for( i=0; i<size; i++ ) {
if( board[i][i] == 1 ) {
numOfX ++;
} else {
numOfO ++;
}
}
if( numOfO == size ) {
result =0;
} else if( numOfX == size ) {
result = 1;
}
// 检查斜对角线
numOfO = numOfX = 0;
for( i=0; i<size; i++ ) {
if( board[i][size-1] == 1 ) {
numOfX ++;
} else {
numOfO ++;
}
}
return 0;
}
//Update
#include<stdio.h>
int main()
{
const int size = 3;
int board[size][size];
int i,j;
int numOfX;
int numOfO;
int result = -1; // -1: 平局; 1: X赢; O:O赢
// 读入矩阵
for( i=0; i<size; i++ ) {
for( j=0; j<size; j++ ) {
scanf("%d",&board[i][j]);
}
}
// 检查行、列
for( i=0; i<size && result == -1; i++ ) {
numOfO == numOfX = 0;
for( j=0; j<size; j++ ) {
if( board[i][j] == 1 ) {
numOfX ++;
} else {
numOfO ++;
}
if( board[j][i] == 1 ) {
numOfX ++;
} else {
numOfO ++;
}
}
if( numOfO == size ) {
result = 0;
} else if( numOfX == size ) {
result = 1;
}
}
// 检查对角线
// 检查正对角线
numOfO = numOfX = 0;
for( i=0; i<size; i++ ) {
if( board[i][i] == 1 ) {
numOfX ++;
} else {
numOfO ++;
}
}
if( numOfO == size ) {
result =0;
} else if( numOfX == size ) {
result = 1;
}
// 检查斜对角线
numOfO = numOfX = 0;
for( i=0; i<size; i++ ) {
if( board[i][size-1] == 1 ) {
numOfX ++;
} else {
numOfO ++;
}
}
return 0;
}