IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> 初入C语言_5 -> 正文阅读

[C++知识库]初入C语言_5

  1. 数组

我们又回归了C语言的学习,今天我们要进行的内容是对数组进一步的讲解,想必大家在完成扫雷和三子棋游戏的时候就对数组有点印象,很抱歉在那时就先给大家讲了,作为任何编程语言当中都不可或缺的东西肯定是要给大家专门开一篇博客讲解的,今天讲的内容都是C语言当中的重点,希望大家不要跳,不要囫囵吞枣,话不多说我们开始吧。

那么什么是数组呢?从名字我们不难发现,数组就是一组数,好像一组数字放在一起等别人来用,八九不离十就是这个意思,所谓数组就是在内存上连续存放的一组数字,创建方式和创建变量类似,类型+名称[];

一、一维数组

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

数组是一组相同类型元素的集合。 数组的创建方式:

type_t   arr_name   [const_n];
//type_t 是指数组的元素类型
//const_n 是一个常量表达式,用来指定数组的大小

//type_t就是数组的类型,arr_name就是你为数组定义的名称,const_n就是为数组初始化的个数
//例如:
//代码1
int arr1[10];
//代码2
int count = 10;
int arr2[count];//数组时候可以正常创建?
//代码3
char arr3[10];
float arr4[1];
double arr5[20];

注意:初始化数组的时候并不可以用变量代替,[]内只能用常量,在之前的扫雷和三子棋游戏当中大家应该注意到了那里面定义的数组里面放的是#define定义的,这也是可以的,由于我为#define后面的变量赋初值了,所以可以作为数组初始化的个数。

看完数组的初始化和定义了,我来问大家一个问题,我可不可以不初始化[]里的值,如果不给[]放常量,它会给我创建一个多大的数组呢?会不会报错呢?我来告诉大家:不会报错,这是符合C语言语法的,如果不在数组的方括号里赋初值,为了使C语言更加人性化,意味着你所定义的数组的大小会根据后面元素的个数来变化,例如:

int arr1[10] = {1,2,3};
int arr2[] = {1,2,3,4};//四个元素
int arr3[5] = {1,2,3,4,5};
char arr4[3] = {'a',98, 'c'};
char arr5[] = {'a','b','c'};//三个元素
char arr6[] = "abcdef";//6个元素

但是建议大家不要养成定义数组不初始化个数的习惯,自己定义的数组有多大自己还是心里要有数的。

我们现在知道了int arr1[3]={1,2,3};是定义了一个有三个int元素的数组,char arr2[5]={"abcde"};是定义了一个char类型的数组,数组里放着五个元素,那么如果我这样给呢:char arr3[]={'a','b','c'};我们来分析一下,数组是char类型的没错,里面又三个元素也没错,但是有没有发现arr3和arr2有些许不同,一个放了一个字符串,一个是一个一个放的,有何区别呢?C语言当中这样规定:当一个数组定义完成之后,会默认在数组的最后一个元素后放上一个\0。那么这个\0在ascci码当中对应的值就刚好为0,比如你在计算数组里有多少个元素,当数到最后时遇到\0了,就会自动停止,所以可以把\0当作一个停止符号,所以大家可以把arr2和arr3放到自己的编译器上运行一下,看一下这两个数组里有多少个元素,arr2是要比arr3多一个\0的,因为arr3在定义时是挨个字符存放的,并没有放\0,所以arr2和arr3最大的区别就在于程序员想不想把\0放进去,如果无所谓,就用arr2,如果不想放\0,就用arr3。

2.一维数组的使用

我们讲了数组的定义和初始化后,数组究竟要怎么用呢?我们现在就来写一段代码,来打印1~10。

#include <stdio.h>
int main()
{
     int arr[10] = {0};//数组的不完全初始化,此时arr数组里的10个元素都为0
     //计算数组的元素个数
     int sz = sizeof(arr)/sizeof(arr[0]);
     //对数组内容赋值,数组是使用下标来访问的,下标从0开始。所以:
     int i = 0;//做下标
     for(i=0; i<10; i++)//这里写10,好不好?
     {
         arr[i] = i;
     } 
     //输出数组的内容
     for(i=0; i<10; ++i)
     {
         printf("%d ", arr[i]);
     }
     return 0;
}

大家可能有些疑问,为什么i要从0开始?并且还采用左闭右开,因为数组的下标永远是从0开始,意思就是七个元素的数组,它的下标就是0~6,数组的下标永远比我们数的少1,所以这样定义i的取值就是0~9,就可以初始化数组里十个元素分别为0~9。利用循环再把arr里的内容都打印出来。

数组个数的算法:大家能发现这段代码里有这么一行:int sz = sizeof(arr)/sizeof(arr[0]);这就是计算int数组有多少个元素的方法, sizeof是一个操作符,我们下文会讲,其作用就是计算大小,在sizeof里放数组名就代表要计算整个数组的大小,再除arr数组任意一个元素的大小,(这里以arr[0]为例)因为数组有十个元素,每个元素又是int类型,所以整个数组大小就是10*4=40字节,一个元素的大小就是4字节,所以得数就是10,即数组元素的大小。(注意:利用sizeof(arr)/sizeof(arr[0])只能计算int型数组的元素个数,如果想计算char类型数组的元素个数,要利用strlen函数。)

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

我们提到数组在内存中都是连续存放的,每个元素都是紧挨着,这样才有利于我们既快速又安全地拿到我们想到的元素,那么它再内存中到底是怎么样存储的呢?我们接下来就研究一下:

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

%p是以地址的形式打印,即打印数组每个元素在内存中的地址,那这段代码运行后结果是什么呢?

?这里要告诉大家,地址都是以16进制形式存储的,0~9,A~F所以地址中才会有这么多字母,大家先来看9C-A0,我们知道C是代表12,加四后等于十六,按照十六进制正好进一,九进一正好就是十,也就是A,所以我们可以发现arr[0]、arr[1]是进了四,以此类推,每个元素也都是进4,和我们刚才讲的数组每个元素在内存中都是占用四个字节吻合,所以我们发现了数组在内存当中都是以四个字节为一元素的方式一次排列,元素的地址,也在有规律的递增。

?

?二、二维数组

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

学会了一维数组,我们已经对数组有了一个大概的认识,在C语言当中还有另外一种数组,二维数组,如果说一维数组是一条线的话,二维数组就是一个面,我们之前的扫雷和三子棋游戏也都是用二维数组来定义的,看似一个个小网格,由于有行也有列,所以叫二维,我们还是从定义和初始化来讲:

二维数组如何创建呢?我想这个问题不用我来说大家也都能想到,一维数组怎样创建二维数组也怎样搞呗,长*宽等于面积,只要给定长度和宽度就可以创建,没错,我们来看几个:

int arr[3][4];
char arr[3][5];
double arr[2][4];

这几个二维数组就在内存中创建了行为三列为四,行为三列为五,行为二列为四的网格,是不是很简单呢?有同学问了:既然一维数组和二维数组很像,那定义的时候可不可以也不写初始值呢?根据我给定的个数确定元素有多少。注意:行可省略,列不可省略,也就是说我们在创建二维数组时,必须要写列,这样编译器会自动根据列来计算行,为什么不可以反过来这我也不知道,可能是龟腚吧,不过一般来说也不会省略的,相信大家都是勤奋的程序员。

//给大家几个创建二维数组的例子
int arr[3][4] = {1,2,3,4};
//注意:{}内加{}是为了区分有几行,如果不写的话编译器会自动根据行数计算
int arr[3][4] = {{1,2},{4,5}};
//这个二维数组只会初始化两行,
//整个第三行和前两行的第四列的元素会自动初始化为0
int arr[][4] = {{2,3},{4,5}};
//这里就会自动初始话行数为:2,因为有两个{},每行放两个元素,每列也放两个元素,其余为0

2.二维数组的使用

二维数组和一维数组的使用方法是一样的,其行和列的下标也是遵循-1,也就是说二维数组也是从下标0开始,到元素-1的下标,依照一维数组打印的代码我们写一段在二维数组内放数据打印数据的代码:

#include <stdio.h>
int main()
{
     int arr[3][4] = {0};
     int i = 0;
     for(i=0; i<3; i++)
     {
         int j = 0;
         for(j=0; j<4; j++)
         {
            arr[i][j] = i*4+j;//由于是二维数组,所以必须要用循环套循环
         }
     }
     for(i=0; i<3; i++)
     {
         int j = 0;
         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 = 0;
     for(i=0; i<3; i++)
     {
         int j = 0;
         for(j=0; j<4; j++)
         {
             printf("&arr[%d][%d] = %p\n", i, j,&arr[i][j]);
         }
     }
     return 0;
}

结果如下:

按照上面我们计算一维数组地址的方法计算一下这里的二维数组,48-4C,因为我们定义的也是int类型,所以8+4=12,在十六进制里是C,C+4=0,0+4=4,正好符合我们的计算,而第二行也是紧挨着第一行来计算的,所以我们可以放心记这条结论:二维数组在内存中也是连续存放的。

?三、数组作为函数参数

我们在封装函数的过程中少不了形参实参,也少不了数组的传递,那么数组作为形参在传递的过程中是如何进行的呢?以冒泡排序为例,函数将一个整形数组排序。 那我们将会这样使用该函数:

//方法1:
#include <stdio.h>
void bubble_sort(int arr[])
{
 int sz = sizeof(arr)/sizeof(arr[0]);//这样对吗?
 int i = 0;
 for(i=0; i<sz-1; i++)
   {
        int j = 0;
        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[] = {3,1,7,5,8,9,0,2,4,6};
    bubble_sort(arr);//是否可以正常排序?
    for(i=0; i<sizeof(arr)/sizeof(arr[0]); i++)
    {
        printf("%d ", arr[i]);
    }
    return 0;
}

大家可以把这段代码放到自己的编译器上运行一下, 这是一段错误代码,那么问题出在哪里呢?大家可以使用vs的调试功能:ctrl+F11,进入到代码当中就可以发现,我们计算的sz是1,唉?我明明初始化的是10,为什么sz是1呢?肯定是其中的计算环节出问题了,那我们就可以追溯到数组传参的时候传入的难道只有1个元素?4/4=1所以我猜应该是传参的时候只传入了一个元素,那我们究竟该如何传参呢?在研究此问题之前,我们先来介绍一下数组名这个知识:

数组名是什么?

#include <stdio.h>
int main()
{
    int arr[10] = {1,2,3,4,5};
    printf("%p\n", arr);
    printf("%p\n", &arr[0]);
    printf("%d\n", *arr);
    //输出结果
    return 0;
}

这段代码运行完大家会发现第一行打印和第二行打印的结果一样,第三行打印的结果是第一个元素的值,我们就可得出结论:数组名是数组首元素的地址。但是回过头来我们来看这个:sizeof(arr)为什么计算的值是40?这里有两个例外:

1. sizeof(数组名),计算整个数组的大小,sizeof内部单独放一个数组名,数组名表示整个数 组。 2. &数组名,取出的是数组的地址。&数组名,数组名表示整个数组。

除了这两种情况,其余遇到数组名一律是首元素地址,那刚才那段代码第一行和第二行结果一样也就没什么疑问了。

解决完这个问题后,那么冒泡排序究竟该怎么设计呢?

//方法2
void bubble_sort(int arr[], int sz)//参数接收数组元素个数
{
 //...
}
int main()
{
    int arr[] = {3,1,7,5,8,9,0,2,4,6};
    int sz = sizeof(arr)/sizeof(arr[0]);
    bubble_sort(arr, sz);//是否可以正常排序?
    for(i=0; i<sz; i++)
   {
        printf("%d ", arr[i]);
   }
    return 0;
}

比起刚才的方法一,我们只需要在调用冒泡排序函数时,先计算出数组元素的个数然后传参进去就可以了。

好了,今天的C语言学习到此先结束,大家休息一下,我们下次再见。

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2021-10-02 14:26:18  更:2021-10-02 14:28:14 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年12日历 -2024/12/29 4:09:14-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码
数据统计