C语言学习之路7
本节对C语言中数组部分进行学习记录
·如何写一个程序计算用户输入的数字的平均数?
#include<stdio.h>
int main(){
int x;
double sum=0;
int cnt=0;
scanf("%d",&x);
while(x!=-1){
sum+=x;
cnt++;
scanf("%d",&x);
}
if(cnt>0){
printf("%f\n",sum/cnt);
}
}
·不需要记录输入的每一个数
·如何写一个程序计算用户输入的数字的平均数,并输出所有大于平均数的数?
·如何记录很多数?
·num1,num2,num3…?
数组
·int number[100]
·scanf("%d",&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+=number[cnt];
cnt++;
scanf("%d",&x);
}
if(cnt>0){
int i;
double average=sum/cnt;
for(i=0;i<cnt;i++){
if(number[i]>average){
printf("%d\n",number[i]);
}
}
}
}
·这个程序是危险的,因为输入的数据可能超过100个。
定义数组
·<类型>变量名称[元素数量],如:int grades[100]; double weight[20];
·元素数量必须是整数
·C99之前:元素数量必须是编译时刻确定的字面量
数组
·是一种容器(放东西的东西),特点是:
(1)其中所有的元素具有相同的数据类型。
(2)一旦创建,不能改变大小。
(3)*(数组中的元素在内存中是连续依次排列的)
int a[10]
·一个int的数组
·10个单元:a[0],a[1],…,a[9]
·每个单元就是一个int类型的变量
·可以出现在赋值的左边或右边:a[2]=a[1]+6;
·*在赋值左边的叫做左值
数组的单元
·数组的每个单元就是数组类型的一个变量
·使用数组时放在[]中的数字叫做下标或索引,下标从0开始计数:grades[0]、grades[5]、grades[99]
有效的下标范围
·编译器和运行环境都不会检查数组下标是否越界,无论是对数组单元做读还是写
·一旦程序运行,越界的数组访问可能造成问题,导致程序崩溃。segmentation fault
·但是也可能运气好,没造成严重的后果
·所以这是程序员的责任来保证程序只使用有效的下标值:[0,数组的大小-1]
长度为0的数组
·int a[0]
·可以存在,但是无用
·写一个程序,输入数量不确定的[0,9]范围内的整数,统计每一种数字出现的次数,输入-1表示结束
#include<stdio.h>
int main(){
const int number=10;
int x;
int count[number];
for(int 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(int i=0;i<number;i++){
printf("%d:%d\n",i,count[i]);
}
}
·在一组给定的数据中,如何找出某个数据是否存在?
#include<stdio.h>
/*
找出key在数组a中的位置
@param key 要寻找的数字
@param a 要寻找的数组
@param length 数组a的长度
@return 如果找到,返回其在a中的位置,如果找不到则返回-1
*/
int search(int key,int a[],int length);
int main(){
int a[]={2,4,6,7,1,3,5,9,11,13,23,14,32};
int x;
int loc;
printf("请输入一个数字:");
scanf("%d",&x);
loc=search(x,a,sizeof(a)/sizeof(a[0]));
if(loc!=-1){
printf("%d在第%d个位置上\n",x,loc);
}else{
printf("%d不存在\n");
}
}
int search(int key,int a[],int length){
int ret=-1;
int i;
for(i=0;i<length;i++){
if(a[i]==key){
ret=i;
break;
}
}
return ret;
}
说明:
(1)数组的集成初始化
int a[]={2,4,6,7,1,3,5,9,11,13,23,14,32};
·直接用大括号给出数组的所有元素的初始值
·不需要给出数组的大小,编译器替你数数
(2)集成初始化时的定位
等价于a[0]=2,a[2]=3,a[3]=6,其余为0
·用[n]在初始化数据中给出定位
·没有定位的数据接在前面的位置后面
·其他位置的值补零
·也可以不给出数组大小,让编译器算
·特别适合初始数据稀疏的数组
(3)数组的大小
·sizeof给出整个数组所占据的内容的大小,单位是字节
sizeof(a)/sizeof(a[0])
·sizeof(a[0])给出数组中单个元素的大小,于是相除就得到了数组的单元个数
·这样的代码,好处就是一旦修改数组中初始的数据,不需要修改遍历的代码
(4)数组的赋值
int a[]={2,4,6,7,1,3,5,9,11,13,23,14,32};
int b[]=a;//×不能做这样的赋值
·数组变量本身不能被赋值
·要把一个数组的所有元素交给另一个数组,必须采用遍历
for(i=0;i<length;i++){
b[i]=a[i];
}
(5)遍历数组
·通常都是使用for循环,让循环变量i从0到<数组的长度,这样循环体内最大的i正好是数组最大的有效下标
·常见错误是:循环结束条件是<=数组长度,或离开循环后,继续用i的值来做数组元素的下标
(6)数组作为函数参数时,往往必须再用另一个参数来传入数组的大小
·数组作为函数的参数时:不能在[]中给出数组的大小,不能再利用sizeof来计算数组的元素个数
判断素数
#include<stdio.h>
int isPrime(int x);
int main(){
int x;
scanf("%d",&x);
if(isPrime(x)){
printf("%d是素数\n",x);
}else{
printf("%d不是素数\n",x);
}
}
int isPrime(int x){
int ret=1;
int i;
if(x==1)ret=0;
for(i=2;i<x;i++){
if(x%i==0){
ret=0;
break;
}
}
return ret;
}
·对2到x-1测试是否可以整除
·对于n要循环n-1遍,当n很大时就是n遍
另外一种方案
int isPrime(int x){
int ret=1;
int i;
if(x==1||(x%2==0&&x!=2))ret=0;
for(i=3;i<x;i+=2){
if(x%i==0){
ret=0;
break;
}
}
return ret;
}
·去掉偶数后,从3到x-1,每次加2
·如果x是偶数,立刻
·否则要循环(n-3)/2+1遍,当n很大时就是n/2遍
但是无须走到x-1,到sqrt(x)就够了
int isPrime(int x){
int ret=1;
int i;
if(x==1||(x%2==0&&x!=2))ret=0;
for(i=3;i<sqrt(x);i+=2){
if(x%i==0){
ret=0;
break;
}
}
return ret;
}
·只需要循环sqrt(x)遍
还有更好的方法:判断是否能被已知的且<x的素数整除
#include<stdio.h>
int isPrime(int x,int knownPrimes[],int numberOfKnownPrimes){
int ret=1;
int i;
for(i=0;i<numberOfKnownPrimes;i++){
if(x%knownPrimes[i]==0){
ret=0;
break;
}
}
return ret;
}
int main(){
const int number=100;
int prime[number]={2};//素数表,前100个素数
int count=1;
int i=3;
while(count<number){
if(isPrime(i,prime,count)){
prime[count++]=i;//count是下标,如果i是素数就把他加入到素数表中,然后移到下一个位置
}
i++;
}
for(i=0;i<number;i++){//输出素数表
printf("%d",prime[i]);
if((i+1)%5)printf("\t");
else printf("\n");
}
}
构造素数表
·预构造n以内的素数表
(1)令x为2
(2)将2x、3x、4x直至ax<n的数标记为非素数
(3)令x为下一个没有被标记为非素数的数,重复(2);直到所有的数都已经尝试完毕
(4)令x++,如果x<n,重复(3),否则结束
#include<stdio.h>
int main(){
const int maxNumber=25;
int isPrime[maxNumber];
int i;
int x;
for(i=0;i<maxNumber;i++){
isPrime[i]=1;
}
for(x=2;x<maxNumber;x++){
if(isPrime[x]){
for(i=2;i*x<maxNumber;i++){
isPrime[i*x]=0;//从2开始,它所有的倍数isPrime都标记为0
}
}
}
for(i=2;i<maxNumber;i++){
if(isPrime[i]){//剩下isPrime为1的数就是素数了
printf("%d\t",i);
}
}
printf("\n");
}
·算法不一定和人的思考方式相同
二维数组
·int a[3][5];
·通常理解为a是一个3行5列的矩阵
二维数组的遍历
for(i=0;i<3;i++){
for(j=0;j<5;j++){
a[i][j]==i*j;
}
}
·a[i][j]是一个int
·表示第i行第j列上的单元
二维数组的初始化
int a[][5]={
{0,1,2,3,4},
{2,3,4,5,6},
};
·列数是必须给出的,行数可以由编译器来数
·每行一个{},逗号分隔
·最后的逗号可以存在,有古老的传统
·如果省略,表示补零
·也可以用定位(*C99 ONLY)
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 num0fx;
int num0fo;
int result=-1;//-1:没人赢 1:x赢,0: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++){
num0fo=num0fx=0;
for(j=0;j<size;j++){
if(board[i][j]==1){
num0fx++;
}else{
num0fo++;
}
}
if(num0fo==size){
result=0;
}else if(num0fx==size){
result=1;
}
}
//检查列
if(result==-1){
for(j=0;j<size&&result==-1;j++){
num0fo=num0fx=0;
for(i=0;i<size;i++){
if(board[i][j]==1){
num0fx++;
}else{
num0fo++;
}
}
if(num0fo==size){
result=0;
}else if(num0fx==size){
result=1;
}
}
}
//检查对角线 正对角线
num0fo=num0fx=0;
for(i=0;i<size;i++){
if(board[i][i]==1){
num0fx++;
}else{
num0fo++;
}
}
if(num0fo==size){
result=0;
}else if(num0fx==size){
result=1;
}
//反对角线
num0fo=num0fx=0;
for(i=0;i<size;i++){
if(board[i][size-i-1]==1){
num0fx++;
}else{
num0fo++;
}
}
printf("%d",result);
}