当前位置 : 主页 > 编程语言 > c语言 >

C语言初阶-数组

来源:互联网 收集:自由互联 发布时间:2023-08-28
1.一维数组的创建和初始化 1.1 数组的创建 数组是一组相同类型元素的集合 数组的创建方式: type_t arr_name [const_n];//type_t数组元素类型arr_name数组名 const_n常量表达式或常量,用来指定数

1.一维数组的创建和初始化

1.1 数组的创建

数组是一组相同类型元素的集合

数组的创建方式:

type_t   arr_name  [const_n];//type_t数组元素类型 arr_name数组名 const_n常量表达式或常量,用来指定数组的大小 [ ]基本语法形式

int arr[20];  
char ch[5];
double date1[20];
double date2[15+5];//常量表达式指定数组的大小

//在支持C99标准的编译器上,指定数组大小也可以用变量
//在C99标准之前,数组的大小必须是常量或者常量表达式
//在C99之后,数组大小可以是变量,为了支持变长数组(数组的大小通过变量来指定)
//下面代码只能在支持C99标准的编译器上编译
int n=10;
int arr1[n];//变长数组不能初始化

变长数组:用变量指定数组的大小

C语言初阶-数组_数组名

1.2 数组的初始化

在创建数组的同时给它一些合理的值

//不完全初始化 剩余元素默认初始化为0
int arr[10] = {1,2,3};//不止一个元素初始化用{}
//完全初始化
int arr1[10] = {1,2,3,4,5,6,7,8,9,0};

//字符型不完全初始化 剩余元素默认初始化为0(0就是\0,\0的ASCII值是0)
char ch1[10] = {'a','b','c'};
// a b c 0000000 
//用双引号引起来的字符串初始化
char ch2[10] = "abc";
//a b c \0 000000
//虽然这两个初始化结果是一样的,但是性质不一样,第一个只放入了abc,默认补了7个0
//第二个放入了abc\0 默认补了6个0

//不指定数组大小,只进行初始化,不指定元素个数
//数组大小根据初始化的内容确定
int arr2[] = {1,2,3};//数组大小为3
char ch3[] = {'a','b','c'};//3
char ch4[] = "abc";//4 多了一个'\0'

1.3 一维数组的使用

[ ] 下标引用操作符,专门用来访问数组元素的操作符

#include <stdio.h>
int main()
{
   int arr[] = {1,2,3,4,5,6,7,8,9,10};
   //局部的数组;在内存的栈区中找了一块连续的空间去存储数据 
   //数组的每个元素对应一个编号,编号从0开始
   //这里的编号就是数组的下标
   //知道编号(下标)就可以定位到该元素
   
   //[] 下标引用操作符
   arr[4];//访问下标为4的元素 即 5
   
   //打印数组中所有元素
   int i = 0;
   int sz = sizeof(arr) / sizeof(arr[0]);//数组元素个数
   for( i = 0;i < sz;i++)
   {
      printf("%d " ,arr[i]); //访问数组元素可以用变量,创建数组大小在不支持C99的编译器下不可以用变量
   }
   for( i=sz-1;i>=0;i--)//倒序打印
   {
      printf("%d " ,arr[i]); 
   }
   return 0;
}

注意:

  1. 数组是使用下标来访问,下标从0开始
  2. 数组大小可以通过计算得到

1.4 一维数组在内存中的存储

//打印数组每个元素的地址
 for(i=0;i<sz;i++)
 {
   printf("&arr[%d]=%p\n",i,&arr[i])
 }

C语言初阶-数组_数组_02

每个元素的地址之间相差4(一个整型元素占4个字节)随数组下标的不断增长,元素地址也在有规律增长。由此可知:数组在内存中是连续存放的

C语言初阶-数组_创建和初始化_03

2.二维数组的创建和初始化

2.1 二维数组的创建

二维数组能够存放的数据更多一些,相同类型的数据又出现了好多组,解决多组数据的存储

int arr[3][4];//3行4列 一维数组是一行,二维数组是多行
char arr1[5][10];//5行10列 5行,每一行上有10个元素

2.2 二维数组的初始化

//完全初始化
int arr1[3][4]={1,2,3,4,2,3,4,5,3,4,5,6};
//没有分组直接放入全部数据 每4个分一组放到一行上

//不完全初始化
int arr1[3][4]={1,2,3,4,2,3,4,5,3,4};
//没有给全部数据 前面还是按4个一组分配到每一行,剩下不够的补0
//依次往里放,不够后面默认初始化为0
int arr1[3][4]={{1,2},{3,4},{5,6}};
//用{}分组

//不能省略二维数组列的大小,只能省略二维数组行的大小  
int arr2[][4]={{1,2,3,4},{2,3}};
int arr2[][4]={{1,2,3,4,2,3};
//可以不进行分组,每4个一组,剩下不够的补0
//三维数组只能省略第一维,第二维和第三维不能省略

arr1[3][4]={1,2,3,4,2,3,4,5,3,4,5,6};

C语言初阶-数组_创建和初始化_04

arr1[3][4]={1,2,3,4,2,3,4,5,3,4};

C语言初阶-数组_创建和初始化_05

arr1[3][4]={{1,2},{3,4},{5,6}};

C语言初阶-数组_冒泡排序_06

arr2[][4]={{1,2,3,4},{2,3}};    arr2[][4]={{1,2,3,4,2,3};

C语言初阶-数组_二维数组_07

2.3 二维数组的使用

通过下标方式定位某元素   行/列下标(编号)从0开始

#include <stdio.h>

int main()
{   //先确定行0~2再确定列0~3
    int arr[3][4] = {1, 2, 3, 4, 2, 3, 4, 5, 3, 4, 5, 6};
    printf("%d\n",arr[2][0]);          //打印某个元素
    int i = 0;
    for (i = 0; i < 3; i++)
    {
        int j = 0;
        for (j = 0; j < 4; j++)
        {
            scanf("%d",&arr[i][j]);    //给每个元素重新赋值
            //printf("%d ", arr[i][j]);//打印二维数组每一行
        }
        printf("\n");
    }
    return 0;
}

注意:

  1. 访问一个元素或者所有元素都可以通过下标方式(坐标)定位元素
  2. 计算二维数组的行和列:二维数组看作一维数组时,每一行看作一个元素,每个元素(每一行)都是一个一维数组,所以可以把二维数组理解为一维数组的数组
  3. 访问第一行arr[0][j]: arr[0]看作第一行的一维数组的数组名(j:0~3);访问第二行arr[1][j] :arr[1]看作第二行的一维数组的数组名  ;访问第三行arr[2][j]: arr[2]看作第三行的一维数组的数组名(类比一维数组:int arr[10]={0};  访问数组每个元素时采用:arri数组名[下标]对于第一行来说arr[0]就是数组名)

2.4 二维数组在内存中的存储

C语言初阶-数组_数组名_08

每两个元素的地址之间相差4,跨行处元素也是相差4个字节,所以二维数组在内存中也是连续存放的

C语言初阶-数组_数组_09

arr[0]为第一行一维数组的数组名

可以将这个二维数组arr[3][4]理解为连续的有12个元素的一维数组arr[12], 这两个数组在内存中的布局方式是一样的,只是访问形式不一样

为什么必须要输入列(列不能省略)?

int arr[][4]={1,2,3,4,5,6};省略了列完全不知道怎么存放,所以不能省略

3.数组越界

数组的下标是有范围限制的,下标从0开始,n个元素中最后一个元素下标为n-1, 使用了小于0或者大于n-1的下标就造成了数组的越界访问。

C语言初阶-数组_数组名_10

越界访问编译器不一定报错,并不代表程序是对的

C语言初阶-数组_数组名_11

二维数组:行和列也存在越界(在自己范围内越界/在外面范围越界)

4.数组作为函数参数

4.1冒泡排序函数

冒泡排序的核心思想:

两个相邻元素进行比较,如果比较的大小不满足顺序,需要调整顺序。

例: 9 8 7 6 5 4 3 2 1 0 排列成升序

C语言初阶-数组_数组_12

一趟冒泡排序让一个数据来到它最终应该出现的位置上

10个元素需要9趟冒泡排序:采用交换形式排序,9个已经交换到最终应该出现的位置上,剩下的一个已经不要交换了,已经在它最终应该出现的位置上

N个元素需要N-1趟冒泡排序,每一趟冒泡排序内部都是在进行两个相邻元素的比较

错误示范:

#include <stdio.h>

void bubble_sort(int arr[])
//数组传参时,形参有两种写法:
// 1.数组形式 int arr[]直观易理解 并不代表重新创建一个数组
//   []里面可以写数字但是写了也用不上所以一般不写
//   arr看似是数组本质上是一个指针变量   算sizeof(arr) / sizeof(arr[0])
//   32位 4/4=1
// 2.指针形式 int* arr
{
    //趟数
    int sz = sizeof(arr) / sizeof(arr[0]); //元素个数
    // sz=1 下面循环根本就没有进去所以输出还是未排序的数据
    //所以不能在这里求元素个数
    int i = 0;
    for (i = 0; i < sz - 1; i++)
    { //一趟冒泡排序 相邻两个元素比较
      //第一趟10个元素--9次相邻比较   第二趟 9个元素--8次相邻比较
        int j = 0;
        for (j = 0; j < sz - 1 - i; j++)
        {
            if (arr[j] > arr[j + 1])
            { //交换
                int tmp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = tmp;
            }
        }
    }
}

int main()
{
    int arr[] = {9, 8, 7, 6, 5, 4, 3, 2, 1, 0}; //把数组的数据排列成升序
    //将数据内容改变:0123456789 冒泡排序算法进行排序
    bubble_sort(arr); //数组传参时只写数组名,数组名本质上是数组首元素的地址
    int i = 0;
    int sz = sizeof(arr) / sizeof(arr[0]); //元素个数
    for (i = 0; i < sz; i++)
    {
        printf("%d ", arr[i]);
    }
    return 0;
}

正确示范:

#include <stdio.h>
//冒泡排序
void bubble_sort(int arr[], int sz)
{
    int i = 0;
    for (i = 0; i < sz - 1; i++)
    {
        int j = 0;
        for (j = 0; j < sz - 1 - i; j++)
        {
            if (arr[j] > arr[j + 1])
            {
                int tmp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = tmp;
            }
        }
    }
}

int main()
{
    int arr[] = {9, 8, 7, 6, 5, 4, 3, 2, 1, 0};
    int i = 0;
    int sz = sizeof(arr) / sizeof(arr[0]); //元素个数
    bubble_sort(arr, sz);
    for (i = 0; i < sz; i++)
    {
        printf("%d ", arr[i]);
    }
    return 0;
}

4.2数组名是什么?

之前我们总说,数组名就是首元素地址,但是这句话有一点不准确

下面来探讨一下数组名:

#include <stdio.h>
int main()
{
    int arr[10];
    printf("%p\n", arr);     //数组名
    printf("%p\n", &arr[0]); //首元素地址
    printf("%p\n", &arr);   
    
    int n = sizeof(arr);//数组名是首元素地址sizeof(首元素地址)=4/8
    int m =sizeof(&arr[0]);
   
    printf("%d\n",n);//输出40  矛盾了
    printf("%d\n",m);
    return 0;
}

C语言初阶-数组_数组名_13

发现:数组名和首元素地址确实是一样的

数组名确实能表示首元素地址,但是有两个例外,1.sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小,单位是字节 2.&数组名。这里的数组名表示整个数组,取出的是整个数组的地址(还是从首元素开始),除了这两个例外,其他的所有的数组名都表示首元素地址。

C语言初阶-数组_冒泡排序_14

发现:数组名,首元素地址和&数组名三个都是一样的

那么这三个有什么区别?

#include <stdio.h>
int main()
{
    int arr[10];
    printf("%p\n", arr);     //这里数组名就是首元素地址
    printf("%p\n", arr+1);    
    printf("-----------------\n");
    
    printf("%p\n", &arr[0]); //首元素地址
    printf("%p\n", &arr[0]+1);//首元素地址+1跳过4个字节 
    printf("-----------------\n");
    
    printf("%p\n", &arr);   //&数组名:数组的地址
    printf("%p\n", &arr+1); //数组的地址+1跳过整个数组去了
    //差值是0x28 8*1+2*16=40个字节  
    return 0;
}

C语言初阶-数组_创建和初始化_15

值看起来一样,但是意义不一样

二维数组的数组名的理解

C语言初阶-数组_数组名_16

int arr[3][4];
int sz=sizeof(arr);
//sizeof(数组名)中数组名表示整个数组 计算整个二维数组的大小 
//&数组名:这里数组名也是表示整个数组 二维数组的地址
printf("%d\n",sz); //48

arr;
//除以上两种特例外,其他情况下二维数组的数组名也表示首元素地址
//二维数组的首元素地址:是&arr[0][0]? 不是
//用第一行的地址表示首元素地址,这里将二维数组看成数组元素是一维数组的数组。一行就是一个元素
//二维数组的首元素的地址:第一行数组的地址
//第一行一维数组的地址,即&arr[0]

//虽然第一行数组地址与第一行数组首元素地址数值相同,但二者意义是不一样的
#include <stdio.h>
int main()
{ //二维数组的数组名:
    int arr[3][4];

    printf("%p\n", &arr[0][0]);     //二维数组中元素arr[0][0]的地址
    printf("%p\n", &arr[0][0] + 1); //+1跳到下一个元素(地址相差4byte 一个整型的大小)
    printf("--------------------------\n");
    printf("%p\n", &arr[0]);     //第一行数组的地址 arr[0]是第一行数组的数组名
    printf("%p\n", &arr[0] + 1); //+1跳到第二行  (地址相差16byte 4个整型的大小)
    printf("--------------------------\n");
    printf("%p\n", arr);     //二维数组首元素的地址:第一行数组的地址
    printf("%p\n", arr + 1); //+1跳到第二行   (地址相差16byte 4个整型的大小)
    printf("--------------------------\n");
    printf("%p\n", &arr);     //二维数组的地址
    printf("%p\n", &arr + 1); //+1跳出整个二维数组(地址相差48byte 12个整型的大小)
    printf("--------------------------\n");
    printf("%d\n", sizeof(arr));       //二维数组的大小 4*12=48
    printf("%d\n", sizeof(arr[0]));    //第一行数组的大小  4*4=16
    printf("%d\n", sizeof(arr[0][0])); // arr[0][0]元素的大小 4
    printf("--------------------------\n");
    printf("%d\n", sizeof(arr) / sizeof(arr[0]));       //行数 3
    printf("%d\n", sizeof(arr[0]) / sizeof(arr[0][0])); //列数  4

    return 0;
}

C语言初阶-数组_数组名_17

计算二维数组的行和列?

行的计算:总数组的大小/一行的大小 sizeof(arr)/sizeof(arr[0]) (在这里的arr[0]就是第一行的数组名,第一行数组名放到sizeof内部计算第一行整个数组的大小)

列的计算:一行的大小/一个元素的大小 sizeof(arr[0])/sizeof(arr[0][0])

5.数组应用实例

  1. 三子棋
  2. 扫雷游戏
网友评论