《C和指针》读书笔记(第八章 数组)(上)

简介: 《C和指针》读书笔记(第八章 数组)(上)

0 内容简介

在C语言中,数组有着举足轻重的地位。而数组和指针千丝万缕的联系,也让其在求职,学习和工作中成为技术探讨的焦点。计算机编程语言群星璀璨,为何C语言数组在多年面试中的热度居高不下?它究竟有什么样的神奇魅力?今天我们就一起来探讨相关话题。

先来看看本篇的主要内容(章节编号与书中一致)。有一个宏观上的把握。

1. 一维数组

一维数组是最常见的数组,也是最常用的数组,在实际的开发中,为了便于迭代和阅读,有时会将多个数组拆分成多个一维数组。

1.1 数组名

一维数组的数组名是一个指针常量,指向数组的第一个元素,可以参考下面的程序:

#include <stdio.h>
int main()
{
  int temp[] = {1,2,3};
  printf("%d \n", *(temp));
  printf("%d \n", *(temp + 1));
  printf("%d \n", *(temp + 2));
  return 0;
}

打印输出:

1.2 下标引用

除了优先级以外,下标引用和间接访问完全一致。可以参考下面的程序:

#include <stdio.h>
#include <stdlib.h>
int main()
{
  int temp[] = {1,2,3};
  int *p = temp + 1;
  printf("数组的第一个元素是:%d \n", *(temp));
  printf("数组的第二个元素是:%d \n", *(temp + 1));
  printf("数组的第三个元素是:%d \n", *(temp + 2));
  //打印数组的第一个元素
  printf("数组的第一个元素是:%d \n", p[-1]);
  system("pause");
  return 0;
}

打印输出:

从上面的程序可以看出,指针p此时指向了数组的第2个元素,所以p[-1]会指向数组的第一个元素。

1.3 指针与下标

指针与下标,都是访问数组元素的有效方式,然而下标绝不会比指针更有效率,但指针有时会比下标更有效率,因牵扯到底层指令,在此不做展开。

1.4 指针的效率

指针有时比下标更有效率,前提是它们被正确地使用。因牵扯到底层指令,在此不做展开。

1.5 数组和指针

数组和指针并不是相等的。在声明数组的时候,已经分配好了内存,而在声明指针的时候,只知道其指向的数据类型,并不知道指向的具体地址,或者是没有任何意义的地址。

比方说有下面两个声明:

int a[5];
  int *p;

我们可以通过下面的程序进行验证

#include <stdio.h>
#include <stdlib.h>
int main()
{
  int a[5];
  int *p;
  printf("数组a的大小是%d \n", sizeof(a));
  printf("指针p的大小是%d \n", sizeof(p));
  system("pause");
  return 0;
}

打印输出:

可以看到,在经过编译后,一个int类型的数据占4个字节,这个时候系统已经为数组分配好了所有的内存;而指针p,我们只知道其指向一个int类型的变量,然而并不知道具体指向了哪个变量

1.6 作为函数参数的数组名

当一个数组名作为参数传递给一个函数时,传递给函数的是一份该指针的拷贝。函数如果进行了下标引用,实际上就是对这个指针执行间接访问操作,并且通过这种间接访问,函数可以访问和修改调用程序的数组元素。

可以参考如下的程序:

#include <stdio.h>
#include <stdlib.h>
void reverse_array(int arr[], int size)
{
  for (int i = 0; i < size / 2; i++)
  {
    int temp = arr[i];
    arr[i] = arr[size - i - 1];
    arr[size - i - 1] = temp;
  }
}
int main()
{
  int a[5];
  for (int i = 0; i < 5; i++)
    a[i] = i;
  reverse_array(a, 5);
  for (int i = 0; i < 5; i++)
  {
    printf("数组a第%d个元素是%d \n", i, a[i]);
  }
  system("pause");
  return 0;
}

1.7 声明数组参数

有一个有趣的问题,当我们把一个数组当做参数传递给函数的时候,正确的函数形参应该怎样的呢?应该声明为一个指针还是数组?

严格意义上来说都是正确的。可参考以下代码

#include <stdio.h>
#include <stdlib.h>
//声明为数组
void reverse_array1(int arr[], int size)
{
  for (int i = 0; i < size / 2; i++)
  {
    int temp = arr[i];
    arr[i] = arr[size - i - 1];
    arr[size - i - 1] = temp;
  }
}
//声明为指针
void reverse_array2(int *arr, int size)
{
  for (int i = 0; i < size / 2; i++)
  {
    int temp = arr[i];
    arr[i] = arr[size - i - 1];
    arr[size - i - 1] = temp;
  }
}
int main()
{
  int a1[5];
  int a2[5];
  for (int i = 0; i < 5; i++)
  {
    a1[i] = i;
    a2[i] = i;
  }
  //翻转数组
  reverse_array1(a1, 5);
  reverse_array2(a2, 5);
  //打印输出
  for (int i = 0; i < 5; i++)
  {
    printf("数组a1第%d个元素是%d \t", i, a1[i]);
    printf("数组a2第%d个元素是%d \n", i, a2[i]);
  }
  system("pause");
  return 0;
}

打印输出:

从上述例子可以看出,两种初始化从效果上来说是等同的。但是要说更加准确,应该是使用指针。因为实参实际上是个指针,而不是数组。

1.8 初始化

当数组的初始化局部于一个函数(或代码块)时,应该仔细考虑一下,在程序的执行流每次进入该函数(或代码块)时,每次对数组进行重新初始化是否值得。如果答案是否定的,就把数组声明为static,这样数组的初始化只需要在程序开始前执行一次。

关于static关键字,可以参考这篇文章:static关键字详解(C/C++)

1.9 不完整的初始化

所谓的不完整初始化,是指在数组初始化的时候,如果我们只对部分元素赋值,那么,剩下的元素会自动被赋值为0。可以参考下面的代码:

#include <stdio.h>
#include <stdlib.h>
int main()
{
  int a1[5] = {0,1};
  //打印输出
  for (int i = 0; i < 5; i++)
  {
    printf("数组a1第%d个元素是%d \n", i, a1[i]);
  }
  system("pause");
  return 0;
}

打印输出:

可以看到,没有初始化的几个元素,会被自动初始化为0,但这种自动初始化是有限制的,只能自动化地赋值后面的元素,不能赋值前面和中间的元素。

1.10 自动计算数组长度

如果在数组定义的时候就进行了初始化,那么不必指定数组长度,参考下面的例子:

#include <stdio.h>
#include <stdlib.h>
int main()
{
  int a1[] = {0,1};
  //打印输出
  printf("数组a1的大小是%d \n", sizeof(a1) / sizeof(int));
  system("pause");
  return 0;
}

打印输出:

1.11 字符数组的初始化

字符数组有两种初始化方式,一种是常规的初始化,比如:

char a1[] = {'0','1'};

还有另一种方式,方便快捷,就是像字符串的定义方式类似:

char a2[] = "01";

其实这两者并不完全等同,在第二种初始化方式中,默认多了一个‘\0’,所以数组a2有3个元素。可以参考下面的测试代码:

#include <stdio.h>
#include <stdlib.h>
int main()
{
  char a1[] = {'0','1'};
  char a2[] = "01";
  //打印输出
  printf("数组a1的大小是%d \n", sizeof(a1) / sizeof(char));
  printf("数组a2的大小是%d \n", sizeof(a2) / sizeof(char));
  system("pause");
  return 0;
}

打印输出:

C 语言中并不存在字符串这个数据类型,而是使用字符数组来保存字符串。

相关文章
|
10月前
使用指针访问数组元素
【10月更文挑战第30天】使用指针访问数组元素。
124 3
|
10月前
|
存储 程序员 编译器
C 语言数组与指针的深度剖析与应用
在C语言中,数组与指针是核心概念,二者既独立又紧密相连。数组是在连续内存中存储相同类型数据的结构,而指针则存储内存地址,二者结合可在数据处理、函数传参等方面发挥巨大作用。掌握它们的特性和关系,对于优化程序性能、灵活处理数据结构至关重要。
|
10月前
|
存储 NoSQL 编译器
C 语言中指针数组与数组指针的辨析与应用
在C语言中,指针数组和数组指针是两个容易混淆但用途不同的概念。指针数组是一个数组,其元素是指针类型;而数组指针是指向数组的指针。两者在声明、使用及内存布局上各有特点,正确理解它们有助于更高效地编程。
|
10月前
|
存储 C语言 计算机视觉
在C语言中指针数组和数组指针在动态内存分配中的应用
在C语言中,指针数组和数组指针均可用于动态内存分配。指针数组是数组的每个元素都是指针,可用于指向多个动态分配的内存块;数组指针则指向一个数组,可动态分配和管理大型数据结构。两者结合使用,灵活高效地管理内存。
|
10月前
|
容器
在使用指针数组进行动态内存分配时,如何避免内存泄漏
在使用指针数组进行动态内存分配时,避免内存泄漏的关键在于确保每个分配的内存块都能被正确释放。具体做法包括:1. 分配后立即检查是否成功;2. 使用完成后及时释放内存;3. 避免重复释放同一内存地址;4. 尽量使用智能指针或容器类管理内存。
|
10月前
|
存储 人工智能 算法
数据结构实验之C 语言的函数数组指针结构体知识
本实验旨在复习C语言中的函数、数组、指针、结构体与共用体等核心概念,并通过具体编程任务加深理解。任务包括输出100以内所有素数、逆序排列一维数组、查找二维数组中的鞍点、利用指针输出二维数组元素,以及使用结构体和共用体处理教师与学生信息。每个任务不仅强化了基本语法的应用,还涉及到了算法逻辑的设计与优化。实验结果显示,学生能够有效掌握并运用这些知识完成指定任务。
198 4
|
10月前
使用指针访问数组元素
【10月更文挑战第31天】使用指针访问数组元素。
116 2
|
10月前
|
算法 索引
单链表题+数组题(快慢指针和左右指针)
单链表题+数组题(快慢指针和左右指针)
104 1
|
11月前
|
存储
如何使用指针数组来实现动态二维数组
指针数组可以用来实现动态二维数组。首先,定义一个指向指针的指针变量,并使用 `malloc` 为它分配内存,然后为每个子数组分配内存。通过这种方式,可以灵活地创建和管理不同大小的二维数组。
|
11月前
|
存储
如何通过指针数组来实现二维数组?
介绍了二维数组和指针数组的概念及其区别,详细讲解了如何使用指针数组模拟二维数组,包括定义与分配内存、访问和赋值元素、以及正确释放内存的步骤,适用于需要动态处理二维数据的场景。