初始C语言——详细地讲解数组的内容以及易错点

简介: 初始C语言——详细地讲解数组的内容以及易错点

前言

      在上一章中,我们已经详细地介绍了有关函数的相关内容,学习了函数是什么、C语言中函数的分类、函数的参数、调用、函数的嵌套调用和链式访问、函数的声明和定义、函数递归。


      而在这一章,小编将带领大家进行数组的学习,虽然数组的知识点比较小,但是我们还是要进行好好学习,从标题中,我们能看出要详细地学习数组的知识,希望大家看的开心!


一、一维数组的创建与初始化

      小编在之前就比较迷数组,第一就是数组的下标是从0开始的,但是不要害怕,紧跟小编的步伐,来进行学习吧!


1.1 数组的创建

      数组是一组相同类型元素的集合,一定要记住,因为后面我们要学习结构体,结构体可以存放不同类型元素的集合。  


数组的创建方式:

type_t arr_name [const_n]

type_t    是指数组的元素类型

const_n 是一个常量表达式,用来指定数组的大小

      在了解完数组的创建方式后,下面我们来进行数组创建的实例:

//代码1
int arrq[10];
//代码2
int count = 10;
int arr2[count];  //数组这样可以正常创建吗?
//代码3
char arr3[10];
float arr4[10];
double arr5[10];

   代码2这种情况在C语言(C99之前)中是不能使用的,因为数组创建在C99标准之前,[ ]中要给一个常量才可以,不能使用变量。在C99标准支持了变长数组的概念,数组的大小是可以使用变量指定的,但一定要切记变长数组是不能初始化的。


1.2 数组的初始化

为啥要讲数组的初始化呢?


      因为局部变量不初始化的话,局部变量里放的是随机值;全局变量不初始化的话,全局变量里放的是0。所以说初始化很重要。


数组初始化是指:在创建数组的同时给数组的内容一些合理初始值(初始化)。


数组的初始化分为:完全初始化和非完全初始化。


看代码:

    int arr1[10] = { 1,2,3 };        //不完全初始化
  int arr2[] = { 1,2,3,4 };        //不完全初始化
  int arr3[5] = { 1,2,3,4,5 };     //完全初始化
  char arr4[3] = { 'a', 98, 'c' }; //完全初始化
  char arr5[] = { 'a', 'b', 'c' }; //不完全初始化
  char arr6[] = "abcdef";          //不完全初始化

 数组的完全初始化是指:数组创建时在给定数字时将数组填满;而数组的非完全初始化是指:数组创建时在给定数字时填不满数组,则剩下的元素默认初始化为0;下面,我们来进行调试验证。

微信截图_20230911224607.png

     数组在创建的时候如果想不指定数组的确定大小就得初始化数组的元素个数根据初始化的内容来确定。

1.3 一维数组的使用

      对于数组的使用,我们之前介绍了一个操作符[ ],下标引用操作符他其实就是数组访问的操作符。下面,我们来看一下代码吧!

#include <stdio.h>
int main()
{
  int arr[10] = { 0 };//数组的不完全初始化
    //计算数组的元素个数
  int sz = sizeof(arr) / sizeof(arr[0]);
    //对数组内容赋值,数组是使用下标来访问的,下标从0开始,所以:
  int i = 0; //做下标
  for (i = 0; i < sz; i++)
  {
    arr[i] = i;
  }
    //输出内容数据
  for (i = 0; i < sz; i++)
  {
    printf("%d ", arr[i]);
  }
  printf("\n");
  return 0;
}

 在这里,小编我要提醒大家:要记住数组的长度怎样计算?但有一种情况大家不要使用这种方法进行计算(代码在下面)这种方式会在sizeof(arr)算的是整个数组的长度,而不是光输入的数组长度,其余情况倒是可以随便用。

const int N = 10;
int arr[100];
for(int i = 0; i < N; i++)
{
    ...
}
int sz = sizeof(arr)/sizeof(arr[0]);

总结:


1)数组是使用下标来访问的,下标是从0开始的,依次增加1;


2)数组的大小可以通过计算得到的。


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

      当讨论完一维数组的使用后,接下来我们来讨论数组在内存中的存储。 下面,我们来看一下代码吧!(在这里提一嘴,打印地址的占位符是:%p;为了便于观察,我们将环境改为X86。)

#include <stdio.h>
int main()
{
  int arr[10] = { 0 };
  int sz = sizeof(arr) / sizeof(arr[0]);
  int i = 0;
  for (i = 0; i < sz; i++)
  {
    arr[i] = i;
  }
  for (i = 0; i < sz; i++)
  {
    printf("&arr[%d] = %p\n", i, &arr[i]);
  }
  printf("\n");
  return 0;
}

输出的结果如下:

微信截图_20230911224739.png

  仔细观察输出的结果,我们知道,随着数组下标的增长,元素的地址也是有规律的递增。由此可以得出结论:数组在内存中是连续存放的。(方便内存区使用数组)

微信截图_20230911224808.png

二、二维数组的创建与初始化


2.1 二维数组的创建

      在小编看来,二维数组与一维数组的不同在于:二维数组增加了行和列的关系接下来,看二维数组的创建。

//数组创建
int arr[3][4];
char arr[3][5];
double arr[2][4];

2.2 二维数组的初始化

小编记得二维数组的初始化要比一维数组的初始化要难,下面也有好几种情况:

int arr1[3][4] = { 1,2,3,4 };
int arr2[3][4] = { {1,2},{3,4} };//如果二维数组没有放满,则补0
int arr3[][4] = { {1,2},{5,4} };//二维数组如果有初始化,行可以省略,列不能省略

下面在监视窗口中可以验证上述所说:

微信截图_20230911224903.png

为什么在二维数组初始化中可以有大括号呢?


我们可以将二维数组的每一行想像为一个一维数组。


2.3 二维数组的使用

      二维数组的使用也是通过下标的方式,在假想中,二维数组是一个矩阵,所以二维数组有自己的行号和列号,我们可以通过行号和列号进行使用一个元素,就如同坐标系一样去访问。下面举个例子:

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

微信截图_20230911224944.png

下面看代码:

int main()
{
  int arr[3][5] = { 0 };
  int i = 0;
  for (i = 0; i < 3; i++)
  {
    int j = 0;
    for (j = 0; j < 5; j++)
    {
      arr[i][j] = i * 4 + j;
    }
  }
  for (i = 0; i < 3; i++)
  {
    int j = 0;
    for (j = 0; j < 5; j++)
    {
      printf("%d ", arr[i][j]);
    }
    printf("\n");
  }
  return 0;
}

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

      在经历了上面对二维数组的学习后,大家可能会发现其实二维数组与一维数组基本类似。像一维数组一样,这里我们尝试打印二维数组的每一个元素地址。下面,我们来看一下代码吧!(在这里再提一嘴,打印地址的占位符是:%p;为了便于观察,我们将环境改为X86。)

int main()
{
  int arr[3][5] = { 0 };
  int i = 0;
  for (i = 0; i < 3; i++)
  {
    int j = 0;
    for (j = 0; j < 5; j++)
    {
      arr[i][j] = i * 4 + j;
    }
  }
  for (i = 0; i < 3; i++)
  {
    int j = 0;
    for (j = 0; j < 5; j++)
    {
      printf("&arr[%d][%d] = %p\n", i, j, &arr[i][j]);
    }
    printf("\n");
  }
  return 0;
}

输出的结果如下:

微信截图_20230911225043.png

通过结果我们可以分析得到,其实二维数组在内存中也是连续存储的,这里能看出二维数组是一维数组的数组,证实了这一点。

微信截图_20230911225107.png

三、数组越界

      数组的下标是有范围限制的;数组的下标规定是从0开始的,如果数组有N个元素,最后一个元素的下标就是N-1。所以数组的下标如果小于0,或者大于N-1,就是数组越界访问了,超出了数组合法空间的访问。二维数组的行和列也可能存在越界。


      C语言本身是不做数组下标的越界检查,编译器也不一定报错,但是编译器不报错,并不意味着程序就是正确的。  

微信截图_20230911225137.png

但如果有赋值操作的话,程序就会崩溃。

微信截图_20230911225155.png

所以程序员写代码时,最好自己做好越界的检查。


四、数组作为函数参数

      往往我们写代码的时候,会将数组作为参数传入函数,接下来我们借着举例子的时候,为大家介绍一下排序算法中最简单的排序——冒泡排序。


冒泡排序的算法思想:两两相邻的元素进行比较,将较大(较小)的沉入最下面。


4.1 冒泡排序函数的错误设计

我们接下来看代码:

void bubble_sort(int arr[])
{
    int sz = sizeof(arr) / sizeof(arr[0]);
    for (int i = sz - 1; i >= 0; i++)
    {
        for (int j = 0; j < i; j++)
        {
            if (arr[j] > arr[j + 1])
            {
                int temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;
            }
        }
    }
}
int main()
{
    int arr[] = { 3,5,7,8,0,2,1,4,6,9 };
    bubble_sort(arr);
    for (int i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
    {
        printf("%d ", arr[i]);
    }
    return 0;
}

这个代码出现问题,那我们来找一下问题:经过调试之后,可以看到bubble_sort函数内部的sz为2,而不是我们想要的值。难道数组作为函数参数的时候,不是把整个数组传递过去吗?

微信截图_20230911225301.png

4.2 数组名是什么?

为了解决上述问题,我们进行探究数组名的内容:看下面的代码:

微信截图_20230911225340.png

结论:数组名就是地址。通常来说,数组名是数组首元素的地址。

       如果数组名是首元素地址的话,那么我们之前学过计算数组的长度和这个情况不同。看代码:

微信截图_20230911225357.png

所以数组名表示的并不一定都是首元素地址,有两个例外:


sizeof(数组名),计算整个数组的大小,sizeof内部单独放入一个数组名,数组名表示整个数组,单位是字节。

&数组名,取出的是数组的地址。&数组名。数组名表示整个数组。

4.3 冒泡排序函数的正确设计

      当数组传参的时候,实际上只是把数组的首元素的地址传递过去了。所以即使在函数参数部分写成数组的形式:int arr[ ] 表示的依然是一个指针: int* arr。那么这也回答了4.1中的问题:sizeof(arr)的结果是8。


那我们该怎么进行更改函数设计呢?下面看代码:


void bubble_sort(int arr[], int sz)//参数接受元素个数
{
    //代码同上面函数
}
int main()
{
    int arr[] = { 3,5,7,8,0,2,1,4,6,9 };
    int sz = sizeof(arr) / sizeof(arr[0]);
    bubble_sort(arr, sz);
    for (int i = 0; i < sz; i++)
    {
        printf("%d ", arr[i]);
    }
    return 0;
}

总结

      在这一部分,小编详细地编写了有关数组的一篇博客。希望大家看完以后,进行点评,谢谢大家!

相关文章
|
18天前
|
存储 编译器 C语言
C语言之数组
C语言之数组
24 0
|
19天前
|
C语言
C语言:数组和指针笔试题解析(包括一些容易混淆的指针题目)
C语言:数组和指针笔试题解析(包括一些容易混淆的指针题目)
|
8天前
|
机器学习/深度学习 C语言
C语言三维数组的创建
该代码片段展示了如何在C语言中创建一个动态的3D数组。由于`n`在编译时未知,不能直接声明为`int f[n][n][n]`。正确的方法是使用`malloc`进行动态内存分配。首先分配`n`个`int **`,然后对每一层分配`n`个`int *`,最后每个元素分配`n`个`int`。之后可以使用这个3D数组,并在完成后正确释放内存。
11 2
|
19天前
|
C语言
数组深入剖析(C语言基础入门)
数组深入剖析(C语言基础入门)
|
21天前
|
存储 C语言
C语言中字符串的引用与数组元素操作
C语言中字符串的引用与数组元素操作
21 0
|
1月前
|
存储 人工智能 程序员
爱上C语言:超详细讲解数组,学数组看这一篇就够了(数组篇)
爱上C语言:超详细讲解数组,学数组看这一篇就够了(数组篇)
|
1月前
|
编译器 程序员 C语言
【C语言】变长数组,二分查找和数组之间自动替换的实现
【C语言】变长数组,二分查找和数组之间自动替换的实现
|
1月前
|
存储 C语言
【C语言数组】创建、初始化、以及使用2
【C语言数组】创建、初始化、以及使用
|
1月前
|
存储 C语言
【C语言数组】创建、初始化、以及使用1
【C语言数组】创建、初始化、以及使用
|
2月前
|
存储 C语言 C++
C语言第八弹---一维数组
C语言第八弹---一维数组