数据类型 数组名[数组元素数量];
注意:数组元素数量一定是常数、常量、整型,不能是变量!
数组:一组具有相同类型的变量的集合。
数组名:标识这组相同的数据的名字。
数组元素:构成数组的每个数据项。
数组:定义、初始化、使用
一维数组
- 定义:存储类型 数据类型 数组名[正整数]
- 初始化:
全部初始化:int a[5]={12,23,45,35,9},int a[]={11,22,3,4,3},没有数组长度的是全部初始化,它的长度根据后面的个数来定。
部分初始化:int a[5]={0},第一个元素初始化为0,其他没有初始化的也是零。int a[5]={11},第一个元素是11,其他没有初始化的为0。
- 使用:
一维数组的使用
数组下标:数组元素的索引
注意:数组下标的索引都是从0开始的,例如使用score[0],…,score[9],下标既可以是常量,也可以是整型表达式,允许快速随机访问。
对于单个数组元素,我们可以把它看成普通变量使用。
float score[10]; score[5]=80; // 第6个人的成绩是80。
- 存储:
系统分配一块连续的存储空间,大小为:数据类型的大小*数组元素数量。
注意:数组名表示数组的首地址(即第一个数组元素的地址)!
例如:
int a[10];
&a[5] = ?
假设数组首地址是a = 1000,整数类型占用4个字节空间,因此,第6个元素的地址是:&a[5] = 1000+4*5=1020。(第6个元素前面存储了5个元素)
例:怎么使两个数组的值相等?
int a[4]={2,1,4,6},b[4];
b=a;
注意:数组名是常量(表示首地址),它的值不能被修改!因此数组间不能进行赋值!
方法:
方法1:逐个元素赋值
b[0]=a[0];
b[1]=a[1];
……
方法2:循环语句赋值(实质还是逐个赋值)
int i;
for(i=0;i<4;i++)
{
b[i]=a[i];
}
程序1:显示用户输入的月份拥有的天数(不包括闰年的月份)
输入:月份,如果不在1~12之间,则要求重新输入
输出:月份对应的天数
算法思想:每个月多少天是固定的,我们把12个月分别有几天存储在长度为12的一维数组中,并用month-1作为数组的下标进行索引。
/* 显示用户输入的月份拥有的天数(不包括闰年的月份) */ #include "stdio.h" #include<stdlib.h> #define MONTH 12 // 注意:define宏定义放在main函数之前,并且不能有分号!!! int main() { int i, month; int days[MONTH] = { 31,28,31,30,31,30,31,31,30,31,30,31 }; do { printf("Please input a month...\n"); scanf_s("%d", &month); } while ((month < 1) || (month > 12)); // 注意这里是逻辑或!用于处理不正常的月份输入!如若输入正常,那么会退出循环输出天数! printf("The number of days is %d!\n", days[month - 1]); // 注意:下表索引是month-1 !!! system("pause"); return 0; }
程序2:把10个人的成绩存入score数组中,找出最高分及所在位置。
输入:10个人的成绩
输出:最高分和所在位置
输入转化成输出的算法思想:用一个整数存储找到的最高分,一个整数存储它的位置,初始时第一个人,初始是第一个人,然后从第二个人依次判断是否高于上一个,如果是,则更新;
/* 把10个人的成绩存入score数组中,找出最高分及所在位置 */ #include "stdio.h" #include<stdlib.h> int main() { int i, student = 0; float max, score[10]; for (i = 0, max = score[0];i < 10;i++) { scanf_s("%f", &score[i]); if (score[i] > max) { max = score[i]; student = i; } } printf("The highest score is %.2f!\nThe student is %d!\n", max, i); system("pause"); return 0; }
补充:
1. const常量使用方法:const int n=10; 放在main函数里面,后面有分号!
2. define宏定义:#define N 10; 放在main函数前,不能有分号!
3. score[i]为数组元素;score+i和&score[i]表示元素地址。
4. 所有数据在使用前必须初始化!
如果一维数组的元素是一个n-1维数组,就构成了n维数组。
- 多维数组的定义:
类型定义符 数组名 [元素个数1][元素个数2]…[元素个数n]
使用多维数组时注意越界问题!
- 二维数组的存储:一个两行三列的数组的逻辑存储结构
列优先:把每一列存完再存下一列
行优先:先存每一行
不同的语言采用的存储方式不一样,C语言采用行优先方式。
二维数组的存储空间大小(多少字节数):
数据类型的大小*一维数组元素数量*二维数组元素数量
- 二维数组的初始化
全部初始化:
按元素初始化:int a[3][3]={1,2,3,4,5,6,7,8,9}
按行初始化:int a[3][3]={{1,2,3}{4,5,6}{7,8,9} // 几个{}就有几行!
省略行数的初始化:int a[][3]={{1,2,3}{4,5,6}{7,8,9}}
部分初始化:
部分元素初始化:int a[3][3]={1,2} // a[0][0]=1; a[0][1]=2
按行部分初始化:int a[3][3]={{1,2}{4}{7,8,9}}
省略行数的部分初始化:int a[][3]={{1,2}{4}{7,8,9}}
字符数组同理
一维字符数组char James[]="James"; // 字符串初始化常量,默认在字符串最后加一个结束符\0,因此要加1!一个一维字符数组可以存放一个字符串!
二维字符数组:char name[][n]={};一个二维数组可存放多个字符串!
求字符串长度:len=strlen("James");
字符串处理函数,需要加头文件<string.h>。
字符数组的定义:char str[10];
初始化:
逐个元素赋值str[1]='c'; str[2]='h';
定义的同时初始化:按字符char str[10]={'c','h'};
按字符串常量初始化(可省略大括号):char s1[20]={"How do you do"}; char s2[20]="How do you do"; 这两个是等价的!
注意:用字符串常量进行初始化时,末尾自动加\0。
其他初始化的方法:
1. 输入输出操作
单个字符输入输出(格式字符为%c):
char str[10]; for (i=0;i<5;i++) scanf_s("%c",&str[i]); // 数组元素输入 for(i=4;i>=0;i--) printf("%c",str[i]); // 数组元素输出
字符串整体输入输出(格式字符为%s):
char str[10]; scanf_s("%s",str); // 数组名是数组的起始地址 printf("%s",str);
注意:scanf函数这里不加&,对于数组不加取地址符,因为数组名就是地址!
字符串输入输出函数:
gets函数:gets(字符数组名),作用:从终端输入一个字符串到字符数组。
例如:
gets(word); // 从终端输入一个字符串存到word数组中,系统会自动加上'\0'结束符
puts函数:puts(字符数组名),作用:把一个字符串输出到终端,并在输出时将字符串结束标记'\0'转换为'\n',即输出完字符串后换行。
如:
char word[]='abc'; word1[]='def'; // 用puts输出时,abc和def显示在两行。
常见的字符串处理函数:
字符串连接函数strcat(字符数组1,字符数组2);
功能:将字符数组2连接到字符数组1的后面。因此要求新数组长度可以容纳两个字符数组合并之后的长度。
字符串拷贝函数strcpy(字符数组1,字符数组2);
功能:把数组2的内容拷贝到数组1中。第二个参数可以是字符串常量!str1=str2这种形式是错的!
字符比较函数strcmp(字符串1,字符串2);
功能:将两个字符串的字符从左到右逐个进行比较,返回值为比较结果。
字符串1=字符串2,则返回0;
字符串1>字符串2,则返回正数;(看ASCII码谁大)
字符串1<字符串2,则返回负数;
字符串长度函数strlen(字符数组)
功能:测试字符数组中字符串的实际长度。(注意这里不包含结束符,即不要加1!)
程序3:存储100个学生的姓名、学号、语文成绩、数学成绩、英语成绩,并按语文成绩进行排序,按名次输出所有学生信息。
思路:姓名可以用字符数组存储,100个学生就要100个一维字符数组,姓名是有长度的,假设所有姓名长度不超出20,因此我们用100*20的一个二维数组存储姓名。学号用整数存储,用一个长度为100的整数数组,三门成绩用3个长度为100的实数数组。
选择排序算法:把所有人的最高分找到并放到第一个位置,余下的最高分放到第二个位置,…,最低分放到最后一个位置。
错误:语文成绩降序排列了,但其他信息没有跟着一起更新!说明其他信息没有配对!
解决方法:需要将每个人的信息组织在一起(结构),对语文成绩降序排列时其他成绩也跟着排序。
结构:(定义方式和普通变量定义方式相同,对于这个问题而言,每个学生有五个变量:姓名、学号和三门成绩)
struct student { char name[20]; int id; float chinese; float english; float math; }
结构是一个或多个变量的集合,结构中的变量可以是不同的类型(这点和数组不同,数组要求变量类型相同!),为了处理方便,我们把这些变量组织在一个名字下。结构将一组相关的变量看做一个存储单元,而不是各自独立的实体,因此结构有助于组织复杂的数据。
结构类型需要先定义,定义结构类型struct,一般格式为:
struct 结构类型名 { 类型名1 成员名1; 类型名2 成员名2; …… 类型名n 成员名n; }
定义了结构类型之后,并不能直接使用!struct和int/char这些都一样,只是说明了变量集合是什么样子,要定义变量之后才能分配空间、存储数据、操作数据!例如:
struct student s1,s2={"张三", 2, 95, 88, 73}; // 初始化(存储)了s2的姓名、学号和三门成绩 s1.id = 1; // 初始化(存储)s1的id(学号)
问题:如果想要s1结构体也具有和s2完全相同的数据,可不可以s1=s2?
答:可以!数组不可以进行整体赋值,但相同类型的结构体可以进行整体赋值!因为数组名是常量,是不能被修改的!
注意:
1. 结构类型名不得与其他变量的名字相同。
2. 结构成员名可以与其他变量名字相同(作用域不同)。
3. 结构类型定义之后一定要加一个分号。
4. 不同类型的结构变量不允许相互赋值(相同类型可以相互赋值)。
5.结构类型名必须包含关键字struct。
6. 结构变量的初始化和数组变量的初始化相同。
7. 结构类型不是定义变量,只是定义了数据类型是结构体类型。
/* 按语文成绩降序排列 */ #include<stdio.h> #include<stdlib.h> const int n = 4, m=20; // C文件中const常量的使用并没有把n和m看做常量,而是固定的变量,因此会报错,只需要将文件类型改为.cpp即可。 typedef struct student // 定义了struct student的类型,然后用typedef将struct student重新定义为student,因此在后面我们就可以直接使用student { char name[m]; int id; float chinese; float english; float math; }student; int main() // 通过for语句将所有人的信息读入到数组s[n]中 { student s[n],tmp; int i,max,j; for(i=0;i<n;++i) scanf_s("%s%d%f%f%f",s[i].name,m,&s[i].chinese,&s[i].english,&s[i].math); } for(i=0;i<n;++i) // 选择排序算法 { max=i; for(j=i+1;j<n;++j) if(s[j].chinese>s[max].chinese) max=j; tmp=s[max]; s[max]=tmp; } for(i=0;i<n;++i) // 输出 printf("%s%d%.2f%.2f%.2f\n",s[i].name,s[i].id,s[i].chinese,s[i].english,s[i].math); system("pause"); return 0; }
程序4:求1~100间所有素数。
/* 求1~100之间的所有素数 */ #include<stdio.h> #include<stdlib.h> #include<math.h> // 错误:sqrt()未定义!原因是:忘记加这个头文件<math.h> int main() { int i,n,bprim; for(n=1;n<=100;++n) { int bprim=1; for(i=2;i<=sqrt(n);++i) { if (n%i==0) bprim=0; break; } if(bprim==1) printf("%d is prim!\n",n); else printf("%d is not prim!\n",n); } system("pause"); return 0; }
思路:依次判断2,3,…,100,方法可行,但效率较低。
优化思路:2是素数,2的所有倍数是合数,3是素数,3的所有倍数标记为合数,4是2的倍数(已经标记过),5是素数,5的所有倍数我们标记为合数。
关键:需要标记2~100间的数是否处理过!我们假定一个长度为100的数组,初始化为0,表示全部是素数,如果判断为合数那么置为1。
/* 求1~100之间的所有素数 */ #include<stdio.h> #include<stdlib.h> #include<math.h> // 错误:sqrt()未定义!原因是:忘记加这个头文件<math.h> int main() { const int n=100; // C文件中const常量的使用并没有把n和m看做常量,而是固定的变量,因此会报错,只需要将文件类型改为.cpp即可。 int isPrim[n+1]={0}; // 注意:我们要判断2~100,因此需要101个数作为下标! int i,j; for(i=2;i<sqrt(double(n));++i) // 错误:sqrt对重载函数的调用不明确。 //原因:sqrt函数的参数和返回值都是double类型,因此要做一个强制类型转换!) // 一个数一定是:大数乘小数,sqrt(n)内没有出现它的小因子,那么之后就一定没有大因子, // 说明sqrt(n)之后的数是不是素数,取决于前面半部分的倍数。 { if (isPrim[i]==0) // 很简单:因为2是素数,因此从2开始,把它所有倍数标记为1, // 那么再依次搜索下一个为0的标志位,只要遇到0,一定说明它是素数(因为是1点1点加上来的) for(j=2*i;j<=n;j+=i) // i是素数,那么输出它的所有倍数j,并把它们的标志位标记为1 isPrim[j]=1; } for(i=2;i<=n;++i) if(isPrim[i]==0) { printf("%d ",i); // %d后面有个空格! } system("pause"); return 0; }
程序5:给定由6个整数组成的序列{16,8,4,32,5,9},将其按从小到大的顺序排列。
思路:首先存储数据(一维数组存储)
方法:冒泡排序法(从起始端开始,依次比较相邻的两个元素,若他们不合顺序,则交换位置)。第一趟:最大元素放到了最后的位置,第二趟:次大元素放到了右边第二个位置……
/* 给定由6个整数组成的序列{16,8,4,32,5,9},将其按从小到大的顺序排列 */ #include<stdio.h> #include<stdlib.h> #include<math.h> // 错误:sqrt()未定义!原因是:忘记加这个头文件<math.h> int main() { const int n=6; int i,j,tmp; int s[n]={16,8,4,32,5,9}; // 测试时最好初始化便于检查是否正确,若正确,再加上输入数据的功能。 for(j=1;j<6;++j) // j表示第几趟,若有n个数,那么进行n-1趟,每一趟比较进行n-j次比较! { for(i=0;i<n-j;++i) // i表示第j趟的第几位参与比较(注意:是进行n-j次比较!) { if(s[i]>s[i+1]) { tmp=s[i]; s[i]=s[i+1]; s[i+1]=tmp; } } } for(i=0;i<=5;i++) printf("%d\n",s[i]); // 不要忘记输出啊! system("pause"); return 0; // 养成习惯,函数返回整数,则在还没有进行程序编写前,加一个return+整数。 }
C语言中不允许内部定义!C++允许。意思是说:for(int i=1; ;);在C++中允许,C语言中不允许。
程序6:查找在N个元素中是否存在某一元素。
分析:若数据无规律,则需进行逐一比较。若有规律(数据有序),采用折半查找法可以提高效率。
思路:若数据有序,那么定义low,high和middle。每次只差找一半,效率很高!
/* 在10个有序的数据中查找是否存在某一元素,折半查找法! */ #include<stdio.h> #include<stdlib.h> int main() { int *data,len,x,i; int low=1,high,mid; printf("输入数组元素个数:"); scanf_s("%d",&len); data=(int*)malloc((len+1)*sizeof(int)); // malloc用来动态申请内存空间 // 下标是从0开始,为了能够存储len那个位置的元素,这里是len+1! if (data==NULL) { printf("分配内存失败"); return -1; } printf("输入有序数据:"); for (i=0;i<len;++i) scanf_s("%d",&data[i+1]); printf("输入待查找数据:"); scanf_s("%d",&x); high=len; mid=(low+high)/2; while(low<=high) // 折半查找法 { if(data[mid]==x) { printf("%d\n",mid); break; } else if(data[mid]>x) { high=mid-1; } else { low=mid+1; } mid=(low+high)/2; } if(data[mid]==x) { printf("%d在数组的第%d个位置\n",x,mid); } else printf("%d不在待查找元素序列中。\n",x); if(data) // 如果data内存空间分配成功,则最后一定要释放内存空间! free(data); // free用来释放malloc申请的空间,malloc和free配对使用,有malloc一定有free! system("pause"); return 0; }
(参考:https://www.jb51.net/article/114244.htm)
程序7:求Fibonacci数。
规律:前面两个月的和是后面一个月的兔子对数(前两个月是1对,从第三个月开始后面一个月的兔子对数是前面两个月之和)。
分析:有多少月就需要多少需要保存(用数组)。
/* 求第10个月有多少对兔子 */ #include<stdio.h> #include<stdlib.h> int main() { const int n=11; int f[n],i; f[1]=1; f[2]=1; printf("% 4d % 4d",f[1],f[2]); for(i=3;i<n;++i) { f[i]=f[i-1]+f[i-2]; printf("% 4d",f[i]); } system("pause"); return 0; }
程序8:在屏幕上模拟显示一个数字时钟。
分析:时钟分为时分秒,因此定义一个结构存储时分秒。
/* 在屏幕上模拟显示一个时钟 */ #include<stdio.h> #include<stdlib.h> #include<windows.h> // Sleep函数的头文件! struct clock // 定义一个时钟结构体类型(结构体定义在主函数外面) { int hour; int minute; int second; }; // 或者在后面用typedef struct clock CLOCK; typedef struct clock CLOCK; int main() { CLOCK t={0,0,0}; // 定义一个CLOCK类型的t变量 int n=100,i=0; // 这个n表示让它显示的总秒数 while(++i<n) { t.second++; if(t.second>=60) // 秒数》=60,分加一,秒归零 { t.second=0; t.minute++; } if(t.minute>=60) { t.minute=0; t.hour++; } if(t.hour>=24) t.hour=0; printf("%2d:%2d:%2d\r",t.hour,t.minute,t.second); // 用冒号:分隔开 // \r表示在同一行输出,如果用的是\n,可以用清屏函数system("CLS")达到同样的目的。 Sleep(1000); // 每1000ms进行一次循环,注意:Sleep函数S是大写!头文件是<windows.h>! } system("pause"); return 0; }
区分:Sleep(ms)和sleep(s),简单说,VC用Sleep,其他一律用sleep(例如Linux)。
已知获取本机当前时间的函数,修改时间显示程序,可以实时显示本机当前时间。
time_t now; // 定义time_t变量 struct tm *local; // 定义struct tm变量 time(&now); // 利用time函数获取时间 local=localtime(&now); // 通过localtime把获取的时间转换为当地时间,输出类型是struct tm类型 printf("%2d:%2d:%2d\r",local->tm_hour,local->tm_min,local->tm_sec); // 输出 // \r表示在同一行输出,如果用的是\n,可以用清屏函数system("CLS")达到同样的目的。
注意:\r是回车符,表示还是在当前行,并且光标移到当前行的第一格。\n是换行符。\r\n是回车加换行。
程序9:数据分析。从键盘输入学生的学号,分析其年级、学院、专业、班级、编号。
分析:读入字符串,共14位(加一个结束符)。每个学生的所有数据是一个结构,结构体中有年级、学院、专业、班级、编号五个成员,每个成员是一个字符数组,用字符数组存储字符串,存储时内存要多分配一个存储结束符。
/* 数据分析 */ #include<stdio.h> #include<stdlib.h> typedef struct student { char grade[5]; // 每个成员最后都要加一个结束符,因此成员长度比实际大一 char department[3]; char major[3]; char cclass[3]; char number[4]; }student; int main() { char number[14]; student s; int i; scanf_s("%s",number,14); // scanf_s用于字符型数据时,要加上字符个数! for(i=0;i<4;++i) s.grade[i]=number[i]; s.grade[i]='\0'; // 加字符结束符 s.department[0]=number[i++]; s.department[1]=number[i++]; s.department[2]='\0'; s.major[0]=number[i++]; s.major[1]=number[i++]; s.major[2]='\0'; s.cclass[0]=number[i++]; s.cclass[1]=number[i++]; s.cclass[2]='\0'; s.number[0]=number[i++]; s.number[1]=number[i++]; s.number[2]=number[i++]; s.number[3]='\0'; printf("学院:%s;专业:%s;年级:%s;班级:%s;学号:%s\n",s.department,s.major,s.grade,s.cclass,s.number); system("pause"); return 0; }