chap06:数组(array)


数组是一种派生类型的数据。

定义: 是一组具有相同类型、相同名称的有有序数据(变量)的集合。

  • 相同类型:数组里的数据具有相同数据类型;
  • 相同名称:个体通过下标(索引)来区分;
  • 有序:在内存中按顺序连续存放,最低的地址对应第一个元素,最高的地址对应最后一个元素。;

数组有一维数组多维(二维、三维……)数组,我们先从一维数组开始介绍。

一、一维数组:

1、一维数组的声明(定义)

type arrayName [arraySize];
  • type: 可以是任意有效的 C 数据类型;
  • arrayName: 数组名(符合标识符的命名规则);
  • arraySize: 必须是一个大于零的整型常量;

例:

int score[5];  //定义了一个整型数组score,含有5个数组元素。
double balance[10]; //定义了一个浮点型数组balance,含有10个数组元素。

2、访问数组元素

数组元素可以通过数组名称加下标(索引)进行访问。元素的索引是放在方括号内,跟在数组名称的后边。

  • 下标(索引): 从0开始(基索引)到arraySize-1;可以看成是从第一个数组元素的偏移量。数组元素在内存中按顺序连续存放,最低的地址对应第一个元素,最高的地址对应最后一个元素。;

    在这里插入图片描述

    • 数组长度arraySize可以使用 sizeof 运算符来获取数组的长度,例如:
    int numbers[5];
    int array_size = sizeof(numbers) / sizeof(numbers[0]);
    
    • 数组名
      在 C 语言中,数组名表示数组的地址,即数组首元素的地址。下标就是偏移量
  • 数组元素可以像普通变量一样使用(赋值、参与运算……),以称为下标变量

例: 上例中定义的score数组包含有5个数组元素,分别为:score[0],score[1],score[2],score[3],score[4]

int score[5];  //定义了一个整型数组score,含有5个数组元素。
score[0]=10;
score[1]=score[0]*2;
score[2]=score[0]+score[1];
printf("%d,%d,%d",score[0],score[1],score[2]);

3、初始化数组

在 C 中,您可以逐个为数组元素赋值(很麻烦),也可以使用一个初始化语句,如下所示:

int score[5]={10,20,30,40,50};

如果您省略掉了数组的大小,数组的大小则为初始化时元素的个数。因此,如果:

int score[]={10,20,30,40,50};

它与前一个实例中所创建的数组是完全相同的。

只有在初始化时,才能省略数组的大小。如以下定义是错误的。

int score[]; //错误的

如果初始化的值的数量小于数组的大小(如下所示),后面的数组元素自动初始化为零值。

int score[5]={10,20}; //score[0],score[1]分别初始化为10,20,score[2],score[3],score[4]为0

### 4、通过循环访问数组元素

下面的程序是不是很繁琐!

#include <stdio.h> 
int main ()
{
    int score[5]; 
    
    //输入5个值分别给5个数组元素
    scanf("%d",&score[0]);
    scanf("%d",&score[1]);
    scanf("%d",&score[2]);
    scanf("%d",&score[3]);
    scanf("%d",&score[4]);
    
    //输出5个数组元素的值
    printf("%d,%d,%d,%d,%d\n",score[0],score[1],score[2],score[3],score[4]);	
} 

如果是数组大小是50个呢?这样写就是一个灾难!

可以用循环实现:

#include <stdio.h> 
int main ()
{
    int score[5]; 
    
    //输入5个值分别给5个数组元素
    for(i=0;i<5;i++){
        scanf("%d",&score[i]);
    }    
    
    //输出5个数组元素的值
    for(i=0;i<5;i++){
        printf("%d  ",score[i]);	
    }    
} 

非常重要: 仔细体会下面程序。

#include<stdio.h>
#include<stdlib.h>  //要使用随机函数,必须要包含这个库 
#define  N 10 //宏定义:定义一个符号常量N来表示数组大小,这样便于修改 

int main (){
	int score[N],i,sum=0;
	
	srand(time(0)); //初始化随机数发生器,要不然产生的随机数是伪随机数
	// 随机生成N个数给相应数组元素
	for(i=0;i<N;i++)
		score[i]=rand()%101;   // rand()%101  产生0~100之间的随机数 
   
   //求所有数组元素的和 
    for(i=0;i<N;i++)
    	sum += score[i];
    
	//输出所有数组元素的值,每个占一行 
    for(i=0;i<N;i++){
        printf("%d\n",score[i]);	
    }    	
    
    printf("总和:%d,平均数:%.2f\n",sum,(float)sum/N);
	// 这样写:(float)sum/N    /两边都是整数时,是整除,要强制转换 
}

注: 产生 a~b之间的随机整数(包括a,b): a + rand()%(b-a+1)

二、用一维字符数组存储处理字符串

1、一些基本概念

C语言中只有字符串常量的概念。
字符串字面值或常量:是括在双引号 “” 中的。一个字符串包含类似于字符常量的字符:普通的字符、转义序列和通用的字符。如:“Hello” , “Don\'t give up and don\'t give in.”

C语言里没有字符串变量的概念(后面我们学习其它语言有),但我们可以用一维字符数组来处理字符串,因此我们可以将一维字符数组看成字符串变量。

但要注意字符数组不是字符串变量,只是可以用来存储字符串。

注意:(非常重要)字符串在内存中存储时,会自动添加一个null 字符,即’\0’。如字符串"Hello"在内在中存储时如下图

在这里插入图片描述

占6个字节的空间。

因此:

char s1[]={'H','e','l','l','o'};
char s2[]={"Hello"};

这两个字符数组是不一样的!s1里存的就不是字符串,s2里存的就是字符串。它们在内在中所占的字节数是不一样的(数组长度是不一样的)。

如:输出它们所占的字节数。

printf("S1占%d个字节,S2占%d个字节。\n",sizeof(s1),sizeof(s2));

输出:S1占5个字节,S2占6个字节。

2、字符串的输入与输出

字符串的输出

在C语言中,有两个函数可以在控制台(显示器)上输出字符串,它们分别是:

  • puts():输出字符串并自动换行,该函数只能输出字符串。
  • printf():通过格式控制符%s输出字符串,不能自动换行。除了字符串,printf() 还能输出其他类型的数据。
#include <stdio.h>
int main(){    
    char str[] = "I am a student.";    
    printf("%s", str);  //通过字符串名字输出   
    printf("%s", "I am a student.");  //直接输出 
    
    printf("\n")
    puts(str);  //通过字符串名字输出    
    puts("I am a student.");  //直接输出 
}

运行结果:

I am a student. I am a student.

I am a student.

I am a student.

注意,输出字符串时只需要给出名字,不能带后边的[ ],例如,下面的两种写法都是错误的:

printf(“%s\n”, str[]);
puts(str[10]);

字符串的输入:

在C语言中,有两个函数可以让用户从键盘上输入字符串,它们分别是:

  • scanf():通过格式控制符%s输入字符串。除了字符串,scanf() 还能输入其他类型的数据。
  • gets():直接输入字符串,并且只能输入字符串。

但是,scanf() 和 gets() 是有区别的:

  • scanf() 读取字符串时以空格为分隔,遇到空格就认为当前字符串结束了,所以无法读取含有空格的字符串。
  • gets() 认为空格也是字符串的一部分,只有遇到回车键时才认为字符串输入结束,所以,不管输入了多少个空格,只要不按下回车键,对 gets() 来说就是一个完整的字符串。换句话说,gets() 用来读取一整行字符串。

请看下面的例子:

#include <stdio.h>
int main(){    
    char str1[30] = {0};    
    char str2[30] = {0};    
    char str3[30] = {0};    
    //gets() 用法    
    printf("Input a string: ");    
    gets(str1);    
    //scanf() 用法    
    printf("Input a string: ");    
    scanf("%s", str2);    
    scanf("%s", str3);
    
    printf("\nstr1: %s\n", str1);    
    printf("str2: %s\n", str2);    
    printf("str3: %s\n", str3);    
    return 0;
}

运行结果:

Input a string: This is a flower.↙
Input a string: This is a flower.↙

str1: This is a flower.
str2: This
str3: is

第一次输入的字符串被 gets() 全部读取,并存入 str1 中。第二次输入的字符串,前半部分被第一个 scanf() 读取并存入 str2 中,后半部分被第二个 scanf() 读取并存入 str3 中。

注意,scanf() 在读取数据时需要的是数据的地址,这一点是恒定不变的,所以对于 int、char、float 等类型的变量都要在前边添加&以获取它们的地址。但是在本段代码中,我们只给出了字符数组的名字,却没有在前边添加&,这是为什么呢?因为数组名字就是数组的首地址,所以再添加&就是多此一举,会导致错误了。

3、字符串处理函数

C语言提供了丰富的字符串处理函数,可以对字符串进行输入、输出、合并、修改、比较、转换、复制、搜索等操作,使用这些现成的函数可以大大减轻我们的编程负担。

用于输入输出的字符串函数,例如printfputsscanfgets等,使用时要包含头文件stdio.h,而使用其它字符串函数要包含头文件string.h

本节只能讲解几个常用的。

1)字符串连接函数 strcat()

strcat 是 string catenate 的缩写,意思是把两个字符串拼接在一起,语法格式为:

strcat(arrayName1, arrayName2);

arrayName1、arrayName2 为需要拼接的字符串。

strcat() 将把 arrayName2 连接到 arrayName1 后面,并删除原来 arrayName1 最后的结束标志'\0'。这意味着,arrayName1 必须足够长,要能够同时容纳 arrayName1 和 arrayName2,否则会越界(超出范围)。

strcat() 的返回值为 arrayName1 的地址。

下面是一个简单的演示:

#include <stdio.h>
#include <string.h>
int main(){
	char str1[100]="The URL is ";
    char str2[60];
    printf("Input a URL: ");
    gets(str2);
    strcat(str1, str2);
    puts(str1);
    return 0;
}
2)字符串复制函数 strcpy()

strcpy 是 string copy 的缩写,意思是字符串复制,也即将字符串从一个地方复制到另外一个地方,语法格式为:

strcpy(arrayName1, arrayName2);

strcpy() 会把 arrayName2 中的字符串拷贝到 arrayName1 中,字符串结束标志'\0'也一同拷贝。请看下面的例子:

#include <stdio.h>
#include <string.h>
int main(){
	char str1[50] = "《C语言趣味编程》";
    char str2[50] = "http://c.biancheng.net/cpp/u/jiaocheng/";
    strcpy(str1, str2);
    printf("str1: %s\n", str1);
    return 0;
}

你会看到,将 str2 复制到 str1 后,str1 中原来的内容就被覆盖了。

另外,strcpy() 要求 arrayName1 要有足够的长度,否则不能全部装入所拷贝的字符串。

3)字符串比较函数 strcmp()

strcmp 是 string compare 的缩写,意思是字符串比较,语法格式为:

strcmp(arrayName1, arrayName2);

arrayName1 和 arrayName2 是需要比较的两个字符串。

字符本身没有大小之分,strcmp() 以各个字符对应的 ASCII 码值进行比较。strcmp() 从两个字符串的第 0 个字符开始比较,如果它们相等,就继续比较下一个字符,直到遇见不同的字符,或者到字符串的末尾。

返回值: 若 arrayName1 和 arrayName2 相同,则返回0;若 arrayName1 大于 arrayName2,则返回大于 0 的值;若 arrayName1 小于 arrayName2,则返回小于0 的值。

对4组字符串进行比较:

#include <stdio.h>
#include <string.h>
int main(){
	char a[] = "aBcDeF";
    char b[] = "AbCdEf";
    char c[] = "aacdef";
    char d[] = "aBcDeF";
    printf("a比较b: %d\n", strcmp(a, b));
    printf("a比较c: %d\n", strcmp(a, c));
    printf("a比较d: %d\n", strcmp(a, d));
    return 0;
}

运行结果:
a 比较 b: 32
a 比较 c: -31
a 比较 d: 0

三、二维数组

在实际问题中有很多数据是二维的或多维的,因此C语言允许构造多维数组。多维数组元素有多个下标,以确定它在数组中的位置。本节只介绍二维数组,多维数组可由二维数组类推而得到。

1、二维数组的定义

二维数组定义的一般形式是:

dataType arrayName[length1][length2];

其中,dataType 为数据类型,arrayName 为数组名,length1 为第一维下标的长度,length2 为第二维下标的长度。

我们可以将二维数组看做一个 Excel 表格,有行有列,length1 表示行数,length2 表示列数,要在二维数组中定位某个元素,必须同时指明行和列。例如:

int a[3][4];

定义了一个 3 行 4 列的二维数组,共有 3×4=12 个元素,数组名为 a,即:

a[0][0], a[0][1], a[0][2], a[0][3]
a[1][0], a[1][1], a[1][2], a[1][3]
a[2][0], a[2][1], a[2][2], a[2][3]

如果想表示第 2 行第 1 列的元素,应该写作 a[2][1]

也可以将二维数组看成一个坐标系,有 x 轴和 y 轴,要想在一个平面中确定一个点,必须同时知道 x 轴和 y 轴。

二维数组在概念上是二维的,但在内存中是连续存放的;换句话说,二维数组的各个元素是相互挨着的,彼此之间没有缝隙。那么,如何在线性内存中存放二维数组呢?有两种方式:

  • 一种是按行排列, 即放完一行之后再放入第二行;
  • 另一种是按列排列, 即放完一列之后再放入第二列。

在C语言中,二维数组是按行排列的。也就是先存放 a[0] 行,再存放 a[1] 行,最后存放 a[2] 行;每行中的 4 个元素也是依次存放。数组 a 为 int 类型,每个元素占用 4 个字节,整个数组共占用 4×(3×4)=48 个字节。

你可以这样认为,二维数组是由多个长度相同的一维数组构成的

2、二维数组的初始化(赋值)

二维数组的初始化可以按行分段赋值,也可按行连续赋值。

例如,对于数组 a[5][3],按行分段赋值应该写作:

int a[5][3]={ {80,75,92}, {61,65,71}, {59,63,70}, {85,87,90}, {76,77,85} };

按行连续赋值应该写作:

int a[5][3]={80, 75, 92, 61, 65, 71, 59, 63, 70, 85, 87, 90, 76, 77, 85};

这两种赋初值的结果是完全相同的。

3、对于二维数组的初始化还要注意以下几点:

  1. 可以只对部分元素赋值,未赋值的元素自动取“零”值。例如:
int a[3][3] = {{1}, {2}, {3}};

是对每一行的第一列元素赋值,未赋值的元素的值为 0。赋值后各元素的值为:
1 0 0
2 0 0
3 0 0

再如:

int a[3][3] = {{0,1}, {0,0,2}, {3}};

赋值后各元素的值为:
0 1 0
0 0 2
3 0 0

  1. 如果对全部元素赋值,那么第一维的长度可以不给出。例如:
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};

4、二维数组可以看作是由一维数组嵌套而成

如果一个数组的每个元素又是一个数组,那么它就是二维数组。当然,前提是各个元素的类型必须相同。根据这样的分析,一个二维数组也可以分解为多个一维数组,C语言允许这种分解。

例如,二维数组a[3][4]可分解为三个一维数组,它们的数组名分别为 a[0]、a[1]、a[2]。

这三个一维数组可以直接拿来使用。这三个一维数组都有 4 个元素,比如,一维数组 a[0] 的元素为 a[0][0]a[0][1]a[0][2]a[0][3]

【实例】 一个学习小组有 5 个人,每个人有 3 门课程的考试成绩,求该小组各科的平均分和总平均分。

MathCEnglish
张三807592
616571
596370
858790
767785

对于该题目,可以定义一个二维数组 a[5][3] 存放 5 个人 3 门课的成绩,定义一个一维数组 v[3] 存放各科平均分,再定义一个变量 average 存放总平均分。最终编程如下:

#include <stdio.h>
int main(){
	int i, j;  //二维数组下标    
	int sum = 0;  //当前科目的总成绩    
	int average;  //总平均分    
	int v[3];  //各科平均分    
	int a[5][3];  //用来保存每个同学各科成绩的二维数组  
    
	printf("Input score:\n");    
	for(i=0; i<3; i++){
    	for(j=0; j<5; j++){
        	scanf("%d", &a[j][i]);  //输入每个同学的各科成绩
            sum += a[j][i];  //计算当前科目的总成绩
        }
        v[i]=sum/5;  // 当前科目的平均分        
        sum=0;    
     }
     average = (v[0] + v[1] + v[2]) / 3;
     printf("Math: %d\nC Languag: %d\nEnglish: %d\n", v[0], v[1], v[2]);
     printf("Total: %d\n", average);
     
     return 0;
}
  • 26
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值