5.3 指针与数组

5.3.1 数组的几个概念

什么是数组?数组是组织数据的一种方式。

如何创建一个数组?

int numbers[5]; // 定义一个包含 5 个整数的数组

为什么要定义数组?

  1. 组织数据:数组允许你将一组相关的数据元素组织在一起,从而更容易管理和访问这些数据。

  2. 节省内存:数组在内存中是连续存储的,这意味着它们占用的内存空间相对较小,因为它们没有额外的开销用于存储数据之间的链接信息。

  3. 快速访问:通过索引,你可以快速访问数组中的任何元素,而不必遍历整个数据集。

生活中的例子:

/*
  ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
  │ 存在一个问题:只支持整数输入。                                                                                                 │
  └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
 */
#include <stdio.h>

int main()
{
    // 定义一个整数数组来存储一周中每天的温度
    int temperatures[7];

    // 输入每天的温度数据
    for (int i = 0; i < 7; i++)
    {
        printf("Enter temperature for day %d: ", i + 1);
        scanf("%d", &temperatures[i]);
    }

    // 打印一周中每天的温度
    printf("Weekly temperatures:\n");
    for (int i = 0; i < 7; i++)
    {
        printf("Day %d: %d degrees\n", i + 1, temperatures[i]);
    }

    return 0;
}
➜  ~
Enter temperature for day 1: 37
Enter temperature for day 2: 38
Enter temperature for day 3: 26
Enter temperature for day 4: 40
Enter temperature for day 5: 38
Enter temperature for day 6: 36
Enter temperature for day 7: 26
Weekly temperatures:
Day 1: 37 degrees
Day 2: 38 degrees
Day 3: 26 degrees
Day 4: 40 degrees
Day 5: 38 degrees
Day 6: 36 degrees
Day 7: 26 degrees

Saving session...completed.

5.3.2 数组下标

数组的索引通常从 0 开始为什么?数组索引从 0 开始的设计是历史上的一种约定,起初是为了更有效地实现数组的内部表示和访问。这个设计选择有一些历史原因和技术因素:

  • 直接内存访问:在底层的内存管理中,数组的元素是按照连续的内存地址排列的。通过从零开始的索引,可以更容易地计算出每个元素的内存地址。例如,第一个元素的地址就是数组的起始地址,第二个元素的地址是第一个元素地址加上一个固定的偏移量,以此类推。这种设计使得数组的元素可以通过简单的数学运算来直接访问,而无需复杂的指针计算。

  • 历史原因:早期的编程语言,特别是汇编语言和低级语言,通常使用从 0 开始的索引。C语言在设计时受到了这些语言的影响,因此也采用了从 0 开始的索引。这种约定的延续使得C语言的设计更具一致性和可预测性。

  • 方便性:从 0 开始的索引也具有一些方便之处。例如,数组的长度就是最后一个元素的索引加1,这使得计算数组的长度更加直观和方便。

尽管从 0 开始的索引在历史上有一些技术原因,但它已经成为了一种编程的标准约定。这种约定在C语言中被广泛采用,也在许多其他编程语言中得到了继承和延续,因此程序员通常期望数组的索引从0开始。虽然它可能在某些情况下导致初学者困惑,但一旦习惯了,它可以提供更加一致和方便的编程体验。在定义数组的时候,其实已经申请了一个内存空间,数组在内存中有也自己的一个“序号”=“数组下标”。

5.3.3 数组的初始化

数组可以进行完全初始化、不完全初始化。

还可,进行指定初始化:

int a[5]={[2]=3,[3]=2};

5.3.4 数组长度计算

求一个未知长度的数组的长度,并进行遍历打印:

#include <stdio.h>
int main()
{
    int arr[] = {1, 2, 3, 4, 5};
    int length = sizeof(arr) / sizeof(arr[0]);
    // printf("%d\n", length);

    int i = 0;
    for (int i = 0; i < length; i++)
    {
        printf("%d\n", arr[i]);
    }

    return 0;
}

打印数组占用字节数:

/* 
  ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
  │ 多样的数组长度有关的计算                                                                                                    │
  │ OUTPUT:                                                                                                         │
  │ 4                                                                                                               │
  │ 20                                                                                                              │
  │ 5                                                                                                               │
  └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
 */
#include <stdio.h>

int main() {
    int arr[]={1,2,3,4,5};
    //打印int类型占用的字节数
    int length1 =sizeof(int);
    printf("%d\n", length1);
    //打印数组占用的字节数
    int length2 =sizeof(arr);
    printf("%d\n", length2);
    //打印数组中元素的个数
    int length3 =sizeof(arr) /sizeof(arr[0]);
    printf("%d\n", length3);

    return 0;
}

5.3.5 二维数组(矩阵)及其初始化

注意:作为一个 idiom 记忆,内存花括号的省略会是很危险的。

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

二维数组初始化 SOL1:

二维数组初始化 SOL2 不够补零 :

5.3.6 二维数组、指针与冒泡排序

数组作为函数的参数,并以指针的形式出现。

// Bubble sort (冒泡排序)2
#include <stdio.h>

void bubble_sort( int arr[] ,  int length ) {   
//      /*int arr[] 形式上是数组,实际上是指针*/
//确定需要几次冒泡
// int length=sizeof(arr)/sizeof(arr[0]);  
//内部求解长度不可行, 在外部求解完之后再进函数

int i=0;

for (i=0; i<length-1; i++){  //length-1 趟
    
    int j=0; //下标
    for (j=0; j<  length-i-1    ; j++)  {
        if (arr[j] > arr[j+1] )  // 两对数之间进行比较,没进去一趟对数-1 
    {
        /* 交换 */
        int Tmp = arr[j];
        arr[j] = arr[j+1];
        arr[j+1] = Tmp;
    }
    }

    }
  
}

int main() {
    int arr[] = {5, 2, 1, 4, 3, 0}; //arr是数组名
    int length=sizeof(arr)/sizeof(arr[0]); //length是数组的长度
    bubble_sort(arr, length);  //这一步就在调用函数 bubble_sort(),把上面两个“变量”作为参数传给函数

    int i=0;
    for ( i = 0; i < length; i++) {
        printf("%d", arr[i]);
    }

    

    return 0;
}

5.3.7 数组名作为指针

指针与数组更为关键的联系:可以用数组的名字作为指向数组第一个元素的指针。

注意:虽然能把数组名用作指针,但是不能给数组名赋新的值。如果要更改,采取下列先复制再更改的方法:

p=a;
while (*p!=0)
    p++;
#include <stdio.h>

int main()
{
    int arr[] = {1, 2, 3, 4, 5};
    int *p;
    int sum = 0;  // 声明并初始化 sum

/* 
  ┌────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
  │     p=&a[0]:将指针 p 设置为数组 a 的第一个元素的地址                                                                                │
  │     p<=&a[N]:p 的值(也就是地址)小于或等于数组 a 的最后一个元素的地址,循环就会继续                                                                │
  │     p++:这是 for 循环的更新部分。每次循环结束时,p 的值都会增加,使其指向数组的下一个元素。                                                              │                                                                                                         │
  └────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
 */
    for (p = &arr[0]; p <= &arr[4]; p++)
    {
        sum += *p; //*p 代表 p 所指向的值
        printf("%d\n", *p);
    }

    // 如果你想打印 sum 的值,可以添加以下代码
    printf("Sum: %d\n", sum);

    return 0;
}
/* 
  ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
  │ 使得遍历数组循环更加方便                                                                                                    │
  │ 运行~                                                                                                              │
  │ 1                                                                                                               │
  │ 2                                                                                                               │
  │ 3                                                                                                               │
  │ 4                                                                                                               │
  │ 5                                                                                                               │
  │ Sum: 15                                                                                                         │
  └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
 */


/* 
  ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
  │     for (p = &arr[0]; p <= &arr[4]; p++)                                                                            │
  │     {                                                                                                               │
  │         sum += *p;                                                                                                  │
  │         printf("%d\n", *p);                                                                                         │
  │     }                                                                                                               │
  │     改写:                                                                                                             │
  │                                                                                                                     │
  │     p = &arr[0];                                                                                                    │
  │     while (p <= p <= &arr[4])                                                                                       │
  │     {                                                                                                               │
  │         sum += *p++;                                                                                                │
  │     }                                                                                                               │
  └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
 */

代码解释:

/* 
  ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
  │ 注意:*p是内容,p是地址     
  |                                                                                                 │
  | sum += *p++;                                                                                                    │
  │ 等价于                                                                                                             │
  │ sum += *p;                                                                                                      │
  │ p++; 将 p 的值(也就是地址)增加一定的量,使其指向下一个元素                                                                                                           │
  │ sum += *p;                                                                                                      │
  │ 等价于                                                                                                             │
  │ sum = sum + *p; 将 sum 和指针 p 所指向的元素的值相加,然后将结果赋值给 sum                                                                                                │
  └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
 */

5.3.8 指针与数组的神奇公式

把数组取下标和指针算术运算关联起来的神奇公式:

/* 
  ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
  │ OUTPUT:
  │ a[0]: 7                                                                                                         │
  │ a[1]: 12                                                                                                        │
  └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
 */
#include <stdio.h>

int main()
{
    int a[10];
    *a = 7; // a[0] = 7;
    *(a + 1) = 12; // a[1] = 12;

    printf("a[0]: %d\n", *a);
    printf("a[1]: %d\n", *(a + 1));

    return 0;
}

通常情况下,a+i等价于 &a[i],也就是两者都表示指向数组 a 中元素 i 的指针。并且,*(a+i) 等价于 a[i] 两者都表示元素 i 本身。换句话说,可以把数组的取下标看成是指针算术运算的一种形式。

Last updated