初级C语言之【数组】(二)

简介: 初级C语言之【数组】(二)

2.3:二维数组的使用

二维数组中的每一个元素都有其对应的行号和列号,行号和列号都是从0开始的,故可以通过下标来使用二维数组

//通过下标访问数组的每一个元素并将其打印出来
int main()
{
  int arr[3][4] = { 1,2,3,4,5,6,7,8,9,10,11,12 };
  int i = 0;
  for (i = 0; i < 3; i++)
  {
    int j = 0;
    for (j = 0; j < 4; j++)
    {
      printf("%d ", arr[i][j]);
    }
    printf("\n");
  }
  return 0;
}

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

//打印二维数组中每一个元素的地址
int main()
{
  int arr[3][4] = { 1,2,3,4,5,6,7,8,9,10,11,12 };
  int i = 0;
  for (i = 0; i < 3; i++)
  {
    int j = 0;
    for (j = 0; j < 4; j++)
    {
      printf("&arr[%d][%d]=%p\n", i, j, &arr[i][j]);
    }
  }
  return 0;
}
//打印结果如下:
&arr[0][0]=00AFF860
&arr[0][1]=00AFF864
&arr[0][2]=00AFF868
&arr[0][3]=00AFF86C
&arr[1][0]=00AFF870
&arr[1][1]=00AFF874
&arr[1][2]=00AFF878
&arr[1][3]=00AFF87C
&arr[2][0]=00AFF880
&arr[2][1]=00AFF884
&arr[2][2]=00AFF888
&arr[2][3]=00AFF88C

可见二维数组中每相邻两个元素的地址是连续的

468b18e37ee741e89e190fb16dce6dc2.png

//因为二维数组中元素的存储是连续的,因此我们也可通过下面这种方式来打印数组中的每一位元素
int main()
{
  int arr[3][4] = { 1,2,3,4,5,6,7,8,9,10,11,12 };
  int* p = &arr[0][0];
  int i = 0;
  for (i = 0; i < 12; i++)
  {
    printf("%d ", *(p + i));
  }
  printf("\n");
  return 0;
}

如果把二维数组的每一行都看成是一个以为数组,那么:

arr[0]可以看作是第一行元素的数组名

arr[1]可以看作是第二行元素的数组名

arr[2]可以看作是第三行元素的数组名

2.4.1:二维数组行数和列数的计算

//计算二维数组由多少行
sizeof(arr)/sizeof(arr[0])//二维数组总的字节数除以每一行的字节数就是行数
//计算二维数组的列数
sizeof(arr[0])/sizeof(arr[0][0])//二维数组一行的字节数除以每一个元素的字节数就是列数

三:数组越界

int arr[10];//下标的取值范围是0~9,超出这个范围就是越界

数组的下标是有范围限制的。

数组的下标规定是从0开始的,如果数组有n个元素,最后一个元素的下标就是n-1。所以数组的下标如果小于0,或者大于n-1,就是数组越界访问了,超出了数组合法空间的访问。C语言本身是不做数组下标的越界检查,编译器也不一定报错,但是编译器不报错,并不意味着程序就是正确的。

int main()
{
  int arr[10] = { 0 };
  //下标的取值范围是0~9,超出这个范围就是越界
  int i = 0;
  for (i = 0; i <= 10; i++)
  {
    printf("%d ", arr[i]);
  }
}
//结果:
0 0 0 0 0 0 0 0 0 0 -858993460
//通过最后一个打印结果(随机值)可以看出,发生了数组越界
int main()
{
  int arr[10] = { 0 };
  //下标的取值范围是0~9,超出这个范围就是越界
  arr[10] = 11;//数组已越界
}

执行上面这段代码,会报出如下的数组越界错误

/


46692eb34c314e9492c5c11688c713ff.png

四:数组作为函数的参数

4.1:冒泡排序函数

冒泡排序的思想是:从左到右,相邻的两个元素进行比较,并且进行适当的交换。每次比较一轮,就会找到序列中最大的或者最小的数,这个数就会来到序列的最右边。

以从小到大排序为例:从左到右,让相邻的两个元素进行比较,如果左边的元素大于右边的元素就将这两个元素进行交换,从左到右,两两相邻的元素比较结束,最大的那个数就会来到序列的最右边,第一轮比较就结束了。接着进行第二轮,从左边第一个数一直比较到倒数第二个数(最后一个数就是序列里面最大的,无需再进行比较),如下图的序列:9 8 7 6 5 4 3 2 1.第一轮冒泡共进行了9次两两相互比较,第一轮冒泡排序结束后序列里面最大的数字9的位置就确定了下来,即在序列的最后面(因为是从小到大排序)。接着进行第二轮冒泡排序,找出9前面的序列:8 7 6 5 4 3 2 1 0里面的最大值让他来到9的前面,第二轮冒泡排序共进行了8次两两比较,最终序列:8 7 6 5 4 3 2 1 0中的最大值8来到了他应该出现的位置,即在9的前面。第三轮冒泡会让7去到他应该去到的位置,第四轮冒泡会让6去到他应该去到的位置。大家猜猜这十个数字要进行几轮冒泡呢?既然一轮搞定一个数字,那十个数字不就需要十轮嘛?答案是需要九轮,因为十个数经过九轮冒泡,那一定有九个数都去到了他们应该去的位置,既然九个数的位置都确定下来了,那最后剩的那个数肯定就在他应该出现的位置上,就不用再进行第十轮冒泡了。因此,如果有n个数就要进行n-1轮冒泡才能得到一个有顺序的序列。


bc4dafc42cb34b3aba810243417bea7e.png

void 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++)//第一轮要进行九次两两比较,第二轮要进行八次两两比较,所以这里的判断条件是sz-1-i
    {
      //每一轮冒泡要进行多少次两两比较
      if (arr[j] > arr[j + 1])
      {
        int tmp = arr[j];
        arr[j] = arr[j + 1];
        arr[j + 1] = tmp;
      }
    }
  }
}
int main()
{
  int arr[] = { 3,1,5,4,7,6,9,8,0,2 };//一共十个数
  //写一个函数对数组排序
  int sz = sizeof(arr) / sizeof(arr[0]);//计算数组中元素的个数,数组总的字节数除以每一个元素的字节数就是数组中元素的个数
  Sort(arr, sz);
  int i = 0;
  //将排完序的数组打印出来
  for (i = 0; i < sz; i++)
  {
    printf("%d ", arr[i]);
  }
  return 0;
}

注意:计算数组元素个数的这段代码“int sz = sizeof(arr) / sizeof(arr[0]);”一定得放在主函数里面,将计算出的个数sz传到Sort函数里面,切不可在Sort函数里面进行计算。为什么只能这样呢?这就是我们接下来要讨论的问题-数组名是什么了?

4.2:数组名是什么?

数组名是首元素的地址有两个例外

例外1:sizeof(数组名),这里的数组名是表示整个数组,计算的是整个数组的大小,单位是字节

例外2:&数组名,这里的数组名也表示整个数组,&数组名取出的是数组的地址

//通过这段代码可证明:数组名就是首元素的地址
int main()
{
  int arr[] = { 1,2,3,4,5,6,7,8,9 };
  printf("%p\n", arr);//打印数组名
  printf("%p\n", &arr[0]);//打印首元素地址
  return 0;
}
//执行结果:
004FFEAC
004FFEAC
int main()
{
  int arr[] = { 1,2,3,4,5,6,7,8,9 };
  printf("%p\n", arr);//打印数组名
  printf("%p\n", &arr[0]);//打印首元素地址
  printf("%p\n", &arr);//这里&arr取的是数组的地址
  return 0;
}
//执行结果:
00EFFBF8
00EFFBF8
00EFFBF8

虽然上面的这段代码打印出来的结果相同,但是意义却不相同,前两个仅仅代表首元素的地址,而第三个代表的是数组的地址

通过下面的代码便可得以验证

int main()
{
  int arr[] = { 1,2,3,4,5,6,7,8,9 };
  printf("%p\n", arr);
  printf("%p\n", arr+1);
  printf("%p\n", &arr[0]);
  printf("%p\n", &arr[0]+1);
  printf("%p\n", &arr);
  printf("%p\n", &arr+1);
  return 0;
}
//执行结果:
006FFB5C
006FFB60
006FFB5C
006FFB60
006FFB5C
006FFB80

可见arr+1和&arr[0]+1的结果一样,都是跳过了四个字节,而&arr+1则是跳过了四十个字节


58340870eb0a4e66819c920303c4468d.png

这下我们就知道了:数组作为函数的参数,传递的是首元素的地址,既然是地址那形参就应该用一个指针来接收。此时我们再看这段代码“int sz = sizeof(arr) / sizeof(arr[0]);”如果放在函数内部,arr此时仅仅是一个整型指针,而一个指针的字节数在×86的环境下就是四个字节,所以sizeof(arr)的结果就是4而不是整个数组所占的字节数,而sizeof(arr[0])也是4,最终相除的结果就是1了,并不是我们想得到的数组元素个数,因此求数组的元素个数应该在主函数中求

//数组作为函数的参数
void Sort(int* arr)//形参是对应的指针类型
{
}
int main()
{
  int arr[] = { 1,2,3 };
  Sort(arr);//传的是首元素的地址
  return 0;
}

形参得到了数组的首元素地址,接着顺藤摸瓜就能找到数组中的其他元素

到这里,数组的有关分享就结束啦,喜欢的话可以点赞、评论和收藏哟!


9cef4f5a4f8d409ab885c911f3600a47.png


目录
相关文章
|
14天前
|
传感器 算法 安全
【C语言】两个数组比较详解
比较两个数组在C语言中有多种实现方法,选择合适的方法取决于具体的应用场景和性能要求。从逐元素比较到使用`memcmp`函数,再到指针优化,每种方法都有其优点和适用范围。在嵌入式系统中,考虑性能和资源限制尤为重要。通过合理选择和优化,可以有效提高程序的运行效率和可靠性。
56 6
|
18天前
|
存储 缓存 算法
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式,强调了合理选择数据结构的重要性,并通过案例分析展示了其在实际项目中的应用,旨在帮助读者提升编程能力。
41 5
|
18天前
|
存储 程序员 编译器
C 语言数组与指针的深度剖析与应用
在C语言中,数组与指针是核心概念,二者既独立又紧密相连。数组是在连续内存中存储相同类型数据的结构,而指针则存储内存地址,二者结合可在数据处理、函数传参等方面发挥巨大作用。掌握它们的特性和关系,对于优化程序性能、灵活处理数据结构至关重要。
|
22天前
|
存储 C语言 计算机视觉
在C语言中指针数组和数组指针在动态内存分配中的应用
在C语言中,指针数组和数组指针均可用于动态内存分配。指针数组是数组的每个元素都是指针,可用于指向多个动态分配的内存块;数组指针则指向一个数组,可动态分配和管理大型数据结构。两者结合使用,灵活高效地管理内存。
|
22天前
|
存储 NoSQL 编译器
C 语言中指针数组与数组指针的辨析与应用
在C语言中,指针数组和数组指针是两个容易混淆但用途不同的概念。指针数组是一个数组,其元素是指针类型;而数组指针是指向数组的指针。两者在声明、使用及内存布局上各有特点,正确理解它们有助于更高效地编程。
|
25天前
|
存储 人工智能 算法
数据结构实验之C 语言的函数数组指针结构体知识
本实验旨在复习C语言中的函数、数组、指针、结构体与共用体等核心概念,并通过具体编程任务加深理解。任务包括输出100以内所有素数、逆序排列一维数组、查找二维数组中的鞍点、利用指针输出二维数组元素,以及使用结构体和共用体处理教师与学生信息。每个任务不仅强化了基本语法的应用,还涉及到了算法逻辑的设计与优化。实验结果显示,学生能够有效掌握并运用这些知识完成指定任务。
44 4
|
2月前
|
存储 编译器 C语言
【c语言】数组
本文介绍了数组的基本概念及一维和二维数组的创建、初始化、使用方法及其在内存中的存储形式。一维数组通过下标访问元素,支持初始化和动态输入输出。二维数组则通过行和列的下标访问元素,同样支持初始化和动态输入输出。此外,还简要介绍了C99标准中的变长数组,允许在运行时根据变量创建数组,但不能初始化。
47 6
|
2月前
|
存储 人工智能 BI
C语言:数组的分类
C语言中的数组分为一维数组、多维数组和字符串数组。一维数组是最基本的形式,用于存储一系列相同类型的元素;多维数组则可以看作是一维数组的数组,常用于矩阵运算等场景;字符串数组则是以字符为元素的一维数组,专门用于处理文本数据。
|
2月前
|
存储 算法 C语言
C语言:什么是指针数组,它有什么用
指针数组是C语言中一种特殊的数据结构,每个元素都是一个指针。它用于存储多个内存地址,方便对多个变量或数组进行操作,常用于字符串处理、动态内存分配等场景。
|
2月前
|
存储 C语言
C语言:一维数组的不初始化、部分初始化、完全初始化的不同点
C语言中一维数组的初始化有三种情况:不初始化时,数组元素的值是随机的;部分初始化时,未指定的元素会被自动赋值为0;完全初始化时,所有元素都被赋予了初始值。