数组(1)

简介: 数组(1)


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

1.1 一维数组的创建

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

数组的创建方式:

type_t arr_name [const_n];

type_t 是指数组的元素类型

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

数组创建的实例:

int main()
{
  int arr1[10];
  int count = 10;
  int arr2[count];//常量表达式才可以
  //VS2019 VS2022 这样的IDE 不支持C99 中的变长数组
  
  //C99 标准之前 数组的大小只能是常量表达式
  //C99 标准中引入了:变长数组的概念,使得数组在创建的时候可以使用变量,但是这样的数组不能初始化
  return 0;
}
//gcc中就支持变长数组
#include <stdio.h>
int main()
{
  int n = 0;
  scanf("%d", &n);
  int arr[n];//局部的变量,这些局部的变量或者数组是存放在栈区,存放在栈区上的数组,如果不初始化的话,默认是随机值
  int i = 0;
  for (i = 0; i < n; i++)
  {
    arr[i] = i;
  }
  for (i = 0; i < n; i++)
  {
    printf("%d\n", arr[i]);
  }
  return 0;
}

1.2 一维数组的初始化

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

int main()
{
  //int arr1[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };//完全初始化
  //int arr2[10] = { 1, 2, 3 };//不完全初始化,剩余的元素默认都是0
  //int arr3[10] = { 0 };//不完全初始化,剩余的元素默认都是0
  //int arr4[] = { 0 };//省略数组的大小,数组必须初始化,数组的大小是根据初始化的内容来确定
  //int arr5[] = { 1, 2, 3 };
  //int arr6[];//err
  char arr1[] = "abc";
  char arr2[] = { 'a', 'b', 'c' };
  char arr3[] = { 'a', 98, 'c' };
  return 0;
}

2. 一维数组的使用

对于数组的使用我们之前介绍了一个操作符: [] (下标引用操作符),它其实就是数组访问的操作符。

#include <stdio.h>
int main()
{
  int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
  //              0  1  2  3  4  5  6  7  8  9
  //printf("%d\n", arr[5]);//[] 下标引用操作符
  //printf("%d\n", arr[0]);//[] 下标引用操作符
  int i = 0;
  int sz = sizeof(arr) / sizeof(arr[0]);//10
  for (i = 0; i < sz; i++)
  {
    printf("%d ", arr[i]);
  }
  return 0;
}
#include <stdio.h>
int main()
{
  int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
  //              0  1  2  3  4  5  6  7  8  9
  //printf("%d\n", arr[5]);//[] 下标引用操作符
  //printf("%d\n", arr[0]);//[] 下标引用操作符
  int i = 0;
  int sz = sizeof(arr) / sizeof(arr[0]);//10
  for (i = 0; i < sz; i += 2)
  {
    printf("%d ", arr[i]);
  }
  return 0;
}
#include <stdio.h>
int main()
{
  int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
  //              0  1  2  3  4  5  6  7  8  9
  //printf("%d\n", arr[5]);//[] 下标引用操作符
  //printf("%d\n", arr[0]);//[] 下标引用操作符
  int i = 0;
  int sz = sizeof(arr) / sizeof(arr[0]);//10
  for (i = sz - 1; i >= 0; i--)
  {
    printf("%d ", arr[i]);
  }
  return 0;
}
#include <stdio.h>
int main()
{
  int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
  //              0  1  2  3  4  5  6  7  8  9
  //printf("%d\n", arr[5]);//[] 下标引用操作符
  //printf("%d\n", arr[0]);//[] 下标引用操作符
  int i = 0;;
  int sz = sizeof(arr) / sizeof(arr[0]);//10
  for (i = 0; i < sz; i++)
  {
    scanf("%d", &arr[i]);
  }
  for (i = 0; i < sz; i++)
  {
    printf("%d ", arr[i]);
  }
  return 0;
}

总结:

  1. 数组是使用下标来访问的,下标是从0开始。
  2. 数组的大小可以通过计算得到。
#include <stdio.h>
int main()
{
  int arr[10] = { 0 };//10 * 4
  printf("%d\n", sizeof(arr));//40 - 计算的是数组的总大小,单位是字节
  printf("%d\n", sizeof(arr[0]));//4
  int sz = sizeof(arr) / sizeof(arr[0]);//计算数组元素个数的方法
  printf("%d\n", sz);
  return 0;
}

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

//%p  --  是用来打印地址的
#include <stdio.h>
int main()
{
  int arr[10] = { 1, 2, 3, 4, 5 };
  int sz = sizeof(arr) / sizeof(arr[0]);
  int i = 0;
  for (i = 0; i < sz; i++)
  {
    printf("&arr[%d] = %p\n", i, &arr[i]);
  }
  return 0;
}

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

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

4.1 二维数组的创建

int main()
{
  //数组的创建
  int arr[4][5];
  char ch[3][8];
  return 0;
}

4.2 二维数组的初始化

int main()
{
  //数组的初始化
  int arr[4][5] = { {1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7},{5,6,7,8,9} };
  int arr2[4][5] = { 1,2,3,4,5,2,3,4,5,6,3,4,5,6,7,5,6,7,8,9 };
  //二维数组即使初始化了的
  //行是可以省略的,但是列是不能省略的
  int arr3[][5] = { {1,2,3}, {2,3,4}, {3,4,5,6,7}, {5,6,7,8,9} };
  return 0;
}

5. 二维数组的使用

二维数组的使用也是通过下标的方式。

#include <stdio.h>
int main()
{
  int arr[4][5] = { {1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7},{5,6,7,8,9} };
  //printf("%d\n", arr[2][3]);
  int i = 0;
  //行号
  for (i = 0; i < 4; i++)
  {
    int j = 0;
    
    for (j = 0; j < 5; j++)
    {
      printf("%d ", arr[i][j]);//0 1 2 3 4
    }
    printf("\n");
  }
  return 0;
}

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

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

通过结果我们可以分析到,其实二维数组在内存中也是连续存储的

注:

//假想是:1 2 3 4 5
//       2 3 4 5 6
//       3 4 5 6 7
//       5 6 7 8 9
//实际上:连续存放的
//1. 二维数组是【一维数组】的数组
//                元素
//
//2. 可以这样理解: 类比一维数组 int arr[10]  arr[j];//0~9   -->  arr[0][j]中arr[0]是数组名 j是下标

7. 数组越界

  • 数组的下标是有范围限制的:数组的下标规定是从0开始的,如果数组有n个元素,最后一个元素的下标就是n-1;所以数组的下标如果小于0,或者大于n-1,就是数组越界访问了,超出了数组合法空间的访问。
  • C语言本身是不做数组下标的越界检查,编译器也不一定报错,但是编译器不报错,并不意味着程序就是正确的,所以程序员写代码时,最好自己做越界的检查。
#include <stdio.h>
int main()
{
  int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
  int i = 0;
  //0~10
  //越界访问
  //
  for (i = 0; i <= 10; i++)
  {
    printf("%d ", arr[i]);
  }
  return 0;
}

注: 二维数组的行和列也可能存在越界。

8. 数组作为函数参数

往往我们在写代码的时候,会将数组作为参数传给函数,比如:要实现一个冒泡排序(这里要讲算法思想)函数,将一个整形数组排序。


首先,来看一下不用函数的写法:

//输入10个整数,对这组数进行排序
//排序有很多的方法
//1. 冒泡排序
//2. 选择排序
//3. 插入排序
//4. 快速排序
// ....
#include <stdio.h>
int main()
{
  int arr[10] = { 0 };
  //输入
  int i = 0;
  int sz = sizeof(arr) / sizeof(arr[0]);
  for (i = 0; i < sz; i++)
  {
    scanf("%d", &arr[i]);
  }
  //冒泡排序 - 升序
  //冒泡排序 - 两两相邻的元素进行比较
  //一趟冒泡排序让一个值来到最终应该出现的位置上
  //1. 确定冒泡排序的趟数
  //2. 一趟冒泡排序的实现
  //趟数
  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;
      }
    }
  }
  for (i = 0; i < sz; i++)
  {
    printf("%d ", arr[i]);
  }
  return 0;
}

接着,我们运用函数来实现:

#include <stdio.h>
void bubble_sort(int arr[10], int sz)//这里的arr的本质是指针
{
  //           4       /        4      = 1
  //int sz = sizeof(arr) / sizeof(arr[0]);//sz=1
  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;
      }
    }
  }
}
//void bubble_sort(int *arr, int sz)//这里的arr的本质是指针
//{
//  //           4       /        4      = 1
//  //int sz = sizeof(arr) / sizeof(arr[0]);//sz=1
//  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[10] = { 0 };
  //输入
  int i = 0;
  int sz = sizeof(arr) / sizeof(arr[0]);
  for (i = 0; i < sz; i++)
  {
    scanf("%d", &arr[i]);
  }
  //排序 - 升序
  //arr作为数组进行了传参
  //数组传参,传递的是地址,传递的是首元素的地址
  bubble_sort(arr, sz);//让这个函数来完成数组arr中数据的排序
           //arr是数组首元素的地址
  
  //输出
  for (i = 0; i < sz; i++)
  {
    printf("%d ", arr[i]);
  }
  return 0;
}

此外,我们还可以对它进行一些优化:

//冒泡排序的优化
#include <stdio.h>
void bubble_sort(int* arr, int sz)//这里的arr的本质是指针
{
  //           4       /        4      = 1
  //int sz = sizeof(arr) / sizeof(arr[0]);//sz=1
  int i = 0;
  for (i = 0; i < sz - 1; i++)
  {
    //每一趟开始前就假设已经有序了
    int flag = 1;
    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;
        flag = 0;
      }
    }
    if (1 == flag)
    {
      break;
    }
  }
}
int main()
{
  int arr[10] = { 0 };
  //输入
  int i = 0;
  int sz = sizeof(arr) / sizeof(arr[0]);
  for (i = 0; i < sz; i++)
  {
    scanf("%d", &arr[i]);
  }
  //排序 - 升序
  //arr作为数组进行了传参
  //数组传参,传递的是地址,传递的是首元素的地址
  bubble_sort(arr, sz);//让这个函数来完成数组arr中数据的排序
           //arr是数组首元素的地址
  //输出
  for (i = 0; i < sz; i++)
  {
    printf("%d ", arr[i]);
  }
  return 0;
}

看完以上代码,肯定会有疑惑:为什么sizeof(arr)中的arr表示整个数组,而传参时arr表示数组首元素的地址呢?

//数组名该怎么理解?
//数组名通常情况下就是数组首元素的地址
//但是有2个例外:
//1. sizeof(数组名),数组名单独放在sizeof()内部,这里的数组名表示整个数组,计算的是整个数组的大小
//2. &数组名,这里的数组名也表示整个数组,这里取出的是整个数组的地址
//除此之外,所有遇到的数组名都表示数组首元素的地址
#include <stdio.h>
int main()
{
  int arr[10] = { 1, 2, 3, 4, 5, 6 };
  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);//+1,跳过整个数组
  //printf("%d\n", sizeof(arr));//40?
  return 0;
}


目录
相关文章
|
8月前
|
存储 C语言
C 数组
C 数组。
22 0
|
1月前
|
编译器 C++
C++系列四:数组
C++系列四:数组
|
1月前
数组练习2
数组练习2。
19 2
|
1月前
|
存储 人工智能 算法
4.为何数组下表从0开始
4.为何数组下表从0开始
45 1
|
1月前
|
存储 C语言
数组
数组。
15 0
|
1月前
|
存储 程序员 C++
c++数组详细介绍(一)
前言 深入理解C++的数组和字符串是成为熟练C++程序员的重要一步。本文将探索C++中数组和字符串的基本概念,从基础到进阶,包括数组的声明、初始化、访问和多维数组的操作,以及字符串类的使用和与字符数组的转换。还将涉及异常处理、动态内存分配、STL中的其他容器、常用字符串操作。
72 0
|
10月前
|
算法
三 数组
三 数组
41 0
|
10月前
|
存储 编译器 程序员
|
10月前
|
存储 IDE Linux
|
存储 编译器 程序员
C/C++ 中的数组
C/C++ 或任何编程语言中的数组是存储在连续内存位置的相似数据项的集合,可以使用数组的索引随机访问元素。它们可用于存储原始数据类型的集合,例如任何特定类型的 int、float、double、char 等。此外,C/C++ 中的数组可以存储派生的数据类型,例如结构、指针等。
122 0