文章目录

  • 前言
  • 数组
    • 一、一维数组
      • 1.1、初始化一维数组
      • 1.2、一维数组元素赋值
      • 1.3、一维数组传参
    • 二、二维数组
      • 2.1、二维数组的声明和初始化
      • 2.2、二维数组传参
      • 2.3、高维数组
    • 三、数组与指针
      • 3.1、指针简介
      • 3.2、数组名的含义
      • 3.3、指针访问数组
        • 指针访问数组元素
        • 指针访问整个数组

前言

在编程的道路上,我们处理的问题越发复杂,数据越发庞大,为了解决庞大的数据,学习数组便有很重要的意义。


数组

何为数组?

数组:数据类型相同的一系列元素组成的集合。
数组的下标有0开始

了解它的定义,就要开始学习如何使用它。


一、一维数组

一维数组是我们平时使用最多的数组
char str[23];
float candy[44];
int code[50];
以上均为一维数组 - 数组名后面仅有一个[ ]

1.1、初始化一维数组

我们将一维数组分为4部分:如 int arr[30] = {1,2,3};

  1. int - 数组内元素的类型
  2. arr - 数组名
  3. [30] - 数组内元素个数
  4. {1,2,3} - 数组元素的初始化

初始化数组,则要注意第3和第4点


  1. 数组元素个数的初始化
#include <stdio.h>
#define MAXN 20
int main(void)
{int n = 10;//数组元素个数的初始化int a1[4];              //YESint a2[5*3+1];          //YESint a3[sizeof(int)+4];  //YESint a4[MAXN];           //YES int a5[-3];             //NOint a6[0];              //NOint a7[2.5];            //NOint a8[(int)2.5];       //YESint a9[n];              //c99之前的标准不允许int a10[] = {1,3,5};    //YES,元素个数为3return 0;
}

总结:[ ]内可以是常量和常量表达式(但结果应为正整数),符号常量(#define定义的);
至于变量需要编译器支持c99标准,为了代码的兼容性,因此即使你的编译器支持也最好别用。
也可以[ ]内为空,有后面的初始化赋值来决定元素个数


  1. 初始化赋值
#include <stdio.h>
#define MAXN 20
int main(void)
{//初始化时后面用 {},元素之间用逗号隔开 int arr1[] = { 1,3,4,5 }; //元素个数为4int arr2[3] = { 1,3,4,5 };//初始化列表中的项数多于数组元素个数,错误int arr3[2] = { 1,3.4 };   //3.4为double类型,赋给int类型的元素会使数据被截断int arr4[3];              //未赋值,数组内元素为随机值int arr5[30] = { 1 };     //第一个元素被赋值为了1,//后面29个元素被编译器自动赋值为0return 0;
}

总结:

  1. 当初始化列表中的项数少于数组元素时(上面最后一种情况),后面的元素值自动赋为0(字符数组是 \0 ;根据ASCII码表,\0 字符对应的值是 0,所以对于字符数组,也可以说后面的元素被赋值为0)
  2. 赋的值应当于元素的类型相对应(上面的第3种情况),虽然不会报错,但数据不准
  3. [ ]内为空时,编译器会自动匹配数组大小和初始化列表中的项数(上面第1种情况)

还有一种常用的初始化方法 - 用memset进行初始化

int arr[10];
memset(arr,0,sizeof(arr));
对于其作用机制,读者可自行查阅


字符数组进行赋值时

//以下情况允许char arr1[] = { 'a','b','c','d' };char arr2[] = "aasdf";char arr3[] = { "acbhd" };//以下情况不允许char arr4[] = 'a';// 总结: 字符串可以不带括号//        但单个字符赋值时必须要{}

  1. **c99新特性 - 指定初始化器//

有一种情况:int arr[10],你只想将第6个元素初始化为6,其余为0,怎么写?
int arr[10] = {0,0,0,0,0,6};
c99可以这样写
int arr[10] = {[5] = 6};
而且
int arr[10] = { 1,2,[5] = 6,7,8};
arr内的每个元素的值为
1 2 0 0 0 6 7 8 0 0
在 指定初始化器后面的值 会接着在 初始化器后面 赋值


1.2、一维数组元素赋值

声明数组后,便可以借助数组下标对数组元素进行赋值

一般给数组赋值
常用循环

#include <stdio.h>
#define MAXN 20
int main(void)
{int arr[MAXN];for (int i = 0; i < MAXN; i++){arr[i] = i;}return 0;
}

下面进行一些错误示范

#include <stdio.h>
#define MAXN 20
int main(void)
{int a1[MAXN] = { 5,3,5,7 };int a2[MAXN];a1 = a2;             //错误a2[MAXN] = a1[MAXN]; //数组下标越界a2[3] = { 5,3,4,2 }; //无作用,错误return 0;
}

1.3、一维数组传参

int a[10] = {1,2,3,4,5,6,7,8,9,10};
void nb1(int a[]) //[]内数字可省略
void nb2(int* pa) //用指针接受,至于为什么,后面的数组名含义有解释

二、二维数组

在实际应用中,一维数组并不够用,我们需要更高维的数组
列如:我想存储一年的天气情况,查询第m月第n天的天气
如果采用一维数组,那我需要先要算m月n日 距1月1日的天数,在带入下标,这并符合我们的习惯
难道不能像平面直角坐标系一样,通过 横坐标 和 纵坐标 来访问,而这就要用到二维数组


2.1、二维数组的声明和初始化

int arr[row][col];前一个[]为行,后一个[]为列

#include <stdio.h>
int main(void)
{//以下为正确形式int a0[3][4];int a1[3][4] = { {1,2,3,4},{2,3,4,5},{3,4,5,6} };int a2[][4] = { {1,2,3,4},{2,3,4,5},{3,4,5,6} };int a3[][4] = { 1,2,3,4,5,6,7,8,9,10 };//不足的补0//以下为错误形式int a4[][] = { {1,2,3,4},{2,3,4,5},{3,4,5,6} };            //不能省略列int a5[3][3] = { {1,2,3,4},{2,3,4,5},{3,4,5,6},{4,5,6,7} };//行列均超过限定数目,错误return 0;
}

总结:

  1. 若不进行初始化,行列均不能省
  2. 若进行初始化,可以省略行,但不能省略列(如a2,a3)
  3. 可以在{ }内再使用{ }进行归类,也可以不使用,编译器可以自动根据 列 的数量来归类(如a3)
  4. 行列应该后面列表的项数对应,如(a5)

2.2、二维数组传参

int a1[3][4] = { {1,2,3,4},{2,3,4,5},{3,4,5,6} };
void nb1(int a1[][4]);//不能省略列数
void nb2(int (*pa)[5]);//形参是数组指针,后面指针访问数组有讲。

2.3、高维数组

既然都有了有二维数组,自然少不了三维数组,四位数组;
如 int b[10][10][10];
可以这样理解:
一维数组 - 一行数据
二维数组 - 数据表
三维数组 - 一叠数据表
对于初学者,一维和二维数组已经够用,不建议继续研究更高维。

三、数组与指针

3.1、指针简介

指针?什么是指针?本质来看指针是一个存储内存地址的变量
如int类型存储的是整数,int*(指针)则存储的是地址;
要理解指针需要从以下4个方面

  1. 指针的类型
  2. 指针所指向的类型
  3. 指针的值或者叫指针所指向的内存区
  4. 指针本身所占据的内存区

首先了解指针的声明

指针的声明如下:
int a = 10;//a占4字节
int* pa = &a;//拿到的是a的4个字节中的第一个字节的地址
指针大小:32位为4字节,64位为8字节
以上涉及两个操作符符

  1. &
    & 只有一个操作数 表示 取地址操作符
    & 有两个操作数时 表示 按位与操作符
  2. 访问a变量则通过解引用操作符(*)
    如 *p = 20; 则a变量存储的值变为20;

使用指针,最重要的是理解指针的类型的意义

指针类型的意义

  1. 指针类型决定了解引用的权限有多大
  2. 指针类型决定了指针走一步走多远。int类型+1,4字节,char类型+1,1字节
int main(void)
{//内存观察时-1int a = 0x11223344;//一个十六进制等于4个二进制,所以11223344刚好站4字节int* pa = &a;*pa = 0;//4个字节内存都变成了0//a变为 0x00000000int b = 0x11223344;char* pc = &b;*pc = 0;//1个字节变成了0//b变为 0x11223300return 0;
}

至于b为什么不是变为 0x00223344,这涉及 大小端存储 感兴趣的读者可自行查阅

现在我们以经初步了解指针,但指针作为c语言最难的一部分,主要体现在指针与其他知识的结合,而下面我们将介绍指针与数组的结合


3.2、数组名的含义

int a[10];
a为该数组的数组名
数组名在大多数情况下表示首元素的地址

a 等价于 &a[0]


但有两个例外

#include <stdio.h>
int main(void)
{int a[10];// 例外1sizeof(a); //此时数组名表示整个数组,计算整个数组的大小// 例外2&a;        //此时数组名表示整个数组,取出整个数组的地址return 0;
}

对于特例1,很好解释,只要看看结果就行。

对与特例2,我们通过 调试窗口 中的 监视窗口 来看

看图可知:
a表示的地址和&a表示的地址都一样
结果均为:0x0000000f9c0ffb58
这并不能看出二者的区别,但通过a+1,&a+1可看出
a+1 相比 a 的地址增加了 4
&a+1 相比 &a+1 的地址增加了 40

也就是说,a+1仅仅相当于跳过了一个元素,&a+1则跳过了一个数组
故 &a 取出的是一个数组的地址


3.3、指针访问数组

指针访问数组元素

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


从上述代码,我们不难发现 arr[i]的地址与 p+i指针内存储地址 相同,则 *(p+i) 的值为 arr[i];
这表明指针和数组的关系十分密切,事实上,c语言标准在描述数组表示法时也用到了指针

arr[i] 本质上是 *(arr+i)

注意:*(arr+i) != *arr+i *的优先级要高于运算符
前者是 arr[i]
后者是 arr[0] + i

编译器会自动将 arr[i] 转换为 *(arr+i) 而这就为数组描述法增添了其他几种写法

int main(void)
{int arr[] = { 1,2,3,4,5,6,7,8,9,10 };int* p = arr;//其一arr[4]  ==  *(arr+4) == 4[arr];//其二arr[4]  == *(p + 4)  == p[4] == 4[p];return 0;
}

指针访问整个数组

有一种指针类型叫 数组指针,顾名思义:数组指针指向的是整个数组

int main(void)
{int arr[10] = { 1,2,3,4,5,6 };//数组指针的创建//错误int *parr[10] = arr;//*parr先与[]结合,arr不是数组地址//正确int(*parr)[10] = &arr;//指针数组的声明double* d[5];         //指针数组,共5个元素,每个元素类型为double*double* (*pd)[5] = &d;//数组指针,指针pd 指向一个 指针数组d//要点:类型与数组类型一样,括号内为指针return 0;
}

注意指针数组的声明格式:
如上 double* (*pd)[5] = &d;

  1. 类型一致,如上double* 对应 double*
  2. ()不能少
  3. & 不能少,若去掉则等号右边表示为首元素地址

为防止将数组指针与指针数组用混,看下方解释:

  1. int arr[5];//整型数组
  2. int* parr1[10];//整型指针数组
  3. int(*parr2)[10];//整型数组指针,该指针指向一个数组,数组10个元素,每个元素的类型是int类型
  4. int(* parr3[10])[5];//首先 parr3[10]是数组,去掉,剩int(*__)[5],为数组指针,所以parr3是一个存储数组指针的数组,该数组能够存放10个数组指针,每个数组指针能够指向一个数组,数组5个元素,每个元素是int类型

证明 数组指针真的指向整个数组,看下列代码:

int main(void)
{int arr[10] = { 0 };int* p1 = arr;int(*p2)[10] = &arr;printf("%p\n", p1);printf("%p\n", p1+1);//跳了4,即跳了一个数组元素printf("%p\n", p2);printf("%p\n", p2+1);//跳了40,即跳了一个数组return 0;
}

那么数组指针有什么用处呢?
首先数组指针指向的是整个数组,那么用在一维数组上肯定没啥用,它一般用在高维数组

#include <stdio.h>
void print1(int arr[][5], int r, int c)
{int i, j;for (i = 0; i < r; i++){for (j = 0; j < c; j++){printf("%d ", arr[i][j]);}printf("\n");}
}
void print2(int(*pa)[5], int r, int c)
{int i, j;for (i = 0; i < r; i++){for (j = 0; j < c; j++){printf("%d ", *(*(pa + i)+j));//*(pa + i)为arr[i]数组,不是元素,是数组,而像一维数组 *(p+i) 为 一个数组元素}printf("\n");}
}
int main(void)
{int arr[3][5] = { {1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7} };//print1(arr, 3, 5);print2(arr, 3, 5);//arr数组名表示首元素地址,就是第一行元素return 0;
}

其实读者不难发现,这数组指针好像也没啥用,反倒还将代码变得更复杂了
事实上并不是,而是如今学的太少,还轮不到去使用它,日后可能某些问题就非它不可了


【C语言】数组的超详细解答,走过路过别错过相关推荐

  1. 数据结构--链栈的c语言实现(超详细注释/实验报告)

    数据结构–链栈的c语言实现(超详细注释/实验报告) 知识小回顾 栈(Stack)作为一种限定性线性表,是将线性表的插入和删除操作限制为仅在表的一端进行,通常将表中允许进行插入.删除操作的一端成为栈顶( ...

  2. 【机试题】2014大疆嵌入式笔试题(附超详细解答,下篇)

    上一篇主要是对<2014大疆嵌入式笔试题>的前一部分进行了解答,本文主要是对接下来的几道题进行解答.想要参考上一篇的点击链接:[机试题]2014大疆嵌入式笔试题(附超详细解答,上篇). 嵌 ...

  3. Matlab中的向量和数组(超详细)

    Matlab中的向量和数组(超详细) 文章目录 Matlab中的向量和数组(超详细) Matlab中的向量 介绍 创建向量 向量的大小 索引向量 数值索引 逻辑索引 缩短向量 向量运算 算术运算 逻辑 ...

  4. PS图层混合模式超详细解答-图层混合模式的原理(Part1)

    PS图层混合模式超详细解答-图层混合模式的原理 ☕ 前言 本教程非常详细,请用心看完 本教程如果有如何问题,欢迎评论区留言讨论 本教程为了避免冗余,一些不必要的截图就省略了 本教程只讨论8bit的情形 ...

  5. [蘑菇街] 搜索、算法团队招募牛人啦-年底了走过路过不要错过 - V2EX

    [蘑菇街] 搜索.算法团队招募牛人啦-年底了走过路过不要错过 - V2EX [蘑菇街] 搜索.算法团队招募牛人啦-年底了走过路过不要错过

  6. 倍增算法入门 超详细解答+LCA+RMQ(ST表)+例题剖析

    目录 一.倍增算法 二.倍增算法的应用:求LCA(最近公共祖先)附模板题 三.倍增算法的应用:RMQ 问题(ST表)附模板题 一.倍增算法 要了解倍增之前,强烈建议大家先看一下这位大佬对倍增的解释:[ ...

  7. 倍增算法入门 超详细解答+LCA+RMQ(ST表)+例题剖析

    目录 一.倍增算法 二.倍增算法的应用:求LCA(最近公共祖先)附模板题 三.倍增算法的应用:RMQ 问题(ST表)附模板题 一.倍增算法 要了解倍增之前,强烈建议大家先看一下这位大佬对倍增的解释:[ ...

  8. c++自带的可持久化平衡树?rope大法好!(超详细解答 + 5道例题讲解,可直接替代可持久化的线段树、并查集、平衡树!)

    整理的算法模板合集: ACM模板 目录 c++自带的可持久化平衡树?rope大法好! 1. 声明 2. 支持操作 char类型的rope int类型的rope 3. 具体的细节 4. "可持 ...

  9. 【机试题】2019大疆嵌入式笔试题A卷(附超详细解答)

    前不久的大疆嵌入式线上笔试,可能是因为最近只是在做毕设项目,还没有来得及认真系统复习,直接崩了.就凭借着记忆,把一些记得住的笔试题分享一下,作下记录. 整个大疆嵌入式线上笔试,分为选择题(单选题.多选 ...

最新文章

  1. DSVS7050签名服务器的网站,吉大正元数字签名服务器-安装部署手册(COM版 VCTK_S接口)2.1.1.doc...
  2. 中断描述符表IDT以及Linux内核IDT表的初始化的基本情况
  3. 【学习笔记】ODATA
  4. python中text怎么旋转字体_Python+OpenCV实现旋转文本校正方式
  5. 流量回放开源代码Java_流量回放框架 jvm-sandbox-repeater 的实践
  6. oracle如何自定义类型,Oracle 自定义类型
  7. 计算机二级在线练,计算机二级操作练习题.doc
  8. IDC 机房空调问题解决方案
  9. session的销毁方式
  10. 2021中国AI商业落地典型案例
  11. con排插与单片机相连_单片机与电路绘图自学手册
  12. 萌新扫盲2—双绞线的“一百米诅咒”
  13. 【实习日报】2019年6月上半月 前端开发实习工作日报汇总
  14. C#实现让鼠标点击任意绝对位置
  15. 从0到1的电商架构应该怎么做?
  16. AI绘画怎么生成?这些软件帮助你实现
  17. 僵尸网络检测和抑制方法
  18. 微信入口绑定,微信事件处理,微信API全部操作
  19. 有哪些值得一读的理财启蒙书
  20. xcode提交app时出现icon缺少167.png图片的问题

热门文章

  1. 我们如何把pdf转cad简单操作呢?
  2. kiss原则包括什么_编程中的kiss原则是什么?
  3. CVX用户指南之快速入门
  4. Linux--RH124---unit 1 基础使用
  5. 51单片机花样流水灯
  6. android下拉五级菜单联动
  7. 【视觉SLAM之认识摄像头】
  8. 电脑录屏软件哪个免费
  9. windows server 2012 r2 standard 通过堡垒机远程桌面服务器报错Remote Desktop Service CALs Request Failed
  10. 中国游戏社区市场发展动态及十四五发展策略分析报告2022年版