“同学们,我们前面讲过了变量和数据类型,我们来复习一下,用C语言变量存储数据1~10,然后再输出。小明小红你们上黑板来写,其他人写纸上就可以。”
小明和小红走向讲台拿起粉笔写下:
小红:
#include<stdio.h>
int main()
{
int a=1,b=2,c=3,d=4,e=5,f=6,g=7,h=8,i=9,j=10;
printf("%-2d%-2d%-2d%-2d%-2d%-2d%-2d%-2d%-2d%-2d",a,b,c,d,e,f,g,h,i,j);
return 0;
}
小明:
#include<stdio.h>
int main()
{
int a0,a1,a2,a3,a4,a5,a6,a7,a8,a9;
a0 = 1;a1 = 2;a2 = 3;a3 = 4;a4 = 5;
a5 = 6;a6 = 7;a7 = 8;a8 = 9;a9 = 10;
printf("%-2d%-2d%-2d%-2d%-2d%-2d%-2d%-2d%-2d%-2d",a0,a1,a2,a3,a4,a5,a6,a7,a8,a9);
return 0;
}
“好,写完可以回到座位了。”小明和小红放好粉笔,走回了座位。
“大家基本上都写好了,我刚看了一圈,写的都大同小异,和黑板上两位同学的都很类似。首先,这样写没有问题,符合C语言语法规范,也符合需求。下面我们来分析一下两位同学的代码。”
“先来看小红写的,小红同学是用a~j10个英文字母作为整型变量名分别存储的10个数字;小明同学则是用a1-a10来分别存储的10个数字。首先,从变量的定义角度讲都没有问题,但是很明显,我们存储的是一串连续的变量,但是变量在内存中的声明可能并不是连续的,也就是说,我们的变量在定义时,内存空间是随机获取的,那么有没有一种方式,能在内存中获取一串连续递增的地址空间来存储一些变量,方便我们循环遍历呢?答案是肯定的,聪明的C语言开发者们创建了数组存储格式。就是在内存中申请一连续递增的空间来存储变量的。”
定义数组:
语法:datatype 变量名[长度]
代码:
#include<stdio.h>
int main(){
int a[3]={1,2,3};
printf("%d\n%d\n%d\n",&a[0],&a[1],&a[2]);
return 0;
}
#include<stdio.h>
int main(){
int a[3];
a[0]=1;
a[1]=2;
a[2]=3;
printf("%d\n%d\n%d\n",&a[0],&a[1],&a[2]);
return 0;
}
“上面的示例就是整型数组a的定义与赋值,我们向内存空间申请了三个int类型的连续空间,并通过两种不同的赋值语句给数组空间的变量进行了赋值,那么我们来验证一下,输出数组各元素的地址,观察他们是否为连续的空间:”
输出结果如下:
“从输出的十进制的地址可以看出,数组元素a[0]、a[1]、a[2]的地址空间是连续的,并且每次地址增加4,因为我们申请的数组类型是int类型的,前面我们讲过,一个int类型变量会占4个字节,所以连续的地址号会相差4。”
“那么,除了int类型外,我们还可以定义其他类型,比如float、char等。这里便不一一举例了,下面我结合地址和循环让大家更加深入的了解数组。”
“其实,数组就是多个相同类型的变量集合。”
“数组的地址通常是数组首元素的地址。我们可以定义一个指针变量查看数组。”
#include<stdio.h>
int main(){
int a[5]={1,2,3,4,5};
int *p=a;
printf("%d",*p);
return 0;
}
上述代码中的,指针变量*p指向数组a或者说是数组a的首地址,然后输出指针变量的值,那么固然等于数组中的下标为0的元素1,也就是说我们可以通过指针变量向指向单一变量一样指向数组。
那么既然如此,我将int型的指针指向数组的首个元素,然后依次向后移动n次,就可以对数组完成一次遍历。
#include<stdio.h>
int main(){
int a[5]={1,2,3,4,5};
int *p=a;
for(int i=0;i<5;i++){
printf("%d,",*p+i);
}
return 0;
}
除了这种遍历方法外,我们更常用的是数组下标的遍历:
#include<stdio.h>
int main(){
int a[5]={1,2,3,4,5};
for(int i=0;i<5;i++){
printf("%d,",a[i]);
}
return 0;
}
同样,我们也可以用指针或者数组下标的方式完成对数组内容的修改。
#include<stdio.h>
int main(){
int a[5]={1,2,3,4,5};
int *p=a;
*p=2;
a[1]=3;
for(int i=0;i<5;i++){
printf("%d,",a[i]);
}
return 0;
}
“数组除了除了定义数字还可以定义字符串,下面我们来说说字符串数组。”
“我们知道,数组赋值有很多方式:”
// 对单个元素赋值
int a[3];
a[0] = 3;
a[1] = 100;
a[2] = 34;
// 整体赋值(不指明数组长度)
float b[] = { 23.3, 100.00, 10, 0.34 };
// 整体赋值(指明数组长度)
int m[10] = { 100, 30, 234 };
// 字符数组赋值
char str1[] = "http:csnd.codeknight.cn";
// 将数组所有元素都初始化为0
int arr[10] = {0};
char str2[20] = {0};
“赋值虽然是个很简单的事情,但是有一点我们需要注意,就是通常情况下,我们初始定义的数组长度要大于实际赋值的元素的长度,因为在数组定义时,系统会默认给我们的数组后面添加一个结束符‘\0’,也就是cpu判断数组尾部的标志,当遇到这个符号时,就证明这是数组的结尾了。但是这个符号我们是看不见的,也不需要我们看见,只要我们程序员知道有它的存在即可,就向scanf()在缓冲区有’\n’做结束标志位是一样的,'\0’也是数组的结束标志位。所以通常我们定义数组时会比实际大一个数,也就是为了存放标志位。但是,我们现在通用大部分系统或编译器都会自动给我们补充这个位。”
将’\0’作为循环判断数组结束的条件
#include<stdio.h>
#include<string.h>
int main(){
//char s[]="hello";
char ss[5]={'h','e','l','l','o'};
int i=0;while(ss[i]!='\0'){
printf("%c",ss[i]);
i++;
}
return 0;
}
“数组有结束,也有开始,如果定义一个数组长度为n,那么有效范围只有a[0]~a[n-1],若是超出这个范围,就称为数组越界。超出下界为“下溢”,超出上界为“上溢”。”
“最后,我们来聊一聊多维数组。”
“像我们以上例子所讲的都是属于一维数组,就像是一条x数轴,上面不同的点表示不同的值。或者说是没有首位相连的一条珍珠项链,每个珍珠都是数组中连续挨着的数组元素。但是,除了这些一维数组外,数组还有二维、三维、……n维数组,根据不同的维度可以存储更多的数据。”
#include<stdio.h>
const int N=10;
int main(){
int a[N][N]={
{1,2,3},
{4,5,6},
{7,8,9}
};
for(int i=0;i<3;i++){
for(int j=0;j<3;j++){
printf("%d ",a[i][j]);
}
printf("\n");
}
return 0;
}
#include<stdio.h>
const int N=10;
int main(){
int a[N][N][N]={
{
{1,2,3},
{1,2,3},
{1,2,3},
},
{
{4,5,6},
{4,5,6},
{4,5,6},
},
{
{7,8,9},
{7,8,9},
{7,8,9},
},
};
for(int k=0;k<3;k++){
for(int i=0;i<3;i++){
for(int j=0;j<3;j++){
printf("%d ",a[k][i][j]);
}
printf("\n");
}
}
return 0;
}
“以上便是二维、三维数组的例子,想要了解更多的同学,课下就要多钻研,多实践。”