关于数组的基本要点

简介: 关于数组的基本要点



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

1.1 数组的创建

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

数组的创建方式:

type_t(是指数组的元素类型 arr_name   [const_n]是一个常量表达式,用来指定数组的大小

#include<stdio.h>
//C99之前数组只能是常量指定大小
//C99之后引用了变长数组概念,数组大小是可以用变量指定的。
int main()
{
  int arr[5];
  int arr2[3 + 2];
  char arr3[8];
  int n = 0;
  scanf("%d", &n);
  int arr4[n];
  return 0;
}

不过vs是不支持用变量来指定大小的。

1.2 数组的初始化

这里面有不完全初始化(剩余元素默认为0) 也有通过ascll码值代替字母  也可以不输入大小,通过内容自动判定。

不过关于字符数组需要注意一点:字符型不会自动放‘\0’,而字符串会。

1.3 一维数组的使用

数组通过下标来访问:

int main()
{
  int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
  int sz = sizeof(arr) / sizeof(arr[0]);
  int i = 0;
  for (i = 0; i < sz; i++)
  {
    printf("%2d ", arr[i]);
  }
  printf("\n");
  //如果想要倒序,也可以改变
  for (i = 0; i < sz; i++)
  {
    arr[i] = 10 - i;
    printf("%2d ", arr[i]);
  }
  return 0;
}

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

我们可以通过地址来说明:

int main()
{
  int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
  int sz = sizeof(arr) / sizeof(arr[0]);
  int i = 0;
  for (i = 0; i < sz; i++)
  {
    printf("&arr[%d]=%p\n", i, &arr[i]);
  }
  printf("\n");
  //如果想要倒序,也可以改变
  /*for (i = 0; i < sz; i++)
  {
    arr[i] = 10 - i;
    printf("%2d ", arr[i]);
  }*/
  return 0;
}

可以得出相邻地址之间都相差4个字节(整型元素是4个字节)是连续存放的并随下标增长,地址是低到高的。

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

2.1 二维数组的创建

2.2 二维数组的初始化

//对于二维数组,如果初始化了,对于行数是可以省略的,但是列不能省略
int main()
{
  int arr[3][5] = {0};//不完全初始化
  int arr1[3][5] = { {1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7} };
  int arr2[][10] = { {1,2},{2,3,4},{5,5,5} };
  return 0;
}

对于二维数组,如果初始化了,对于行数是可以省略的,但是列不能省略。

2.3 二维数组的使用

假想中的二维数组:

通过下标遍历简单访问二维数组

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

我们还可以改变数组:打印1-15

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

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

打印一下二维数组的地址:

int main()
{
  int arr[3][5] = { {1,2},{4,5},{6,7,8} };
  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],后面的[j]就是一维数组的大小。

三.数组越界

四.数组作为函数参数

冒泡排序基本思想:

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

10个数趟数是9趟,每一趟需要对比交换的次数都会减少,这里用sz-1-i来表示。

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

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

当我们把写好的代码封装到函数时发现出了问题。

没有排序了。

这里就涉及到了数组传参的问题了。

4.2 数组名是什么?

结论:数组名就是数组首元素的地址。

但是有两个意外:1.sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小,单位是字节。2.&数组名,这里的数组名表示整个数组,取出的是整个数组的地址。

通过地址可以发现,首个地址加1只是移动到下一个位,但&arr加1是直接跨越了整个数组的大小。

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

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

sort(arr)不是特殊的两种情况,就是数组的首元素地址,本质上形参用指针接受。所以上述形参中arr[]其实是个指针(当然形参也可以用int *arr),sizeof(arr)(等于4)/sizeof(arr[0])=1,sz=1.

在这种情况下我们可以把sz的求法放在main函数上,因为传的就是地址,接收的就只能是指针,不管怎么算大小都只能是4,所以只能在主函数求出,经过计算再传回去。

void sort(int arr[],int sz)
{
  int i = 0;
  int j = 0;
  //趟数
  for (i = 0; i < sz - 1; i++)
  {
    for (j = 0; j < sz - 1 - i; j++)
    {
      if (arr[j] > arr[j + 1])
      {
        int temp = arr[j];
        arr[j] = arr[j + 1];
        arr[j + 1] = temp;
      }
      else
      {
        break;
      }
    }
  }
}
int main()
{
  int arr[] = { 10,9,8,7,6,5,4,3,2,1 };
  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;
}

小疑惑:

也不用对首个元素地址感到奇怪,为什么不传整个数组呢?其实是因为后续有对数组进行遍历,只要知道首个元素的地址,就可以知道后面的地址了,进而遍历得到整个数组。

相关文章
|
3天前
|
云安全 人工智能 自然语言处理
|
7天前
|
人工智能 Java API
Java 正式进入 Agentic AI 时代:Spring AI Alibaba 1.1 发布背后的技术演进
Spring AI Alibaba 1.1 正式发布,提供极简方式构建企业级AI智能体。基于ReactAgent核心,支持多智能体协作、上下文工程与生产级管控,助力开发者快速打造可靠、可扩展的智能应用。
716 17
|
10天前
|
数据采集 人工智能 自然语言处理
Meta SAM3开源:让图像分割,听懂你的话
Meta发布并开源SAM 3,首个支持文本或视觉提示的统一图像视频分割模型,可精准分割“红色条纹伞”等开放词汇概念,覆盖400万独特概念,性能达人类水平75%–80%,推动视觉分割新突破。
750 57
Meta SAM3开源:让图像分割,听懂你的话
|
8天前
|
搜索推荐 编译器 Linux
一个可用于企业开发及通用跨平台的Makefile文件
一款适用于企业级开发的通用跨平台Makefile,支持C/C++混合编译、多目标输出(可执行文件、静态/动态库)、Release/Debug版本管理。配置简洁,仅需修改带`MF_CONFIGURE_`前缀的变量,支持脚本化配置与子Makefile管理,具备完善日志、错误提示和跨平台兼容性,附详细文档与示例,便于学习与集成。
329 116
|
10天前
|
机器学习/深度学习 人工智能 自然语言处理
AgentEvolver:让智能体系统学会「自我进化」
AgentEvolver 是一个自进化智能体系统,通过自我任务生成、经验导航与反思归因三大机制,推动AI从“被动执行”迈向“主动学习”。它显著提升强化学习效率,在更少参数下实现更强性能,助力智能体持续自我迭代。开源地址:https://github.com/modelscope/AgentEvolver
495 37
|
23天前
|
域名解析 人工智能
【实操攻略】手把手教学,免费领取.CN域名
即日起至2025年12月31日,购买万小智AI建站或云·企业官网,每单可免费领1个.CN域名首年!跟我了解领取攻略吧~
|
2天前
|
Rust 安全
掌握Rust文件读取(从零开始的IO操作指南)
本教程手把手教你用Rust读取文件,涵盖`read_to_string`一次性读取和`BufReader`逐行高效读取,适合初学者掌握安全、高效的Rust文件操作,助你轻松入门系统编程。
149 113