首页 > 其他分享 >第5节:初识数组

第5节:初识数组

时间:2024-10-20 19:16:56浏览次数:8  
标签:初始化 arr int 元素 初识 数组 left

5. 初识数组

1. 数组的概念

数组(Array)是C语言中用于存储相同类型数据的集合。数组中所有元素的类型相同,并且它们在内存中是连续存储的。数组的大小在定义时必须指定,并且一旦定义,大小就不能更改。

  • 数组的索引是从0开始的,也就是说,第一个元素的索引为0,第二个元素的索引为1,以此类推。
  • 数组可以是任何数据类型,比如intfloatchar等。
  • 数组中存放的是1个或者多个数据,但是数组元素个数不能为0。

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

2.1 数组创建

数组创建时,需要声明数组的类型、名称以及数组的大小(元素个数)。数组大小必须是一个正整数。

语法

数据类型 数组名[数组大小];
  • 数据类型:指定数组元素的类型,如int、char、float等。
    • 决定了每个数组元素占用的内存大小
    • 影响数组可以存储的值的范围和精度
  • 数组名:用于标识和访问数组的唯一标识符。
    • 遵循C语言变量命名规则
    • 通常选择有意义的名称,反映数组用途
  • 数组大小:方括号[]内指定数组的元素个数。
    • 必须是正整数或常量表达式
    • 决定了数组占用的总内存空间

例如:

int arr[5];  // 创建一个包含5个整数的数组

这里,arr是一个可以存储5个整数的数组,数组大小为5。因此,数组下标从arr[0]arr[4]。未被初始化的数组元素通常会包含垃圾值(未定义的值)。

2.2 数组的初始化

数组的初始化是指在数组声明时为数组元素分配初始值。可以在声明数组的同时,通过花括号{}提供初始值。

2.2.1 不完全初始化

在不完全初始化的情况下,我们可以只为部分数组元素赋值,剩余的元素将会被自动初始化为零。

示例

int arr[5] = {1, 2};  // 只有前两个元素被初始化,其他元素自动初始化为 0

这个数组中的元素如下:

  • arr[0] = 1
  • arr[1] = 2
  • arr[2] = 0
  • arr[3] = 0
  • arr[4] = 0

注意,只有在静态或全局数组中,未被初始化的元素才会自动初始化为零。在局部数组中,未初始化的元素通常是垃圾值,除非手动进行初始化。

2.2.2 完全初始化

完全初始化是指为数组中的每个元素都显式地提供一个初始值。如果数组大小与初始化列表中的元素数量相同,数组将被完全初始化。

示例

int arr[5] = {1, 2, 3, 4, 5};  // 为每个元素指定了初始值

这个数组的初始化结果为:

  • arr[0] = 1
  • arr[1] = 2
  • arr[2] = 3
  • arr[3] = 4
  • arr[4] = 5

在这种情况下,所有数组元素都已经被明确初始化。

2.2.3 数组的类型

数组在C语言中被视为一种复合数据类型。每个数组都有其特定的类型,这个类型由两个主要因素决定:元素的数据类型和数组的大小。理解数组的类型对于正确使用数组和进行数组操作至关重要。

数组类型的组成

数组的类型由以下两部分组成:

  • 元素类型:指定数组中每个元素的数据类型(如int、char、float等)
  • 数组大小:指定数组中元素的数量

数组的类型可以通过去掉数组声明中的数组名来得到。例如:

int arr1[10];
int arr2[12];
char ch[5];

在这些声明中:

  • arr1的类型是int [10],表示一个包含10个整型元素的数组
  • arr2的类型是int [12],表示一个包含12个整型元素的数组
  • ch的类型是char [5],表示一个包含5个字符的数组

数组类型的重要性

理解数组类型的概念很重要,原因如下:

  • 内存分配:数组类型决定了数组在内存中占用的总空间大小
  • 指针运算:在进行指针运算时,编译器需要知道数组的类型来正确计算偏移量
  • 函数参数:当数组作为函数参数传递时,了解数组类型可以帮助正确声明函数参数
  • 类型检查:编译器使用数组类型信息来执行类型检查,确保操作的合法性

数组类型和数组名

值得注意的是,数组名本身就是一个指向数组第一个元素的指针。例如,对于int arr[10]arr的类型是int *,但它指向的是整个数组int [10]。这种微妙的区别在进行指针运算和数组传递时特别重要。

总之,理解数组类型不仅有助于正确声明和使用数组,在学习指针之后还能帮助我们更好地理解C语言中数组和指针的关系。


3. 一维数组的使用

3.1 数组下标

C语言规定数组是有下标的。C语言中的数组下标是从0开始的,这意味着第一个元素的下标是0,最后一个元素的下标是数组大小 - 1

示例

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

在这里插入图片描述

这里,arr[0]表示数组的第一个元素,输出结果为1。

在C语言中数组的访问提供了一个操作符[] ,这个操作符叫:下标引用操作符

有了下标访问操作符,我们就可以轻松的访问到数组的元素了,比如我们访问下标为7的元素,我们就可以使用arr[7] ,想要访问下标是3的元素,就可以使用arr[3] ,如下代码:

int arr[10] = {1,2,3,4,5,6,7,8,9,10};
printf("%d", arr[7]);  // 输出数组的第八个元素,结果为8
printf("%d", arr[3]);  // 输出数组的第四个元素,结果为4

3.2 数组元素的打印

我们可以通过循环遍历数组来打印每个元素,通常使用for循环。

示例

#include <stdio.h>

int main(int argc, char const *argv[])
{
    int arr[5] = {1, 2, 3, 4, 5};
    for (int i = 0; i < 5; i++) {
        printf("%d ", arr[i]);  // 打印数组中的每个元素
    }
    return 0;
}

输出结果:

在这里插入图片描述

3.3 数组的输入

数组的元素可以通过用户输入来获取。我们可以使用循环和scanf函数从键盘读取数组的元素值。

示例

#include <stdio.h>

int main(int argc, char const *argv[])
{
    int arr[6];
    for(int i = 0; i < 6; i++) {
        scanf("%d", &arr[i]);  // 输入数组的每个元素
    }
    return 0;
}

在这里插入图片描述

在这里插入图片描述

这里,通过scanf函数读取用户输入的5个整数并将其存储到数组中。


4. 一维数组在内存中的存储

数组在内存中是以连续的方式存储的。数组的每个元素占用相同的数据空间。假设数组的第一个元素的地址是1000,且每个int类型占用4个字节,那么第二个元素将存储在地址1004,第三个元素存储在地址1008,依此类推。

在这里插入图片描述

例如:

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

如果arr[0]存储在地址1000,那么:

  • arr[0] 的地址是 1000,值是 1
  • arr[1] 的地址是 1004,值是 2
  • arr[2] 的地址是 1008,值是 3
  • ······

在这里插入图片描述

后面通过指针的学习,我们可以通过指针,也可以访问数组中的元素。数组名本质上是指向第一个元素的指针。例如:

int *ptr = arr;
printf("%d", *ptr);  // 输出 arr[0] 的值,即 1

5. sizeof 计算数组元素个数

在C语言中,sizeof运算符可以用来计算数组占用的总字节数。如果我们想知道数组中的元素个数,可以使用sizeof来计算整个数组的大小,再除以单个元素的大小。

详细解释

  • sizeof(arr):返回整个数组占用的总字节数。在这个例子中,如果int占4字节,那么sizeof(arr)将返回20(5 * 4字节)。
  • sizeof(arr[0]):返回数组中单个元素的字节大小。在这个例子中,它将返回4(假设int占4字节)。
  • **相除操作:**20 / 4 = 5,正确计算出数组元素个数。

示例

int arr[] = {1, 2, 3, 4, 5};
int count = sizeof(arr) / sizeof(arr[0]);

在上面的例子中:

  • sizeof(arr) 返回整个数组的大小,以字节为单位。
  • sizeof(arr[0]) 返回数组中单个元素的大小。 因此,size 将会得到数组中元素的个数,这里为5。

优点

  • 适用性广:这种方法可以用于任何类型的数组,包括intfloatchar等。
  • 自动适应:当数组大小改变时,不需要手动更新代码。
  • 编译时计算:sizeof是在编译时计算的,不会影响运行时性能。

6. 二维数组的创建

6.1 二维数组的概念

二维数组可以被视为数组的数组,即一个矩阵。它由行和列组成,每个元素由两个下标来表示,第一个表示行,第二个表示列。二维数组在内存中也是连续存储的。

在这里插入图片描述

6.2 二维数组的创建

语法

数据类型 数组名[行数][列数];

创建二维数组时,必须同时指定行数和列数。

示例

int matrix[3][4];  // 创建一个包含3行4列的二维数组

解释:上述代码中出现的信息

  • 3表示数组有3行

  • 4表示每一行有4个元素

  • int 表示数组的每个元素是整型类型

  • matrix是数组名,可以根据自己的需要指定名字

  • 这个二维数组可以存储3*4=12个整数。


7. 二维数组的初始化

7.1 不完全初始化

与一维数组类似,二维数组的初始化也可以是不完全的,未初始化的元素会自动被设置为零。

示例

int arr1[3][5] = {1,2}; // 仅初始化了部分元素
int arr2[3][5] = {0};

未初始化的元素将会自动设置为0。例如:

  • arr1[0][0] = 1

  • arr1[0][1] = 2

  • arr1[0][2] = 0

  • arr1[1][0] = 0

  • arr1[1][1] = 0

  • arr1[1][2] = 0

  • arr1[2][0] = 0

  • ······

  • arr2[0][0] = 0

  • arr2[0][1] = 0

  • arr2[0][2] = 0

  • arr2[1][0] = 0

  • arr2[1][1] = 0

  • arr2[1][2] = 0

  • arr2[2][0] = 0

  • ······

    在这里插入图片描述

7.2 完全初始化

二维数组的完全初始化是指为数组的每个元素都提供初始值。

示例

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

该数组的元素初始化为:

  • arr3[0][0] = 1

  • arr3[0][1] = 2

  • arr3[0][2]= 3

  • arr3[0][3] = 4

  • ······

    在这里插入图片描述

7.3 按照行初始化

二维数组也可以按行初始化,通过嵌套花括号。

示例

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

在这里插入图片描述

7.4 初始化时省略行,但不能省略列

在声明二维数组时,可以省略行数,让编译器根据初始化元素自动推导出行数,但列数必须指定。

示例

int arr5[][5] = {1,2,3};
int arr6[][5] = {1,2,3,4,5,6,7};
int arr7[][5] = {{1,2}, {3,4}, {5,6}};

在这里插入图片描述


8. 二维数组的使用

8.1 二维数组的下标

其实二维数组访问也是使用下标的形式的,二维数组的元素通过两个下标来访问,第一个下标表示行,第二个下标表示列。例如:

int arr[3][5] = {1,2,3,4,5, 2,3,4,5,6, 3,4,5,6,7};
printf("%d", arr[0][1]);  // 输出2

在这里插入图片描述

图中最左侧绿色的数字表示行号,第一行绿色的数字表示列号,都是从0开始的,比如,我们说:第2行,第3列,快速就能定位出4。这里,arr[0][1] 表示第1行第2列的元素,值为2

8.2 二维数组的输入和输出

二维数组的输入和输出通常通过嵌套循环来实现。

示例

int matrix[2][2];
for(int i = 0; i < 2; i++) {
    for(int j = 0; j < 2; j++) {
        scanf("%d", &matrix[i][j]);  // 输入每个元素
    }
}

for(int i = 0; i < 2; i++) {
    for(int j = 0; j < 2; j++) {
        printf("%d ", matrix[i][j]);  // 打印每个元素
    }
    printf("\n");
}

在这里插入图片描述


9. 二维数组在内存中的存储

二维数组在内存中是以行优先顺序(Row-major order)存储的,这意味着数组按行存储,第一个行的所有元素存储在连续的内存位置,接下来是第二行的元素,依此类推。

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

在这里插入图片描述

从输出的结果来看,每一行内部的每个元素都是相邻的,地址之间相差4个字节,跨行位置处的两个元素(如:arr[0][4]arr[1][0])之间也是差4个字节,所以二维数组中的每个元素都是连续存放的

在这里插入图片描述


10. C99中的变长数组

在C99标准之前,C语言在创建数组的时候,数组大小的指定只能使用常量、常量表达式,或者如果我们初始化数据的话,可以省略数组大小。

C99标准引入了变长数组(variable-length array,简称 VLA),允许数组的大小在运行时动态确定。例如:

int n;
scanf("%d", &n);  // 输入数组大小
int arr[n];  // 创建一个大小为n的数组

在这里,数组arr 就是变长数组,数组arr的大小取决于用户在运行时输入的n值。这使得数组更加灵活。

特点:

  • 只能在函数内部声明,不能在全局范围或结构体中声明
  • 不能用staticextern修饰。
  • 变长数组的根本特征,就是数组长度只有运行时才能确定,所以变长数组不能初始化
  • 可以作为函数参数。

优点:

  • 提高了代码的灵活性,可以根据运行时的需求分配内存。
  • 避免了使用动态内存分配(如malloc)的复杂性。

缺点:

  • 可能导致栈溢出,特别是当数组大小很大时。
  • 不是所有编译器都支持。比如在VS2022上,虽然支持大部分C99的语法,但是没有支持C99中的变长数组。

11. 数组练习

练习1:多个字符从两端移动,向中间汇聚

这个练习目的是演示如何通过操作字符数组来实现字符从两端向中间移动的效果。

以下是详细的代码解释:

#include <stdio.h>
#include <string.h>
#include <windows.h>  // 为了使用 Sleep 函数

int main()
{
    char arr1[] = "welcome to bit...";  // 目标字符串
    char arr2[] = "#################";  // 初始显示的字符串,与 arr1 长度相同
    int left = 0;  // 左侧起始索引
    int right = strlen(arr1) - 1;  // 右侧起始索引(字符串长度减1)

    printf("%s\\n", arr2);  // 打印初始状态

    while (left <= right)  // 当左右索引未相遇时继续循环
    {
        Sleep(1000);  // 暂停1秒,增加视觉效果
        arr2[left] = arr1[left];  // 从左侧替换字符
        arr2[right] = arr1[right];  // 从右侧替换字符
        left++;  // 左索引向右移动
        right--;  // 右索引向左移动
        printf("%s\\n", arr2);  // 打印当前状态
    }

    return 0;
}

代码解析:

  • 我们定义了两个字符数组:arr1 存储目标字符串,arr2 初始化为全#号
  • 使用 left 和 right 两个索引分别从字符串的两端开始移动
  • 在每次循环中,我们将 arr1 的对应字符复制到 arr2 中,然后移动索引
  • 使用 Sleep 函数增加了视觉效果,让字符替换的过程更容易观察
  • 循环继续直到 left 和 right 相遇,此时整个字符串已经完全替换

练习2:二分查找

在一个升序的数组中查找指定的数字n,很容易想到的方法就是遍历数组,但是这种方法效率比较低。

二分查找是一种高效的查找算法,适用于已排序的数组。它的基本思想是将查找区间不断二分,每次都与区间的中间元素比较,从而快速缩小查找范围。

下面是二分查找算法的详细实现和解释:

#include &lt;stdio.h&gt;

int main()
{
    int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};  // 已排序的数组
    int left = 0;  // 左边界
    int right = sizeof(arr) / sizeof(arr[0]) - 1;  // 右边界
    int key = 7;  // 要查找的目标值
    int mid;  // 中间元素的索引
    int found = 0;  // 标记是否找到目标值

    while (left <= right)
    {
        mid = left + (right - left) / 2;  // 计算中间索引,避免整数溢出
        
        if (arr[mid] > key)
        {
            right = mid - 1;  // 目标在左半部分,缩小右边界
        }
        else if (arr[mid] < key)
        {
            left = mid + 1;  // 目标在右半部分,缩小左边界
        }
        else
        {
            found = 1;  // 找到目标值
            break;
        }
    }

    if (found)
        printf("找到了,下标是 %d\\n", mid);
    else
        printf("找不到\\n");

    return 0;
}

代码解析:

  • 我们使用 leftright 两个变量来表示当前搜索区间的边界
  • 在每次循环中,我们计算中间索引 mid,并将 arr[mid] 与目标值 key 比较
  • 根据比较结果,我们调整搜索区间:如果 key 小于 mid 处的值,搜索左半部分;如果大于,搜索右半部分
  • 如果找到目标值,我们设置 found 标志并退出循环
  • 循环结束后,我们根据 found 标志来输出结果

注意:计算 mid 时使用 mid = left + (right - left) / 2 而不是 (left + right) / 2,这是为了避免在 leftright 都很大时可能发生的整数溢出问题。

二分查找的时间复杂度是 O(log n),这意味着它比简单的线性搜索(时间复杂度 O(n))要快得多,尤其是对于大型数组。

—完—

标签:初始化,arr,int,元素,初识,数组,left
From: https://blog.csdn.net/weixin_44643253/article/details/143088245

相关文章

  • 鸿蒙开发 四十九 数组
    1、数组的创建方式数组:接口范型的形式提供,接口的源码是:interfaceArray<T>,实际开发中数组用得非常多,创建数组的方式有:1、语法格式:let数组名:Array<数组类型>=[],实例:letarr1:Array<IObject>=[],这里申明IObject的数组,2,new关键字创建数组,语法格式:let数组名字:Arra......
  • 六,数组笔记及相关练习题大全
    Java编程基础:数组详解在Java编程中,数组是一块连续固定大小的内存空间,用于存储相同类型的多个元素。数组提供了索引的概念,允许通过索引访问和操作数组中的元素。本文将详细探讨Java中数组的定义、初始化、使用和示例。数组的定义和初始化数组在定义时需要指定数据类型和数组名。......
  • 轮转数组——力扣189题
    力扣189题轮转数组,本身很简单,但是反复提交很多次都没有成功,实在是让人难以启齿,分析应该还是概念不清晰导致的,自以为理解得很清楚,实际确实模棱两可。把数组后几个移动到前面的位置上去,按照我最开始的想法,不过就是切片,把后半部分切下来,形成的两个半个数组再加起来不就是了吗?在py......
  • 提取并排序数组中的偶数
    题目:提取并排序数组中的偶数题目描述:给定一个整数n和一个包含n个整数的数组,编写一个程序提取数组中的所有偶数,并按升序排序后输出。输入格式:第一行包含一个整数 n (1≤ n ≤100,000),表示数组的元素个数。第二行包含 n 个整数,表示数组中的元素。每个整数的绝对......
  • 第7讲:数组
    文章目录1.数组的概念2.⼀维数组的创建和初始化3.一维数组的使用4.一维数组在内存中的存储6.⼆维数组的创建7.二维数组的初始化8.二维数组的使用9.二维数组在内存中的存储10.C99中的变长数组11.数组练习1.数组的概念2.⼀维数组的创建和初始化3.⼀维数......
  • 树状数组——原理详解
    前言这两天在网上学树状数组,但是发现网上关于树状数组的解释大都对初学者不太友善,原理讲解部分并不是很容易理解,所以写了一篇树状数组,顺便帮自己巩固一下。一、什么是树状数组1.概念:简单来说,这是一种数据结构。顾名思义,它通过树的结构来对数组进行高效操作,一般用于求数组前缀......
  • 二维数组1019
    publicclassPlaceDemo{publicstaticvoidmain(String[]args){//班级学生座位(二维数组)place();pace();}publicstaticvoidplace(){//静态初始化数组-----数据类型[][]数组名=new数据类型[]{元素1,元素2,元素3,··......
  • 数组练习1018
    假设班级有8名学生,录入8名学生的java成绩,成绩类型是小数,并输出平均分,最高分,最低分publicclassClassDemo2{publicstaticvoidmain(String[]args){//假设班级有8名学生,录入8名学生的java成绩,成绩类型是小数,并输出平均分,最高分,最低分studentSc......
  • 数组与字符串
    数组一维数组构造的数据类型之一,由若干数据类型相同的元素组成。其中数组名是地址常量不可修改,所以不能赋值操作,sizeof(数组名)求总内存空间。特点:数组不赋初始值,随机生成static修饰,默认位0部分赋值,其余默认为0//验证以上#include<iostream>usingnamespacestd;i......
  • 代码随想录算法训练营day20| 669. 修剪二叉搜索树 108.将有序数组转换为二叉搜索树
    学习资料:https://programmercarl.com/0669.修剪二叉搜索树.html#算法公开课学习记录:669.修剪二叉搜索树(直接在原函数上操作,要根据情况用root的左右子树递归,因为子树中有满足条件的;前序:根左右)点击查看代码#Definitionforabinarytreenode.#classTreeNode:#def_......