C语言 - 数组

数组的概念:

  • 数组(Array),是多个相同类型数据按一定顺序排列的集合,并使用一个名字命名,并通过编号的方式对这些数据进行统一管理。

数组的格式:

  • 如:int a[5];

  • 数组名—->a

  • 下标(或索引、index)—->a[0]….a[4]

  • 元素—->a[0]等于的值

  • 数组的长度—->5

数组的特点:

  • 数组中的元素在内存中是依次紧密排列的,有序的。

  • 创建数组对象会在内存中开辟一整块连续的空间。占据的空间的大小,取决于数组的长度和数组中元素的类型。

  • 我们可以直接通过下标(或索引)的方式调用指定位置的元素,速度很快。

  • 数组,一旦初始化完成,其长度就是确定的。数组的长度一旦确定,就不能修改

  • 数组名引用的是这块连续空间的首地址

一维数组:

  1. 数组名,记录该数组的首地址 ,即 a[0]的地址。

  2. C 语言规定,数组变量一旦声明,数组名指向的地址就不可更改。

  3. 数组通过变量名后加方括号表示,方括号里面是数组可以容纳的成员数量(即长度)。

  4. **注意:**声明数组时,必须给出数组的大小。

  5. 解释:

    一维数组

  6. 数组元素的调用:

    1. 格式:数组名[下标]

    2. 数组的下标从0开始,用“int arr[10];”定义数组,则最大下标值为9,不存在数组元素arr[10]。

      1
      2
      arr[0] = 13;       //对该位置数组元素进行赋值
      int score = arr[0]; //调用此位置的元素值
  7. 关于长度:

    1. 数组的字节长度:

      1. sizeof 运算符会返回整个数组的字节长度。
      1
      2
      int arr[10];
      printf("数组的字节长度为:%zd\n",sizeof(arr)); //40
    2. 数组的长度:

      1. 在定义数组时,需要指定数组中元素的个数,方括号中的常量表达式用来表示元素的个数,即数组长度。
      2. 由于数组成员都是同一个类型,每个成员的字节长度都是一样的,所以数组整体的字节长度除以某个数组元素的字节长度,就可以得到数组的成员数量。
      3. 注意:数组一旦声明/定义了,其长度就固定了,不能动态变化
  8. 定义方式:

    1. 数组可以在声明时,使用大括号,同时对每一个成员赋值。

      1
      int arr[5] = {22, 37, 90, 48, 95};
    2. C 语言允许省略方括号里面的数组成员数量,这时根据大括号里面的值的数量,自动确定数组的长度。

      1
      2
      3
      int arr[3] = {10,20,30};
      // 等同于
      int arr[] = {10,20,30}; //数组arr的长度,将根据大括号里面的值的数量,确定为 3
    3. 对数组部分元素赋初值:如果大括号里面的值,少于数组的成员数量,那么未赋值的成员自动初始化为 0 。

      1
      2
      3
      int arr[5] = {10, 20, 30};
      // 等同于
      int arr[5] = {10,20,30, 0, 0};
    4. 将整个数组的每一个成员都设置为零,最简单的方式如下

      1
      int a[100] = {0};
    5. 数组初始化时,可以指定为哪些位置的成员赋值。

      1
      2
      3
      int arr[15] = {[2] = 10, [5] = 20, [14] = 30};  //非角标2、5、14的位置自动赋值为0
      //等同于
      int arr[15] = {[5] = 20, [14] = 30, [2] = 10}; //指定位置的赋值可以不按角标从小到大的顺序
  9. memcpy()函数:

    1. memcpy() 函数定义在头文件 string.h 中,直接把数组所在的那一段内存,再复制一份。3个参数依次为:目标数组源数组以及要复制的字节数

      1
      2
      3
      4
      int a[3] = {10, 20, 30};
      int b[3];
      // 使用 memcpy 函数复制数组 a 到数组 b
      memcpy(b, a, 3 * sizeof(int));

char型数组与字符串:

  1. 字符型数组,顾名思义,数组元素的数据类型为字符型的数组。

  2. 一方面,可以看做普通的数组,初始化、常用操作如前所述,另一方面,字符型数组可以用于存储字符串。

    1
    2
    char arr[] = {'a','b','c','d'};//数组长度为5,最后不加\0
    char arr[]="helloworld";//数组长度为11,会在最后自动加1个\0
  3. C语言没有专门用于存储字符串的变量类型,字符串都被存储在char类型的数组中。在字符串结尾,C 语言会自动添加一个'\0' 的转义字符作为字符串结束的标志,所以字符数组也必须以 ‘\0’字符结束。

  4. 如果一个字符数组声明如下,由于必须留一个位置给 \0 ,所以最多只能容纳9个字符的字符串。

    1
    char str1[10];
  5. 字符串的长度:

    1
    char nation[10]={"China"};
    1. 数组nation的前5个元素为: ′c′,′h′,′i′,′n′,′a′,第6个元素为′\0′,后4个元素也自动设定为空字符。

    2. **注意:**在计算字符串长度的时候,’\0’ 是结束标志,不算作字符串内容。

      1
      2
      3
      4
      5
      6
      7
      #include <stdio.h>
      #include <string.h> //需要加载此头文件

      int main() {
      char nation[10] = "China";
      printf("%d\n", strlen(nation));//5
      }
  6. 解释说明:

    1
    2
    3
    char s1[50] = "hello";  //声明1
    char s2[] = "hello"; //声明2
    char s3[5] = "hello"; //声明3
    • 声明1:赋给的元素的个数小于该数组的长度,则会自动在后面加 ‘\0’, 表示字符串结束。所以,字符数组 s1 的**长度是 50** ,但是字符串“hello”的**实际长度只有5**(不包含结尾符号 ‘\0’ ),所以后面空出来的45个位置,都会被初始化为 ‘\0’。
    • 声明2:字符数组 s2 的**长度是 6(包含结尾符号 ‘\0’ ),但是字符串“hello”的实际长度只有5**。
    • 声明3:赋给的元素的个数等于该数组的长度,则不会自动添加 ‘\0’。但字符串要求以’\0’结束,所以这种写法是**错误的**,要避免。
  7. 比较**”x”‘x’**的不同:

    • 书写形式不同:字符串常量用双引号,字符常量用单引号。
    • 存储空间不同:在内存中,字符常量只占用一个字节的存储空间,而字符串存储时自动加一个结束标记’\0’,所以’x’占用1个字节,而”x”占用2个字节
    • 二者的操作也不相同。例如,可对字符常量进行加减运算,字符串常量则不能。

二维数组:

  1. 定义的时候,可以不给出行数,但必须要给出列数(不建议不给出行数)

  2. 定义方式:

    1. int a[3][4]; 表示这个数组是3行4列的
    2. 可以理解成一栋楼有3层,每层有4个房间
  3. 解释说明:

    1. 内存中,各元素是连续存放的,不是二维的,是线性的。

    2. C语言中,二维数组中元素排列的顺序是按行存放的。即:先顺序存放第一行的元素,再存放第二行的元素。(最右边的下标变化最快,第一维的下标变化最慢)。

      二维数组

冒泡和选择排序:

  1. 冒泡排序:比较相邻的元素并交换它们的位置,从而将较大的元素逐渐“冒泡”到数组的末尾
    1. 外层循环是控制比较总轮数:比如10个数,只需确定9个数的位置就行,另1个也就确定了,所以外层循环为n-1
    2. 内层循环是控制比较相邻元素:因为是俩俩比较,所以假如有10个数,只需比较9次,但是每当外层循环确定一次,也就是确定了1个数的位置,也就不需要再比较了,所以为n-1-外层循环次数
    3. 公式总结:
      1. 外层为:n-1
      2. 内层为:n-1-外层循环次数
  2. 选择排序:每次从未排序的部分中选择最小(或最大)的元素,将其放到已排序部分的末尾。
    1. 外层循环是控制比较总轮数:比如10个数,拿第1个和另外9个依次比较,所以外层循环为n-1
    2. 内层循环是控制每次比较次数:比如10个数,外层每循环一次,在开头确定一个位置,所以内层每次循环是在外层循环加1的基础上执行的
    3. 公式总结:
      1. 外层:n-1
      2. 内层:int j=外层轮数+1,j<n(总数据)

步长计算:

1
2
3
int arr[2][3]={
{3,6,7},
{8,5,6}};
表达式 类型 步长 典型操作实例
&arr int (*)[2][3] 24字节(23sizeof(int)) 极少用于指针运算,因为会越界
arr int (*)[3] 12字节(3*sizeof(int)) arr+1 跳一行
arr[0] int * 4字节(sizeof(int)) arr[0]+1 跳下一个元素
&arr[0][0] int * 4字节(sizeof(int)) &arr[0][0]+1 <=> &arr[0][1]
arr[0][0] int 直接取值
  • 参考链接: