如何系统学习C 语言(中)之 数组篇
Posted Charmchin学习博客
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何系统学习C 语言(中)之 数组篇相关的知识,希望对你有一定的参考价值。
前面了解了c 语言的基础部分,下面在对它进行一个深入的了解吧
数组
1,一维数组
数组是用来干嘛的呢?就像我们之前所说的变量一样,它是用来存储数据的。那它和变量存储数据有什么不同呢?
显然一个变量只能保存一份数据,在程序数据量小的情况下,使用起来比较方便。但是对于大规模的数据,单纯的变量就有点单薄了,对付大规模的数据,我们需要更强大的数据类型,将众多的变量凝聚在一起,也就构成了数组。
简单的来说,数组就是同一类型数据的集合,下面就是定义的简单数组并对其进行初始化:
int a[5]={10,20}; //部分初始化
int a[5]={[4]=20,[2]=10} //指定初始化
float c=[\'A\'];
int m,g[10]; //若数组和变量的类型一致,可以放在一起定义
下面通过一个综合的小案例来加深一下对数组的基本操作。
//要求:由用户输入5个整数,保存到数组中,并按逆序打印所有元素。程序如下:
#include<stdio.h>
int main(){
int i,a[5];
printf("Please enter 5 integers:\\n");
for(i=0;i<5;++i)
scanf("%d",&a[i]); //将i作为下表值,给数组元素赋初值
printf("Print array elements in reverse order:\\n");
for(i=4;i>=0;--i)
printf("%d",a[i]); //将i作为下标值,打印数组元素值
return 0;
}
最后呢,对于数组的操作我们一定要切记不要越界访问,以免访问到数组之外的内存区域。
2,数组作为函数参数
C 语言允许将数组作为函数的参数,即可以将数组名作为函数的实参进行传递。可以在参数名后面加上中括号的形式,表示该参数是一个数组类型。如下就是一个以数组为参数的函数PArray:
void PArray(int arr[]){
for(int i=0;i<5;i++){
arr[i]*=10;
printf("%d ",arr[i]);
}
}
3,字符数组
C 语言中,几乎所有的数据类型都可以被定义为数组。由于数组可以用来存储字符串,因此字符数组在C 语言中的运用极为广泛。
虽然C 语言中没有字符串这种数据类型,但它的运用却极为普遍,比如我们入手的第一个程序"hello world"。注意到,它是用双引号括起来的一段字符序列。另外,字符串还有一个重要的特征:字符串必须以空字符(用转义字符\'\\0\'表示)作为结尾。即使是字符串常量,也会隐含地拥有这个空字符。例如:
"abc" //这个字符串是由4个字符构成的,即字符a、b、c以及空字符,它的大小为4个字节
那我们是否会想,如何才能将"abc" 这个字符串存储到数组中呢?
//方法1:字符式存储
char str[4]={\'a\',\'b\',\'c\',\'\\0\'}; //全部初始化方式
char str[4]={\'a\',\'b\',\'c\'}; //鉴于空字符的ASCII码值为0,也可采用部分初始化的方式
//方法2:字符串式存储
char str[4]="abc";
//在实际编程中为了避免数组的长度不够这样的问题,我们通常建议采用下面这种方式进行赋值:
char str[]="abc";
在看了上述方法2之后,可能我们就在想如何才能得到一个字符串的长度是吗?
对于字符串,它除了有大小之外,还有长度的概念。我们这里就简单的区分一下:
字符串的大小指的是字符串所占内存的字节数,使用sizeof()库函数计算得出,而字符串长度则是指字符串中有效字符的个数,用strlen()库函数计算得出。所谓有效字符,就是除去作为结尾标记的空字符以外的字符。
4,二维数组
啊
,五环,你比六环少一环~,顾名思义,二维数组就是在一维数组的基础上多了一维,简单的说就是多了一个"[ ]"。
至于对二维数组的初始化,与一维数组的初始化类似,下面就提一下行初始化方式:
float score[3][3]={
{88.5,86.5,96}, //第1行
{88.5,86.5,96}, //第2行
{88.5,86.5,96}, //第3行
}
前面说过一维数组可以作为函数参数,那类比一下,二维数组肯定也可以作为参数来进行函数调用。与一维数组作为函数参数时需要在参数名跟上一对中括号类似,二维数组需要两对中括号,其中第一对中括号用于表示第一维的大小,其值可以被省略,即使用空中括号形式;第二对中括号用于表示第二维的大小,其值不可被省略,即必须指明第二维的大小。 下面是一个打印二维数组所有元素的函数例子:
void printScore(float s[][4],int len){
for(int i=0;i<len;i++){
for(int j=0;j<4;j++)
printf("%6.2f",s[i][j]); //以6字符宽度、保留2位小数的格式打印
printf("\\n"); //打印一行元素后进行换行
}
}
最后,我们可以用这样的思想来理解一下C 语言中的数组:
由普通元素(变量)构成的数组,是一维数组,即一维数组是普通元素(变量)的数组。
由一维数组构成的数组,是二维数组,即二维数组是一维数组的数组。
由二维数组构成的数组,是三维数组,即三维数组是二维数组的数组。
.......
理解了数组的嵌套,再去理解指针与数组的关系,就会比较轻松了 ~ ~
实践中回顾(总结)
理解并掌握数组,会为后面的知识扩展打下坚实的基础。而真正掌握数组不能光靠理论知识,而是要更多的去实践,比较经典的题目有矩阵转置和冒泡排序,下面就用一个经典的冒泡排序算法的实现来对数组的知识进行回顾。
编写程序,在数组中保存1~100的10个随机整数,对数组进行升序排序,并将排序后的数组元素打印输出,代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
//冒泡排序
void bubble(int a[], int len) {
int i, j, tmp;
//1外层循环会执行 len - 1 次,表示共进行 len - 1 轮的比较过程
for (i = 0; i < len - 1; ++i) {
/*内层循环用于完成每一轮的比较过程。 它会从待排序序列中的第一个元素开始,逐个和后面的元素进行比较。另外, 随着外层循环中i 值的不断自增,表达式“len - 1-i”会使内层循环的 执行次数逐渐减少,这相当于把每轮完成后的最大值元素从待排序序列中排除*/
for (j = 0; j < len - 1 - i; ++j) {
if (a[j]> a[j + 1]) //检查前一个元素是否比后一个元素大 //如果是,就进行两个元素的值的互换
{
tmp = a[j]; //将前一元素值赋给 tmp
a[j] = a[j + 1];//将后一个元素值赋给前一个元素
a[j + 1] = tmp;// 将 tmp 值赋给后一个元素
}
}
}
}
int main() {
int i, arr[10]; //设置随机数种子
srand(time(NULL)); //通过循环获取 10 个随机数,并将其保存到数组arr中
for (i = 0; i < 10; ++i)
arr[i] = rand() % 100 + 1; //调用 bubble 函数进行冒泡排序,参数 1 为数组 arr,参数 2 为数组arr 的长度
bubble(arr, 10);
//打印输出排序后数组中的各元素值
for (i = 0; i < 10; ++i)
printf("%d ", arr[i]);
return 0;
}
好了,经过上面的回顾,大家是否对C 语言中的数组有了一个更深的印象了呢?
学习大数据:Java基础篇之数组
Java基础——数组
数组是一个语言中的基本要素,它能够用来保存和管理多个变量。例如,如果要统计三个学生的成绩,可以手动的定义三个变量 a、b、c,如果要输出这三个变量的值,也可以写三个输出语句。但是,如果要管理一个年级所有学生的成绩,此时,可能有上百个学生。如果为每个学生都手动定义一个变量的话,则程序中会有上百个变量。并且,如果要 输出所有学生的成绩,就会有上百个输出语句。很显然,这样的代码是非常复杂和繁琐的。
因此,我们就需要有一个办法,能够比较方便的管理多个数据。在这种情况下,我们就应该用到数组。
一、数组的基本操作
数组:一次性定义多个同类型的变量。
数组空间在内存中必定是连续的。
1,创建数组
int []a 或int a[] 表示定义一个int类型的一维数组变量a
a=new int[3]; 表示分配了一个长度为3个int的内存空间
int[]a=new int[3]; 也可以两部写在一块,表示定义一个int类型的一维数组同时分配长度为3个int的内存空间
2,下标
定义一个数组,就是一次性定义多个同类型的变量。为数组分配完空间之后,就可以使用数组了。使用数组的时候,应当用下标来表示数组元素。例如,上面分配了长度为3个int 类型的变量,这3个int 变量分别可以用:a[0]、a[1]、a[2]来表示。
即如果分配了一个长度为 n 的数组,则数组的下标范围为 0~n-1。
a[0] = 10; //对数组元素进行赋值
a[1] = 20;
a[2] = 30;
3,遍历数组
遍历数组:按照一定顺序,把每个元素都访问一遍,不会遗漏,不会重复
? 示例代码:
package p5;
public class TestArray{
public static void main(String [] args){
int[]a;//数组的定义 int []a; int a [];
a=new int[3];//初始化变量a,为数组分配空间,定义数组的长度
a[0]=1;
a[1]=2;
a[2]=3;//数组的赋值
for(int i=0;i<3;i++){//遍历数组
System.out.println(a[i]);
}
}
}
4,数组的初始化
数组元素在没有赋值的情况下可以使用,数组元素有特定的默认值
byte、short、int、long 这四种整数类型,默认值为 0?
? float 和 double 这两种小数类型,默认值为 0.0
boolean 默认值为 false
char 默认值也为 0
注意,char 类型的 0 不是指的字符‘0’,而是指的编码为 0。
对于对象类型的数组来说,默认值为 null。
显示初始化
第一种形式如下:
int[] a = {10, 20, 30};
特点是,只能在定义数组变量的同时使用。
第二种语法形式如下:
int[] a = new int[]{10, 20, 30};
注意,这种语法下,new 关键字后面的方括号中没有数字,也就是说,显式初始化时不能规定数组长度,数组长度由后面的元素个数决定。
package p5;
public class TestArray{
public static void main(String [] args){
int[]a;//数组的定义 int []a; int a [];
a=new int[3];//初始化变量a,为数组分配空间,定义数组的长度
a[0]=1;
a[1]=2;
a[2]=3;//数组的赋值
/*
int[]a={1,2,3};//数组的显示初始化,数组长度有括号内元素个数决定
//int []a=new int [](1,2,3);
//int []a=new int [3](1,2,3); error!!!
*/
for(int i=0;i<3;i++){//遍历数组
System.out.println(a[i]);
}
}
}
二、数组在内存中的表示
Java 数组在内存中的表示情况。看下面两行代码
int[] a;
? a = new int[3];
我们结合代码,分析一下数组在内存中的表示。
第一行,定义了一个数组变量 a,此时没有分配连续的内存空间。
第二行,首先执行了 new int[3],这句代码分配了一个段连续的内存空间,总共能够放 入 3 个 int,因此是 12 个字节。这 12 个字节每个字节都有自己的一个内存地址,其中,12 个字节中的第一个字节,它的地址被称之为这块内存的“首地址”。假设首地址为 1a2b。第三步,执行完了 new int[3]之后,进行了赋值。究竟是把什么赋值给了变量 a 呢?注 意,赋值赋的是内存的首地址。也就是说,数组变量保存的是数组中的首地址 1a2b。如下图所示
?
三、二维数组
1,二维数组的基本操作
二维数组以及多维数组:数组的数组。
比如说,我们日常生活中的抽屉,我们可以认为抽屉就是用来存放多个物品的,因此抽屉就是一个物品的数组。而一个柜子中,可以存放多个抽屉,因此我们可以理解为,柜子就是抽屉组成的数组。因此,柜子就可以理解为是“数组的数组”,也就是:柜子的元素是抽屉,而抽屉本身又是一个数组。
Ⅰ,创建二维数组
int[][] a; 或者 int[] a[]; 或者 int a[][];
定义二维数组变量的时候,同样没有分配数组空间。
?
a = new int[3][4];
为二维数组分配内存空间,分配一个三行四列的二维数组
我们可以这 么来看:我们分配的这个二维数组就相当于一个柜子,这个柜子有三层,每层放一个抽屉。这个抽屉里面分成了四个格子,每个格子又能放一个元素。由于二维数组是“数组的 数组”,因此,二维数组的“行”,指的是这个二维数组中,包含几个元素。由于二维数组 的元素是一维数组,因此,“行”也就是二维数组中包含几个一维数组。而列,则指的是, 二维数组中的每一个一维数组,各自都包含几个元素。
?
a[0][2]
表示第 0 行第 2 列的元素
int [][]a={{1,2,3}{5,6,7}{8,9,10}{11,12,13}};
二维数组的显示初始化
Ⅱ,遍历二维数组
遍历二维数组时,要获得行和列两个数值。
首先,二 维数组同样有 a.length 这样的变量,而使用 a.length 获得的长度,是二维数组元素的个数, 也就是行的数目,也可以理解成:柜子里抽屉的个数。而列的数目,就相当于是每一个一 1a2b a … … 1a2b 7 维数组的长度,也可以理解为,是每一个抽屉的大小。对于第 i 个抽屉,我们可以使用 a[i].length 来获得它的长度。
? 示例代码:
package p5;
public class TestArray2{
public static void main(String [] args){
//int [][]a=new int[4][3];//二维数组变量的定义,并为二维数组分配内存空间,
//分配一个三行四列的二维数组
// int [][]a=new int [][3];//error!!!
int [][]a={{1,2,3},{5,6,7},{8,9,10},{11,12,13}};//二维数组的显示初始化
for(int i=0;i<a.length;i++){//遍历二维数组
for(int j=0;j<a[i].length;j++){
System.out.print(a[i][j]+" ");
}
System.out.println();
}
}
}
2,二维数组的内存表示
?
对于 a 数组来说,它还是一个一维数组,这个一维数组的长度为 3,也就 是说,a 数组有三个元素。而 a[0],a[1],a[2]又各自记录了一个一维数组的地址。因此我们把 a 数组称为“一维数组的一维数组”,也就是二维数组。
3,不规则数组
除了普通的二维数组之外,Java 还支持不规则数组。举例来说,如果一个柜子有三个 抽屉,这个三个抽屉中并不一定每个抽屉都具有一样的大小,完全有可能第三个抽屉更 大,元素更多,而第一个抽屉相对就比较小。
int[][] a; //定义数组变量
a = new int[3][]; //先确定第一个维度,表明柜子里有三个抽屉?a = new int[][3]; //error!
a[0] = new int[3]; //上层的抽屉有三个元素
a[1] = new int[4]; //下一层有四个元素
a[2] = new int[5]; //最底层有五个元素
?
四,数组的常见算法
1,数组的扩容
首先,数组空间一旦分配完成之后,长度就不能改变了。因此,我们不能够直接在原 有数组的空间后面增加新的内存空间。我们可以采用以下方式,来增加数组的长度:
①、分配一个新的数组,新数组的长度比原有数组要大(比如长度是原有数组的两倍)
②、把原有数组中的数据,复制到新数组中。
?
package p5;
import java.util.Arrays;
public class TestArrayExpand{
public static void main(String []args){
int []a={1,2,3,4};//定义数组a,并显示初始化a
int []b=new int[8];//定义数组b,并为b分配内存空间,
a=expand(a);
}
public static int[] expand(int[]a){//扩充方法1
int []b=new int[a.length*2];
for(int i=0;i<a.length;i++){
b[i]=a[i];
}
return b;
}
public static int []expand1(int []a){//扩充方法2
int []b=new int [a.length*2];
System.arraycopy(a,0,b,0,a.length);
return b;
}
public static int []expand2(int []a){//扩充方法3
return Arrays.copyOf(a,a.length*2);
}
}
?
2,冒泡排序
在排序的过程中,相邻元素不停进行比 较和交换。在交换的过程中,大的元素沉向数组的末尾,小的元素走向数组的开头;这就好像在水里面:重的东西往下沉,而轻的东西往上浮起来。正因为这种排序方式很像水里 的气泡往上浮的过程,因此,这种排序方式被称为冒泡排序。
接下来,我们来写冒泡排序的代码。如果有五个元素,则需要进行 4 次循环,也就是 说,如果数组的长度是 a.length 的话,则需要进行 a.length-1 次循环。因此,外层循环如下:
for(int i = 0; i<a.length-1; i++){
…
}
内层循环稍有点复杂。我们让内层循环的循环变量为 j,则每次进行比较的时候,比较 的都是 a[j]和 a[j+1]这两个元素。那么 j的循环条件怎么写呢?
第 1 次循环,i 的值为 0,因为要排到最后一个,因此 j+1 最大值为 a.length,j 的最大值为 a.length-1;?
? 第 2 次循环,i的值为 1,j+1 的最大值为 a.length-1,j的最大值为 a.length-2;
第 3 次循环,i的值为 2,j+1 的最大值为 a.length-2,j的最大值为 a.length-3;
由上面,我们可知,每次循环 j+1 的最大值,都是 a.length-i;而 j 的最大值,就是 a.length-i-1。 因此,内层循环条件如下:
for(int i = 0; i<a.length-1; i++){
for(int j = 0; j<a.length–i-1; j++){
比较 a[j]和 a[j+1], 如果 a[j]比 a[j+1]大,则交换两个元素的值 13
}
}
进一步细化,代码为?
for(int i = 0; i<a.length-1; i++){
for(int j = 0; j<a.length–i-1; j++){
if(a[j] > a[j+1]){
则交换 a[j]和 a[j+1]的值
}
}
}
如何交换两个变量的值呢?假设有两个变量 a = 5;b=4;要交换两个 a 和 b 的值,应该怎么做呢?
如果直接执行 a = b 的话,则 a 的值 5 就会被 b 的值覆盖。这样,a 有了 b 的值,但是 b 却无法获得 a 变量原来的值了。因此,为了交换两个变量的值,需要第三个变量参与。
首先,定义一个新变量 t; 然后,把 a 的值赋值给 t:t = a;
接下来,把 b 的值赋值给 a: a=b。这样会覆盖 a 原有的值,但是没关系,a 原有的值 已经被保存在 t 变量中了。
再接下来,把在 t 变量中保存的原有的 a 变量的值,赋值给 b。
? 示例代码:
package p5;
public class TestArraySort{
public static void main(String[]args){
int []data={5,4,2,1,3};//定义一个一维数组,并显示初始化数组
int n=data.length;//定义变量n为数组长度,便于后面使用数组长度
for(int i=1;i<n;i++){//冒泡排序必定是n-1次,i表示第几次冒泡排序
for(int j=0;j<(n-i);j++){//归纳总结,从0开始data[j]与data[j+1]比较,比较n-i次
if(data[j]>data[j+1]){
int t=data[j];
data[j]=data[j+1];
data[j+1]=t;//data[j]与data[j+1]交换位置
}
}
}
for(int i=0;i<data.length;i++){
System.out.print(data[i]+" ");//遍历输出data[i]
}
System.out.println();//换行
}
}
3,选择排序
选择排序是一种简单直观的排序算法。它的工作原理是每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完。选择排序是不稳定的排序方法(比如序列[5, 5, 3]第一次就将第一个[5]与[3]交换,导致第一个5挪动到第二个5后面)
package p5;
public class TestArraySort{
public static void main(String[]args){
int []data={5,4,2,1,3};//定义一个一维数组,并显示初始化数组
int n=data.length;//定义变量n为数组长度,便于后面使用数组长度
for(int i=0;i<(n-1);i++){//选择排序
for(int j=i+1;j<n;j++){
if(data[i]>data[j]){
int t=data[i];
data[i]=data[j];
data[j]=t;
}
}
}
for(int i=0;i<data.length;i++){
System.out.print(data[i]+" ");//遍历输出data[i]
}
System.out.println();//换行
}
}
以上是关于如何系统学习C 语言(中)之 数组篇的主要内容,如果未能解决你的问题,请参考以下文章