文章目录

  • 前言——小颖的话
  • 一、一维数组
    • 1.一维数组的创建和初始化
      • 1)一维数组的创建(定义)
      • 2)一维数组的初始化
    • 2.一维数组的使用
    • 3.一维数组在内存中的存储
  • 二、二维数组
    • 1.二维数组的创建和初始化
      • 1)二维数组的创建(定义)
      • 1)二维数组的初始化
    • 1.二维数组的使用
    • 3.二维数组在内存中的存储
  • 三、数组越界问题
  • 四、数组作为函数的参数
  • 总结

前言——小颖的话

上一篇我们学习了C语言里函数的相关知识,今天我们来学习C语言中另一个非常重要的知识点——数组。直接上干货!


提示:正文开始,学习过程中跟着讲解一起打代码会事半功倍哦

一、一维数组

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

1)一维数组的创建(定义)

在第二篇博客C语言知识总括里我们知道,数组是一组相同类型元素的集合。一般我们数组的创建方式是这样:

type_t     arr_name  [const_n];
数组元素类型 数组名     [常数];

在上一篇我们说过,一般type_t表示一种类型,可以使int,long,double,char 等等等所有类型中的任意一个,而const_n表示一个常数(c99标准有变长数组概念,我们在这里暂不讨论)
举几个例子哈:

int arr1[5];
char arr2[10];
float arr3[20];

上面这些都是正确的创建方式,下面是错误示范:

int n = 10;
int arr[n];//n是一个变量,创建数组的时候这里的必须是常量

当然这样也不行:

const int n = 10;
int arr[n];//n叫做“常变量”,虽然其大小不能改变,但是本质还是一个变量

2)一维数组的初始化

在数组进行定义的时候同时给定数组中元素的值,就叫做数组的初始化,比如:

int arr1[3] = {1 ,2 ,3};
int arr2[10] = {0 ,1 ,2};
int arr3[] = {0 ,1 ,2 ,3};
char arr4[] = {'a', 'b', 'c'};
char arr5[] = "abc";

看完例子你可能会有一些疑惑,这里来一一解释

首先数组的初始化包括完全初始化不完全初始化,比如例子里的arr1是完全初始化,就是把每一个元素的值都一一给定,arr2就是不完全初始化,有十个元素但是只给定了前三个,我们之前说过,其他没给定值的元素都默认初始化为0;

arr3的方括号里没有数字,看起来不像是正确的创建格式啊,但是其实在初始化的时候如果不给定数组元素,就默认了数组元素就是你给定值的个数,比如arr3就是4个元素,arr4里三个元素;

然后arr5就是一个数组串的一种定义方式,注意arr5里一共有4个元素,因为数组串的结尾默认会有一个’\0’没有写出来,我们在这个系列第二篇——C语言知识总括-数组串 那一节详细介绍了,这里就不再赘述。

2.一维数组的使用

同样在前面我们说过一个[ ]这个下标引用操作符来访问对应下标的数组元素:

#include<stdio.h>
int main()
{int arr[10];int sz = sizeof(arr)/sizeof(arr[0]);//这是个常用求数组元素个数的代码。int i;for(i = 0 ;i < sz ;i++){arr[i] = i;//给数组每一个元素赋值}for(i = 0 ;i < sz ;i++){printf("%d ",arr[i]);//打印数组中每一个元素}return 0;
}

注意:我们知道sizeof是一个操作符,使用来求一个类型占用内存大小的操作符,但是注意sizeof + 数组名 计算出来的是数组的总大小,所以sizeof(arr) / sizeof(arr[0])就是总大小除以一个元素的大小,不就是数组中元素的个数嘛。

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

那么一维数组里的元素在内存中又是怎样放置的呢?是跳跃式的?随机的?还是连续的?我们敲代码来看:

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

在小颖的电脑上输出是这样(你的地址不一样很正常,每个人得到的地址都会有差异)

这样的地址我们可以发现什么呢?每一个元素和下一个元素之间的地址都相差4个字节,而我们这里是整形数组,每个元素的类型都是int,都正好占有4个字节,这说明数组中的元素在内存中是连续存储的。

二、二维数组

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

1)二维数组的创建(定义)

如果一个数组中的所有元素都是数组,比如一个含两个元素的数组,每个元素都是含三个元素的数组,那么我们就可以创建出这样的数组:

int arr[2][3];

这样创建出来的数组就是二维数组,我们可以这样画图帮助理解:

举几个例子:

int arr1[10];
char arr2[5];
double arr3[6];

1)二维数组的初始化

二维数组的初始化和一维数组相似举例子:

int arr1[2][3] =  {0,1,2,3,4,5,6};
int arr2[3][4] = {0,1,2,3};
int arr3[3][4] = {{0,1}, {2,3},{4,5}};
int arr4[][3] = {{0,1}, {2,3}};

你肯定又会有疑惑了,现在我们继续一一解释
二维数组呢也有完全初始化和不完全初始化,完全初始化呢就是把数据依次填入每个格子里像这样:

不完全初始化呢,就是没填上的格子里全部填上0;以上图为例,你如果你想值在第一行填两个数,第二行填两个数,第三行填两个数,那就可以这样写:

int arr3[3][4] = {{0,1}, {2,3},{4,5}};

没填的部分默认为0.填完之后是这样的:

当然对于二维数组,[ ]里的数字也还可以省略的,但是!!只有代表“行”的数字可以省略,代表“列”的[ ]可不能省啊;
因为代表行的,可以根据你初始化的元素个数推导出来,但是你把每一列有几个元素省略了,你怎么判断有几行几列?
所以

int arr[][3] = {0,1,2,3,4};

是正确的,省略的[ ] 里的数字就是2;

int arr[][] = {0,1,2,3,4,5};
int arr[3][] = {0,1,2,3,4,5};

是错误的。

1.二维数组的使用

二维数组的使用也会使用下标的方式,你可以理解成线性代数里的行列式,只不过元素的下标是从0开始的:

访问元素的时候就和二维坐标系里找数字一样,给我行坐标和列坐标,我就能找到对应的元素是谁;

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

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

那么二维数组元素在内存中是怎样存储的呢?是和我们画出来的一样,同一行是连续的而不同行之间貌似不在一块呢?
我们以码为证:

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


仔细看看发现,啊,原来所有元素都是差四个字节,也就是所有元素都是连续存储的,和一位数组无异。

三、数组越界问题

我们知道,当我们创建数组的时候,电脑会根据其总大小来分配内存空间,比如int arr[3][4];就会调用来3*4*4这么多个字节来分配给这个数组,那万一某一天,我写出来了一个arr[2][4] = 0;我用到了这个数组之外的元素,编译器就会报错了。
因为你没有访问这些内存的权力,擅自使用了没有给你分配的内存,内存肯定就不同意了。而这种情况就叫做越界。
一半对于数组arr[a][b],最多只能访问到arr[a-1][b-1](因为下标是从0开始的)最小只能是arr[0][0],千万不能越界。

四、数组作为函数的参数

我们在调用函数的时候,如果要用到一个数组作为一个参数的时候怎么办呢?比如下面的问题:
使用冒泡排序函数来为一系列数字排序。按照经验哈我们应该第一次会写出这样的代码。

#include <stdio.h>
void bubble_sort(int arr[])
{int sz = sizeof(arr) / sizeof(arr[0]);int i, j;//接下来进行冒泡排序for (i = 0; i < sz - 1; i++){for (j = 0; j < sz - i - 1; j++){if (arr[j] > arr[j + 1]){int tmp = arr[j];arr[j] = arr[j + 1];arr[j + 1] = tmp;}}}
}
int main()
{int arr[] = { 7,4,6,1,4,3,9,0,2,8 };bubble_sort(arr);int i;for (i = 0; i < sizeof(arr) / sizeof(arr[0]); i++){printf("%d ", arr[i]);}return 0;
}

然后你满怀信心的点击“运行”,一看结果,傻眼了:

发现怎么就改变了一个元素啊??
我们(Fn +) F10来调试一下,记得运行到函数的时候按F11而不是继续按F10,因为我们要进入函数内部:
运行到这里的时候发现出现了问题

怎么算出来的sz是2呀(在X64平台下是2,在X86平台下是1)?按道理来说不应该是10吗?
我们通过这样的观察发现:
在64位平台上sizeof(arr)是8,在32位平台上sizeof(arr)是4。这个让我们联想到指针,指针的大小适合平台有关的,而且大小对应的正好是8和4.
难道说,我们传递的参数是一个地址而非整个数组?

bubble_sort(arr);

数组名arr是一个地址?

数组名是啥?
我们根据前面的内容大胆猜测:数组名就是地址
所以我们这样写代码来检测我们的猜测:

#include <stdio.h>
int main()
{int arr[10];printf("%p\n", arr);printf("%p\n", &arr[0]);return 0;
}

结果是这样:

地址是一模一样啊,也就是说,数组名就是数组首个元素的地址。但是如果数组名就是首元素的地址,那么为啥子sizeof(数组名)算出来的不是地址这个指针的大小而是整个数组的大小呢?
在这里要注意一下,数组名是首元素地址又两个例外情况:

1.在sizeof加上数组名的时候,数组名表示数组整体
2.在数组名前面加上取地址符号,数组名表示数组整体

所以我们就知道,函数传参的时候,传递的数组其实没有把数组整体传过去,而只传递了数组首元素的地址。其实也很好理解,因为传参的时候,每个参数都会创建一份临时拷贝,如果是传递一个数组,那么给形参开辟的空间就太大了。

那么问题来了,我们要怎么改变上面这个冒泡排序代码从而完成要求呢?

既然我们没办法在函数内部算,那我们可以把数组元素个数也当做一个参量带入进去。

这样改:

#include <stdio.h>
void bubble_sort(int arr[],int sz)
{int i, j;//接下来进行冒泡排序for (i = 0; i < sz - 1; i++){for (j = 0; j < sz - i - 1; j++){if (arr[j] > arr[j + 1]){int tmp = arr[j];arr[j] = arr[j + 1];arr[j + 1] = tmp;}}}
}
int main()
{int arr[] = { 7,4,6,1,4,3,9,0,2,8 };int sz = sizeof(arr) / sizeof(arr[0]);bubble_sort(arr,sz);int i;for (i = 0; i < sizeof(arr) / sizeof(arr[0]); i++){printf("%d ", arr[i]);}return 0;
}

结果:

诶嘿,这样就没问题了。


总结

到这里,我们对数组初阶的介绍就完成了,而二维数组的学习,让我们可以从直线操作升级成为平面操作,而很多游戏就是平面上进行的,比如三子棋啊,扫雷啊等等等。
学到这里我们对C语言基本语法大概都有个了解了,所以接下来,我们就可以结合之前所学,开始制作一些很简单,但做起来很有成就感的小游戏。欲知后事如何,请看下回分解~~

C语言学习——从零开始学编程(第五篇:数组)相关推荐

  1. C语言学习——从零开始学编程(第二篇:C语言知识总括)

    本节将介绍:C语言的基本框架和大部分知识的浅了解(记得先看目录哦~~) 前言--小颖的话 提示:本文篇幅长,知识点多,可以分开学习,记得收藏点赞哦~ 一.数据类型 1.C语言中的关键字 2.不同类型所 ...

  2. C语言学习——从零开始学编程(第一篇:Visual Studio的下载和第一个C语言程序)

    小颖的话: 也许有很多的小伙伴们和小颖一样,曾在玩着马里奥,贪吃蛇,或者打着植物打僵尸的时候想着这些游戏是怎么运作的❓

  3. C语言学习——从零开始学编程(第四篇:函数)

    目录 前言--小颖的话 一.函数的概念 二.C语言中函数的分类 1.库函数 1.自定义函数 1)函数名 2)函数返回值 3)函数参数 4)实现几个简单函数 三.函数的形参与实参 1.实际参数 2.形式 ...

  4. C语言学习——从零开始学编程(第三篇:选择与循环)

    文章目录 前言--小颖的话 一.语句 1)C语言中的语句有哪些 2)语句 二.选择语句 1) if选择语句 1. if语句 2.if-else语句 3.if - else if - else 多分支语 ...

  5. 从零开始学产品第五篇:三个环境,开发、测试和线上

    本篇为[从零开始学产品]系列课第1章第4节 欢迎到公众号菜单栏,获取产品经理课程更多资料 上节课我们说到了,Bug的生命周期,而只有在测试环境和线上环境发现的Bug,才会被称之为Bug. 倒底什么是测 ...

  6. python从0开始学编程课件_小白从零开始学编程(三)--python基本概念

    前言 从2020年3月份开始,计划写一系列文档--<小白从零开始学编程>,记录自己从0开始学习的一些东西. 第一个系列:python,计划从安装.环境搭建.基本语法.到利用Django和F ...

  7. 《Python深度学习从零开始学》简介

    #好书推荐##好书奇遇季#深度学习入门书<Python深度学习从零开始学>,京东当当天猫都有发售.从模型和实验入手,快速掌握深度学习技术. 业内大咖强力推荐!!!武汉大学信息管理学院教授 ...

  8. 学计算机个人心得体会,学习计算机心得体会范文五篇

    学习计算机心得体会范文五篇 当我们受到启发,对生活有了新的感悟时,写心得体会是一个不错的选择,这样可以记录我们的思想活动.那么写心得体会要注意的内容有什么呢?以下是小编精心整理的学习计算机心得体会范文 ...

  9. Datawhale分组学习—动手学数据分析(五)

    Datawhale分组学习-动手学数据分析(五)主要是做数据建模以及模型评估.模型搭建部分:1)切分数据集为训练集和测试集:2)搭建逻辑回归模型或随机森林模型完成分类任务,通过不断调参来优化模型.模型 ...

最新文章

  1. 查看linux主机是否安装宋体码,Linux 安装宋体字体的简单办法
  2. 【 Notes 】RFID Preliminary Introduction
  3. 【Python】牛客的输入输出到底怎么整??
  4. Objects.requireNonNull 方法说明
  5. C#学习记录1——Hello World! 补充
  6. 【阿圆总结】关于平时阅读的推荐
  7. Mybatis详细教程
  8. pta编程题c语言答案,PTA选择题答案_pta题库答案,pta答案
  9. SqlCommandBuilder 批量更新数据库的怪异问题?
  10. 基于遗传算法的柔性车间调度优化研究附Matlab代码
  11. 工具分享:pycharm-2019.1_windows正版最新(附下载链接)
  12. 红牛整装待发,功能饮料市场地位不可撼动
  13. 我们进入微服务世界的旅程-以及我们从中学到的东西。
  14. Python中的“鸭子形态”,浅谈一下
  15. win7产生大量evtx文件_Windows XML Event Log (EVTX)单条日志清除(四)——通过注入获取日志文件句柄删除当前系统单条日志记录...
  16. 大牛deep learning集合
  17. 虚荣指标 探索性指标 4种_为什么您应该避免使用虚荣指标并衡量重要问题
  18. (三)基于Multisim的电台发射系统:高频功率放大器的设计
  19. RK3288 添加backlight lcd接口
  20. GIS就业参考系列之技术篇——七种武器

热门文章

  1. 使用java运行Python脚本并获取返回值
  2. 软件项目管理实验报告(一)
  3. 247个Python经典有趣实例,185页代码齐全可复制,PDF版拿走即用
  4. 研发部 svn git中的的项目管理目录
  5. 蚂蚁集团2022-09-15笔试
  6. iview@2.13.0/1版本,Table组件IE9不兼容的问题
  7. 从源码分析TreeSet集合(树集)
  8. Parsa‘s Humongous Tree(贪心+树形dp)
  9. Stata数据处理:各种求和方式一览
  10. 基于MATLAB的电磁波垂直入射理想介质演示程序(电磁学仿真)