【第七节】C语言常见的数组

目录

前言

一、数组的概述

二、一维数组

2.1 一维数组的定义

2.2 一维数组元素的引用

2.3 一维数组初始化

2.4 一维数组的存储

2.5 一维数组的越界问题

2.6 字符数组

2.7 字符数组的输入输出

2.8 字符数组与字符串

2.8.1 两者的区别

2.8.2 字符串一些常见函数

三、二维数组

3.1 二维数组的定义

3.2 二维数组的初始化

3.3 二维数组的引用

3.4 二维数组的内存存放顺序


前言

        数组是C语言中一个非常基础和重要的数据结构。数组的起源可以追溯到早期的计算机编程语言,如Fortran和Algol。这些语言在设计和实现过程中,为了解决如何高效地存储和访问一组相同类型的数据,引入了数组这个概念。

        数组的作用主要是为了提高代码效率和可读性。通过将同类型的数据存储在连续的内存空间中,数组可以提供一种简单且直观的方式来组织和处理一组数据。使用数组的索引,我们可以快速地访问和操作数组中的特定元素,而不需要为每个元素单独分配内存空间。

        在C语言中,数组的使用非常普遍,可以用于处理各种问题,如排序、查找、数学计算等。同时,数组也经常被用于存储和操作复杂的数据结构,如多维数组和稀疏矩阵等。


一、数组的概述

        在程序设计中,为了方便处理数据把具有相同类型的若干变量按有序形式组织起来——称为数组。
        数组就是在内存中连续的相同类型的变量空间。同一个数组所有的成员都是相同的数据类型,同时所有的成员在内存中的地址是连续的。

数组属于构造数据类型:
1)一个数组可以分解为多个数组元素:这些数组元素可以是基本数据类型或构造类型。
int a[10];  
struct Stu boy[10];

2)按数组元素类型的不同,数组可分为:数值数组、字符数组、指针数组、结构数组等类别。
int a[10];
char s[10];
char *p[10];

        通常情况下,数组元素下标的个数也称为维数,根据维数的不同,可将数组分为一维数组、二维数组、三维数组、四维数组等。通常情况下,我们将二维及以上的数组称为多维数组。

        试想一下这样的使用场景,我们想在程序中存储一头猪的重量,存储一个的时候可以定义一个变量,存储 10头猪的重量,可以定义 10 个变量,存储 100 ,1000头呢?如果一个一个变量的定义,恐怕时间都不够用。那么这种情况我们可以采用数组。

二、一维数组

2.1 一维数组的定义

数组定义的语法:
数据类型 数组名[数组长度];

例如:  int arr[10];


1 数组名的命名规则和变量名相同,遵循标识符命名规则。
2数组名后接一个或多个方括号,用以指定数组的维数(元素个数)
3数组长度只能是常量和常量表达式 (大于0),不能是变量。
注:(c99 标准中规定允许使用变量,但vs 编译器并不支持,因为不符合c++标准)。

2.2 一维数组元素的引用

数组可以通过下标来访问某一个元素,下标是具有整数值的表达式,也就是说下标可以是:
1 常量
2 变量
3 其他有整数值的表达式

数组元素的表示形式为:
数组名[下标]
例: arr[3],arr[i + 1]。
注意:通过下标对每个数组元素的使用与普通变量一样。数组下表从0开始,故而像 int nTemp[10]这样的数组,下标最大到 9。

代码示例如下

#include <stdio.h>

int main()
{
    int nNum = 10;
    //int Arr[nNum];   //定义时长度不支持变量
    int Arr1[10];  //定义一个整型数据的数组,元素有 10 个
    int Arr2[10]={10, 25, 50, 1, 2, 9, 4, 5, 36, 15};  //初始化

    int num = 0;
    //使用数组下标访问数组
    printf(“%d\n", Arr2[num]); //我们使用数组的时候,下标可以是变量
    num++;
    printf("%d\n", Arr2[num]);
    num++;
    printf("%d\n", Arr2[num]);
    return 0;
}

使用数组名和下标来访问数组中的每一个元素,数组中的某一个元素在使用的时候,和普通的变量没有任何区别。

2.3 一维数组初始化

在数组定义的时候,给每一个元素初值,称之为初始化,通常有以下初始化的情况:
1.整体赋值
int ary[5] ={1,2,3,4,5};
2.部分赋值
int arr[5]={1,2,3};//还有最后两个元素为 0。
3.不给定数组长度 根据实际数组元素个数分配
int arrl[] ={1,2,3,4,5,6); //共有6 个元素,所以实际分配 24 个字节空间

#include <stdio.h>

int main()
{
    int nNum = 10;
    int Arr1[10]; //定义一个整型数组,元素有 10 个
    int Arr2[10] = {10, 30, 50, 1, 2, 3, 4, 5, 9, 15};//初始化
    int Arr3[] = {1, 2, 3, 4, 5}; //正确的,数组长度可以省略
    //下面都是常见的错误情况
    //int Arr4[]; //错误的,没有初始化的时候,数组长度不能省略。
    //int Arr3[nNum]; //错误的,数组长度必须是常量
    //Arr1 = Arr2; //错误的,数组不能整体赋值
    //Arrl = {10, 30, 50, 1, 2, 3, 4, 5, 9, 15}; //错误的,数组不能整体赋值
    //使用数组元素和使用变量一致
    scanf_s("%d", &Arr1[3]);
    printf("%d", Arrl[3]);
    return 0;
}

注意:在定义数组的时候给值叫做初始化,之后的时候叫做赋值。

2.4 一维数组的存储

数组在内存中的存储是连续的。


例:一个包含 11个整型元素的数组
int arrTem[11]; //11 表示的有 11个元素,11是数组长度,而不是数组的下标
arrTem[0],arrTem[1],arrTem[2]....arrTem[10]; //数组下标从0开始,所以最大下标为 10。


注:在内存中用连续的空间存储,每个元素占 4 个字节。
定义数组的时候,数组长度必须是一个常量,使用数组的时候下标既可以是一个常量也可以是一个变量。

2.5 一维数组的越界问题

        使用数组的时候,假如不小心下标超过了最大界限会发生什么?这种情况,编译器是不会检查的,称之为数组的越界问题。这种情况非常的危险,会破坏程序的内存。造成程序间歇性的不稳定。(就是程序运行有可能会出错,有可能不会出错)。下面是举例代码。

#include <stdio.h>

int main() {
    int arr[5]; // 定义一个长度为5的整型数组
    int i;

    arr[0] = 0;
    printf("%d ", arr[0]);
    arr[1] = 1;
    printf("%d ", arr[1]);
    arr[2] = 2;
    printf("%d ", arr[2]);
    arr[3] = 3;
    printf("%d ", arr[3]);
    arr[4] = 4;
    printf("%d ", arr[4]);
    // 越界访问数组元素
    arr[5] = 5;
    printf("%d ", arr[5]);

    return 0;
}

2.6 字符数组

        字符数组是一种比较特殊的数组,主要体现在它的初始化方面以及存储字符串时候的使用方面。

#include <stdio.h>

int main()
{
    char aryl[] = {'H','e','l','l','o','j','e','r','y'}; //正常用法和之前类似
    char ary2[] = "Hellojery"; //字符数组的特殊用法,可以用一个字符串初始化数组

    printf("%d\n", sizeof(ary1)); //打印9
    printf(“%d\n", sizeof(ary2)); //打印 10 因为字符串最后有一个’0’。


    printf("%s\n", ary2); //这里会正确的打印 Hellojery
    printf("%s\n", ary1); //这里不会正确的打印,会多打印一部分字符,因为遇到"\0"才结束。
    return 0;
}

        在 C语言中,字符串是以’\0’为结尾的,所有使用字符串的地方,都是检测字符串结尾的’\0’来判断字符串是不是结束了。
注意:字符串的结尾是一个’\0’,这个字符的ASCII码是0。而字符 0的ASCII码是 0x30。

2.7 字符数组的输入输出

        数组名其实就是数组的起始地址,是一个常量,这个概念可能小白第一次接触到。这种特性也使得数组在内存中占据连续的空间。当我们创建一个数组时,系统会为它分配一块连续的内存空间,而这块空间的起始地址就是数组名所代表的地址。因此,如果我们知道一个数组的起始地址和每个元素的大小,就可以通过简单的计算得到任意元素的地址。

#include <stdio.h>
int main(){
    char string[10];
    scanf_s("%s", string, 10); //会检测输入的字符个数是不是超过了第三个参数
    //scanf("%s", string); //越界是不会检测的
    //为什么 scanf中string 不用取地址呢?因为数组名就是首地址,在此可以得到结论。
    printf("%x", string);
    return 0;
}

2.8 字符数组与字符串

2.8.1 两者的区别

1)C语言中没有字符串这种数据类型,可以通过char的数组来替代;
2)字符串一定是一个char的数组,但char的数组未必是字符串;
3)数字0(和字符‘\0’等价)结尾的char数组就是一个字符串,但如果char数组没有以数字0结尾,那么就不是一个字符串,只是普通字符数组,所以字符串是一种特殊的char的数组。

#include <stdio.h>

int main()
{
	char c1[] = { 'c', ' ', 'p', 'r', 'o', 'g' }; //普通字符数组
	printf("c1 = %s\n", c1); //乱码,因为没有’\0’结束符

	//以‘\0’(‘\0’就是数字0)结尾的字符数组是字符串
	char c2[] = { 'c', ' ', 'p', 'r', 'o', 'g', '\0'}; 
	printf("c2 = %s\n", c2);

	//字符串处理以‘\0’(数字0)作为结束符,后面的'h', 'l', 'l', 'e', 'o'不会输出
	char c3[] = { 'c', ' ', 'p', 'r', 'o', 'g', '\0', 'h', 'l', 'l', 'e', 'o', '\0'};
	printf("c3 = %s\n", c3);

	return 0;
}

2.8.2 字符串一些常见函数

        字符串除了前面介绍的输入输出的函数,还有一些其他的常见函数,比如

strlen   求字符串的长度
strcpy/strcpy_s   字符串拷贝函数
strcmp    比较两个字符串是否一样
strcat/strcat_s  将两个字符串拼接到一起

1)strlen   函数

出处:

#include <string.h>

size_t strlen(const char *s);

功能:计算指定指定字符串s的长度,不包含字符串结束符‘\0’

参数:s:字符串首地址

返回值:字符串s的长度,size_t为unsigned int类型

示例:

char str[] = "abcdefg";
int n = strlen(str);
printf("n = %d\n", n);

2)strcpy 函数

出处:

#include <string.h>

char *strcpy(char *dest, const char *src);

功能:把src所指向的字符串复制到dest所指向的空间中,'\0'也会拷贝过去

参数

dest:目的字符串首地址

src:源字符首地址

返回值

成功:返回dest字符串的首地址

失败:NULL

注意:如果参数dest所指的内存空间不够大,可能会造成缓冲溢出的错误情况。

代码示例:

char dest[20] = "123456789";
char src[] = "hello world";
strcpy(dest, src);
printf("%s\n", dest);

3)strncpy 函数

出处:

#include <string.h>

char *strncpy(char *dest, const char *src, size_t n);

功能:把src指向字符串的前n个字符复制到dest所指向的空间中,是否拷贝结束符看指定的长度是否包含'\0'

参数

dest:目的字符串首地址

src:源字符首地址

n:指定需要拷贝字符串个数

返回值

成功:返回dest字符串的首地址

失败:NULL

代码示例:

char dest[20];
char src[] = "hello world";
strncpy(dest, src, 5);
printf("%s\n", dest);

dest[5] = '\0';
printf("%s\n", dest);

4) strcat 函数

出处:

#include <string.h>

char *strcat(char *dest, const char *src);

功能:将src字符串连接到dest的尾部,‘\0’也会追加过去

参数

dest:目的字符串首地址

src:源字符首地址

返回值

成功:返回dest字符串的首地址

失败:NULL

代码示例:

char str[20] = "123";
char *src = "hello world";
printf("%s\n", strcat(str, src));

5)strncat 函数

出处:

#include <string.h>

char *strncat(char *dest, const char *src, size_t n);

功能:将src字符串前n个字符连接到dest的尾部,‘\0’也会追加过去

参数

dest:目的字符串首地址

src:源字符首地址

n:指定需要追加字符串个数

返回值

成功:返回dest字符串的首地址

失败:NULL

代码示例:

char str[20] = "123";
char *src = "hello world";
printf("%s\n", strncat(str, src, 5));

6)strcmp 函数

出处:

#include <string.h>

int strcmp(const char *s1, const char *s2);

功能:比较 s1 和 s2 的大小比较的是字符ASCII码大小

参数

s1:字符串1首地址

s2:字符串2首地址

返回值

相等:0

大于:>0

小于:<0

代码示例:

char *str1 = "hello world";
char *str2 = "hello mike";

if (strcmp(str1, str2) == 0)
{
    printf("str1==str2\n");
}
else if (strcmp(str1, str2) > 0)
{
    printf("str1>str2\n");
}	
else
{
    printf("str1<str2\n");
}

7)strncmp 函数

出处:

#include <string.h>

int strncmp(const char *s1, const char *s2, size_t n);

功能:比较 s1 和 s2 前n个字符的大小比较的是字符ASCII码大小

参数

s1:字符串1首地址

s2:字符串2首地址

n:指定比较字符串的数量

返回值

相等:0

大于: > 0

小于: < 0

代码示例:

char *str1 = "hello world";
char *str2 = "hello mike";

if (strncmp(str1, str2, 5) == 0)
{
    printf("str1==str2\n");
}
else if (strcmp(str1, "hello world") > 0)
{
    printf("str1>str2\n");
}
else
{
    printf("str1<str2\n");
}

当然strcpy_s和strcat_s是后缀加“_s”的安全版本函数,使用示例如下

#include <stdio.h>

int main()
{
    char dest[20] = "123456789";
    char src[] = "hello world";
    strcpy(dest, src);
    printf("%s\n", dest);

    char cChar1[10] = "hello";
    char cChar2[20] = "hello";
    char cChar3[20] = "world";
    cChar3[5] = 0; //\0

    //求字符串长度 strlen
    printf("%d\n", sizeof(cChar1));
    printf("%d\n", sizeof(cChar2));
    printf("%d\n", strlen(cChar1));
    //cChar2[3] = 0;
    printf("%d\n", strlen(cChar2)); //注意:strlen 只检测0

    //将一个字符串拷贝到一块内存区域中:strcpy
    //strcpy(cChar2, cChar3);非安全版函数
    //strcpy_s(cChar2, 20, cChar3); //把后面的字符串拷贝到前面去

    //比较两个字符串是不是一样的: strcmp
    printf("%d\n", strcmp(cChar2, cChar3));
    //如果要是说,两个字符串完全一样,这个函数得到结果是0,否则就是非0。

    //拼接两个字符串strcat
    strcat_s(cChar1, 10, cChar3); //会报错,因为缓冲区大小不够
    strcat s(cChar2, 20, cChar3); //够了就不报错了,这就是安全函数的作用
    printf("%s", cChar1);
    return 0;
}

三、二维数组

        二维数组就是一种有行和列概念的数组,你可以把二维数组看成是一维数组的延伸,因为从内存的角度来看,他们是一样的。但是由于有了行和列的概念,在处理起一些问题来,更为便捷。

3.1 二维数组的定义

声明方式:
类型说明 符数组名[常量表达式 1][常量表达式2];

二维数组也可以看成是一个一维数组,它的每一个元素又是一个一维数组。

例如二维数组a[2][3],可以看作由2个一维数组a[0]、a[1]组成,这两个数组元素是包含3个整型数组元素。

如果是多维数组,以此类推

声明方式:类型标识符  n维数组名[元素个数1][元素个数2]…[元素个数n];

3.2 二维数组的初始化

1.分行给二维数组赋初值。
例:int arrTem[2][3] = {{1,2,3},{4,5,6}};//使用大扩号分别把每行标记出来

2.可以将所有数据写在一个花括号内,按数组排列的顺序对各元素赋初值。
例:int arrTem[2][3] = {1,2,3,4,5,6};

3.可以对部分元素赋初值。
例:
int arrTem[2][3] = {{1},{2,3}}; //分行方式初始化
int arrTem[2][3] = {1,2,3};  //整体方式初始化

注:这两种方式虽然都是给部分赋值,但是有区别的.
分行方式:至少每行都有值,不足的补 0。
整体方式: 将第一行赋值完后,再赋值后面的内容,有可能该部分值只给了第一行,后面的值都默认为0。

4. 如果对全部元素都赋初值(即提供全部初始数据),则定义数组时对第一维的长度可以不指定,但第二维的长度不能省。
例 1:
int arrTem[][5] = (1,2,3,4,5,6,7,8,9):
数组会自动根据元素实际的个数去开辟空间。一共有9个元素,每一行有 5 个元素,那么能够确定有两行,
第一行:1,2,3,4,5;
第二行:6,7,8,9,0;  //不足补0

例 2:
int arrTem[2][]={1,2,3,4,5,6);//这种方式是不行的
这种为什么就不行呢?因为我们只知道有两行,但每一行中有多少个元素不确定。可以是任何大小的元素个数

3.3 二维数组的引用

二维数组元素的使用方式和一维数组类似:
数组名[下标][下标]

1.下标可以是普通的数值
代码示例:
 

int Arry1[4][5] ={1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5};
printf("%d\n", Arry1[0][0]); //打印第一行的第一个元素:1
printf("%d\n", Arry1[0][1]); //打印第一行的第二个元素:2
printf("%d\n", Arry1[1][0]); //打印第二行的第一个元素:6


2.下标可以是整型表达式
代码示例:

arrTem[2-1][2* 2- 1]; //无论什么表达式,只要满足结果大于等于0

3.下标可以是变量

代码示例:

//二维数组的遍历
for (int i=0; i<4; i++){
    for(int j=0; j < 10; j++){
        printf("%d",Arry2[i][j]);
    }
    printf("\n");
}


什么是遍历?就是访问数组中的每一个元素,这种操作是数组中常见操作。

4. 数组元素在使用的时候和普通变量一样,可以出现在表达式中,也可以被赋值。
代码示例:

arrTem[1][2] = arrTem[2][3] / 2;


上述代码是将该下标中的元素值作除法后,再赋值给前面的元素。
注意:
下标值都是从0开始,注意应在已定义的数组大小的范围内。
常出现的错误代码示例:
 

int a[3][4];
a[3][4] = 3; //数组中没有该下标的元素。下标是从0 开始的。

3.4 二维数组的内存存放顺序

        比如定义了一个2行3列的数组,数组名为a其元素类型为整型,该数组的元素个数为2×3个。

        int a[2][3];

        二维数组a是按行进行存放的,先存放a[0]行,再存放a[1]行,并且每行有3个元素,也是依次存放的。

        二维数组在概念上是二维的,其下标在两个方向上变化,对其访问一般需要两个下标。在内存中并不存在二维数组,二维数组实际的硬件存储器是连续编址的,也就是说内存中只有一维数组,即放完一行之后顺次放入第二行,和一维数组存放方式是一样的。

  • 36
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

攻城狮7号

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值