黑马程序员——C语言基础---数组2

本文介绍了Java编程中数组和函数参数的使用方法,包括数组作为函数实参和形参的两种形式,二维数组的定义、初始化及遍历,以及二维数组在函数参数中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

                                                                       ------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ------

一、数组和函数参数 

数组可以作为函数的参数使用,进行数据传送。 

数组用作函数参数有两种形式:

一种是把数组元素(下标变量)作为实参使用;

一种是把数组名作为函数的形参和实参使

1、数组元素作函数实参 

数组元素就是下标变量,它与普通变量并无区别。 因此它作为函数实参使用与普通变量是完全相 同的,在发生函数调用时,把作为实参的数组元素的值传送给形参,实现单向的值传送。  

2、数组名作为函数参数 

用数组名作函数参数与用数组元素作实参有几点不同  

1)用数组元素作函数参数不要求形参也必须是数组元素,但是用数组名作函数参数时,则要求形 参和相对应的实参都必须是类型相同的数组  

2)在C语言中,数组名除作为变量的标识符之外,数组名还代表了该数组在内存中的起始地址, 因此,当数组名作函数参数时,实参与形参之间不是"值传递",而是"地址传递",实参数组名将 该数组的起始地址传递给形参数组,两个数组共享一段内存单元,编译系统不再为形参数组分配 存储单元。  

3) 在变量作函数参数时,所进行的值传送是单向的。即只能从实参传向形参,不能从形参传回实参。形参的初值和实参相同,而形参的值发生改变后,实参并不变化,两者的终值是不同的。 

数组传递的时候,数组长度会丢失。所以传送数组名的同时也要传递长度!

<span style="font-size:14px;">#include <stdio.h>
void change(int arr[2]){
    //测试,打印arr的值
    printf("arr = %p\n",arr);
    arr[0]=18;
}
void test1(){    
    int a[2]={1,2};
    printf("a = %p\n",a);
    //a  0x7fff5fbff7c0
    //用数组名作为函数参数,传递的是地址
    //实参是数组名a,形参是arr
    //此时 a和arr在内存中代表了同一块内存空间
    change(a);    
    printf("a[0] = %d\n",a[0]);
}
//定义一个函数
float avg(float score[5]){
    float sum = 0.0f;
    for (int i=0; i<5; i++) {
        //把门课的成绩加到了一起
        sum += score[i];
    }    
    return sum/5;
}
int main(int argc, const char * argv[]) {
    float f1[5]={59.9,58.72,60.11,56.33,81.66};
    //数组名作为了函数的实参
    //是地址传递
    float av = avg(f1);
    printf("%.2f\n",av);    
    return 0;
}</span>

二、二维数组的定义

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

类型说明符 数组名[常量表达式1][常量表达式2] 

其中常量表达式1表示第一维下标的长度,常量表达式2 表示第二维下标的长度。

例如:int a[3][4]; 

说明了一个三行四列的数组,数组名为a,其下标变量的类型为整型。该数组的下标变量共有3×4 个,即: 

再如人站队构成的二维数组:

2、二维数组定义注意事项: 

1)数组名严格遵守标识符命名规范 

2)二维数组名不能和变量同名 

int a;

int a[2][3];

3)数组长度可以是常量,也可以是常量表达式 

int a[2+1][3+3]; //相当于定义了int a[3][5]; 

4)二维数组长度可以是宏定义

5)xcode编译器支持,C99标准不允许的写法 

int x=3,y=4;

int a[x][y]; //相当于定义了int a[3][4];

<span style="font-size:14px;">#include <stdio.h>
#define M 3
int main(int argc, const char * argv[]) {    
    //定义一个二维数组
    int a[2][3];   
    //注意事项
//    int b; 数组名不能和变量名同名
    int b[2][3];    
    //数组的长度可以是常量表达式
    int c[2][1+3];    
    //注意这种写法,其他的编译器可能不支持
    int m=3,n =3;
    int arr[m][n];  //xcode特有的    
    //数组长度可以使用宏定义
    int arr2[3+M][2];    
    return 0;
}</span>

三、二维数组初始化

1、二维数组初始化 

二维数组初始化也是在类型说明时给各下标变量赋以初值。 二维数组可: 

1)按行分段赋值

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};  

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

对于二维数组初始化赋值还有以下说明:

1、定义同时初始化 

(1)完全初始化

int b[4]={1,2,3,4};

int a[2][3]={{1,2,3},{4,5,6}}; // ------------分段赋值

//赋值的时候可以省略内层的大括号 //系统会自动根据数组的第二维的长度,把值划分为对应的组 int a[2][3]={1,2,3,4,5,6}; ------------连续赋值 

//如果说完全初始化,可以省略第一维的长度 

int a[][3]={{1,2,3},{4,5,6},{1,2,3}}; //3

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

(2)部分初始化 

int a1[3][2]={{1},{2},{3}}; 

//部分初始化

int a2[3][3]={{1,2},{2,3,4},{4}}; 

//省略括号的部分初始化

int a3[3][3]={1,2,3,4,5,6,7}; 

//省略第一维,部分初始化

int a3[][3]={1,2,3,4,5,6,7};

//指定元素的初始化

int a4[3][3]={[1]={1,2,3}} 

//指定元素的初始化

int a5[3][3]={[1][2]=10}; 

2、先定义后初始化 

int a[m][n];

下标范围:a[0][0] ~ a[m-1][n-1];

a[0][0]=10; //第一个元素

a[m-1][n-1] = 100; //最后一个元素

3) 数组是一种构造类型的数据。二维数组可以看作是由一维数组的嵌套而构成的。设一维数组的 每个元素都又是一个数组,就组成了二维数组。当然,前提是各元素类型必须相同。根据这样的分析,一个二维数组也可以分解为多个一维数组。C语言允许这种分解。  

如二维数组a[3][4],可分解为三个一维数组,其数组名分别为: 

a[1] 

a[2] 

a[0]  

对这三个一维数组不需另作说明即可使用。这三个一维数组都有4个元素,例如:一维数组a[0]的 元素为a[0][0],a[0][1],a[0][2],a[0][3]。必须强调的是,a[0],a[1],a[2]不能当作下标变量使 用,它们是数组名,不是一个单纯的下标变量。

<span style="font-size:14px;">#include <stdio.h>
int main(int argc, const char * argv[]) {   
    int a1[2]={1,2};    
    //1、特殊的一维数组,第一维有2个元素
    int a[2][3]={{12,23,45},{1,2,3}};  //分段    
    //2、连续赋值
    int a2[2][3]={1,2,3,4,5,6};    
    //3、可以省略第一维
    int a3[][3]={{1,2,3},{3,4,5},{3,4,5}}; //int a[3][3]
    int a4[][2]={1,2,3,4,5,6,7,8};  //int a[4][2]    
    int a5[3][4]={1};
    //可以省略第一维
    int a6[3][4]={{1},{2},{3}};
    //可以省略第一维
    int a7[3][4]={1,2,3,4,5};  //int a7[2][4]    
    //二维数组中,第一维可以省略,第二维长度不能省
    //int a8[3][]={1,2,3,4};
    return 0;
}</span>

四、二维数组遍历 

1、二维数组遍历 

二维数组的元素也称为双下标变量,其表示的形式为:  

数组名[第一维下标][第二维下标]  

其中下标应为整型常量或整型表达式。

例如:

a[3][4] 

表示a数组三行四列的元素。  

下标变量和数组说明在形式中有些相似,但这两者具有完全不同的含义。数组说明的方括号中给出的是某一维的长度,即可取下标的最大值;而数组元素中的下标是该元素在数组中的位置标 

识。前者只能是常量,后者可以是常量,变量或表

<span style="font-size:14px;">#include <stdio.h>
void test(){
    int a[][4]={1,2,3,4,0,5,6,7,8,10,23,0};    
    printf("%d\n",a[2][2]);
    //二维数组的每一个元素都是一个双下标变量
    a[2][2] = 100;
    printf("%d\n",a[2][2]);    
    //每一个元素都打出来看下
    for(int i=0;i<3;i++){        
        for (int j=0; j<4; j++) {
            printf("%d\t",a[i][j]);
        }
        printf("\n");
    }
}
int main(int argc, const char * argv[]) {    
    int score[5][3] = {{80,75,92},{61,65,71},{59,63,70},{85,87,90},{76,77,85}};    
    for (int i=0; i<5; i++) {
        for (int j=0; j<3; j++) {
            printf("%d\t",score[i][j]);
        }
        printf("\n");
    }    
    return 0;
}</span>

五、二维数组存储

1、二维数组存储 

二维数组在概念上是二维的,即是说其下标在两个方向上变化,下标变量在数组中的位置也处于 一个平面之中,而不是象一维数组只是一个向量。但是,实际的硬件存储器却是连续编址的,也 就是说存储器单元是按一维线性排列的。如何在一维存储器中存放二维数组,可有两种方式:一 种是按行排列, 即放完一行之后顺次放入第二行。另一种是按列排列, 即放完一列之后再顺次 放入第二列。  

存储方式:

1)计算机会给二维数组分配一块连续的存储空间 

2)数组名代表数组的首地址,从首地址位置,依次存入第1行、第2行、..... 

3)每一行存储方式,从行首地址还是,依次存储行的第1个元素、第2个元素、第3个元素...... 

4)每个元素占用相同的字节数(取决于数组类型) 

5)并且数组中元素之间的地址是连续。  

在C语言中,二维数组是按行排列的。即,先存放a[0]行,再存放a[1]行,最后存放a[2]行。每行 

中有四个元素也是依次存放。由于数组a说明为int类型,该类型占两个字节的内存空间,所以每 

个元素均占有两个字节

<span style="font-size:14px;">#include <stdio.h>
int main(int argc, const char * argv[]) {
    int a[2][2]={1,2,3,4};
    //数组的首地址
    //在二维数组中存在:
    //数组的首地址 == 数组名 == &a[0] == &a[0][0]
    printf("a       = %p\n",a);
    printf("&a[0]   = %p\n",&a[0]);
    printf("&a[0][0]= %p\n",&a[0][0]);    
    //数组的第二行地址
    printf("&a[1]   = %p\n",&a[1]);
    printf("&a[1][0]= %p\n",&a[1][0]);    
    //二维数组的行数和列数    
    //二维数组占用的总字节数?
    //1)总字节数 = 每行占用的字节数之和
    //2) 总字节数 = 元素的个数 * 元素的类型
    //3) 总字节数 = sizeof(数组名);
    //           = 行 * 列 * sizeof(数组类型)
    int len = 2*2*sizeof(int); //16    
    //通过数组名计算数组占用的总字节数
    len = sizeof(a);  //16    
    //每一个行的字节数如何计算
    //int a[2][2];
    // a[0]  --->   1   2
    // a[1]  --->   3   4
    //计算数组每一行占用的字节数
    len = sizeof(a[1]); //8    
    //每一行有多少列?
    //列数 = 行的总字节数 / 每个元素占用的字节(数组类型)
    len = sizeof(a[0])/sizeof(int);    
    printf("len = %d\n",len);   
    int a1[][3]={1,2,23,34,45,5,5,6,7,7,77,8,18};
    //                   总字节数    每一行占用的字节数
    printf("行数:%zd\n",sizeof(a1)/sizeof(a1[0]));
    //                  每一行占用字节数 / 每个元素的类型
    printf("列数:%zd\n",sizeof(a1[0])/sizeof(int));    
    return 0;
}</span>

六、二维数组做函数参数 

1、二维数组元素作为函数参数 

二维数组元素作为函数参数,相当于变量的值传递过程。 

2、二维数组名作为函数参数 

二维数组名作为函数参数,相当于地址传递。  

1)在被调用函数中对形参数组定义时可以可以指定所有维数的大小,也可以省略第一维的大小说明, 

如:

void Func(int array[3][10]);

void Func(int array[][10]);

注意: 二者都是合法而且等价,但是不能把第二维或者更高维的大小省略,如下面的定义是 不合法的:

void Func(int array[][]); 

因为从实参传递来的是数组的起始地址,在内存中按数组排列规则存放(按行存放), 

而并不区分行和列, 

如果在形参中不说明列数,则系统无法决定应为多少行多少列,不能只指定一维而不指定第 二维,下面写法也是错误的: 

void Func(int array[3][]);

2)实参数组维数可以大于形参数组,

例如实参数组定义为: 

void Func(int array[3][10]); 

而形参数组定义为: 

int array[5][10]; 

这时形参数组只取实参数组的一部分,其余部分不起作用。

<span style="font-size:14px;">#include <stdio.h>
int sum(int x, int y){
    return  x+y;
}
/**
 *  通过函数打印数组的内容
 *
 *  @param
 */
void print_arr(int arr[][3]){
    arr[0][0]=10000;    
    for (int i=0; i<5; i++) {
        for (int j=0; j<3; j++) {            
            printf("%d\t",arr[i][j]);            
        }
        printf("\n");
    }
}
void test1(){
    int score[5][3]={
        {80,75,92},
        {61,65,71},
        {59,63,70},
        {85,87,90},
        {76,77,85}
    };    
    int s = sum(score[1][0],score[2][2]);
    printf("s = %d\n",s);
}
int main(int argc, const char * argv[]) {    
    int score[5][3]={
        {80,75,92},
        {61,65,71},
        {59,63,70},
        {85,87,90},
        {76,77,85}
    };
    //实参score和形参array共用了一块内存空间
    //用数组名作为函数的参数是地址传递
    print_arr(score);   
    //数组名作为函数参数注意事项
    //1)类型和长度要一致
    //2)二维数组作为函数的形参,可以不写第一维的长度   
    return 0;
}</span>

                                                                     -------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值