首页 > 其他分享 >C语言——数组超详细版总结

C语言——数组超详细版总结

时间:2024-10-15 16:50:06浏览次数:11  
标签:总结 初始化 arr int 元素 C语言 数组 sizeof

目录

1 一维数组

1.1 一维数组的创建与初始化

1.2.1 一维数组的创建

1.2.2 一维数组的初始化

1.2 一维数组的访问

1.3 一维数组在内存中的存储

2 二维数组

2.1 二维数组的创建与初始化

2.1.1 二维数组的创建

2.2.2 二维数组的初始化

2.2 二维数组的访问

2.3 二维数组在内存中的存储

3 字符数组

3.1 字符数组的创建与初始化

3.2 字符数组在内存中的存储 

3.3 补充:字符串的两种定义方式

4 数组大小的计算

4.1 sizeof操作符

4.2 数组大小 & 元素个数 计算

5 指针和数组 & 数组名代表的意义

6 数组传参

7 指针数组

8 数组指针


【数组的定义】:一组相同类型元素的集合。

【数组的下标】:数组每个元素都有对应下标,下标从0开始。

1 一维数组

1.1 一维数组的创建与初始化

1.2.1 一维数组的创建

type_t arr_name [const_n]
//即:元素类型 数组名 [数组大小]

//示例:
int arr[10];
char ch[20];

【注意点】

const_n通常是常量表达式,即:

        int n = 10;

        int arr[n];  //这种方式不能正常创建

但C99标准中引入了变长数组(数组长度可以通过变量确定)的概念,创建时可以使用变量,但这样的数组不能初始化。

这种情况也可以使用宏定义的方式:

        #define N 10;

        int arr[N];

1.2.2 一维数组的初始化

(1)完全初始化:数组中的所有元素在定义时都被指定了具体的值。

① 元素个数确定,元素值确定

int arr1[5] = {1, 2, 3, 4, 5}; 
char ch1[5] = {'a', 'b', 'c', 'd', 'e'}; 

② 只指定元素值

int arr[] = {1, 2, 3};
//数组大小为3

char ch1[] = {'a', 'b', 'c', 'd', 'e'}; 
//数组大小为5

char ch2[] = "abcde"
//数组大小为6,包含:abcde和\0

此时:arr为一个包含3个整形元素的数组,数组元素分别是:1,2,3。

因此,省略数组大小时必须初始化数组内容,因为这种情况下数组大小根据初始化内容确定!!!

关于字符数组详细内容请看第3章~~~

③ 只指定元素个数

int arr[10] = {0};

此时:arr为一个包含10个整形元素的数组,数组元素都被初始化为0。 

(2)不完全初始化

① 只确定部分数组内容

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

此时: arr前3个元素分别是:1,2,3,后面的2个元素为0。

【注意区分 int arr[5] = {1} 和 int arr[5] = {0}】

int arr[5] = {1}; 此时arr为一个包含5个整形元素,第一个元素为1,其余为0;

int arr[5] = {0}; 此时arr为一个包含5个整形元素,元素值全为0;

char ch1[5] = {'a', 'b', 'c'};
char ch2[5] = "abc";

此时:ch1前3个元素分别是:a b c,后面2个元素为'\0';ch2前3个元素分别是a b c,后面2个元素为'\0'。

关于字符数组详细内容请看第3章~~~

小结:整型数组不完全初始化会将未定义位置值初始化为0;字符数组不完全初始化会将未定义位置值初始化为\0。

1.2 一维数组的访问

数组通过 下标引用操作符[] 来访问内容,注意下标从0开始!

int arr[5] = {1, 2, 3, 4, 5};
printf("%d", arr[2]);   //打印3

1.3 一维数组在内存中的存储

一维数组在内存中是连续存放的,地址由低到高变化。

【数组越界访问】 

数组下标有范围限制,如果有那个元素,那么最后一个元素对应下表(n-1),如果访问下标超出0~(n-1)就产生越界访问。这种情况编译器不一定报错,但不意味着程序是对的。

2 二维数组

2.1 二维数组的创建与初始化

2.1.1 二维数组的创建

int arr[3][4];  //创建3行4列的整型数组
char arr[3][4];
double arr[3][4];

2.2.2 二维数组的初始化

(1)完全初始化:数组大小确定,元素值确定

int arr[4][5] = { {1, 2, 3, 4, 5}, {2, 3, 4, 5, 6}, {3, 4, 5, 6, 7}, {4, 5, 6, 7, 8} };
//创建4行5列整型数组

 (2)不完全初始化

int arr[4][5] = { {1, 2}, {2, 3, 4}, {3, 4, 5, 6, 7}, {4, 5, 6, 7, 8} };
int arr[][5] = { {1}, {2, 3, 4}, {3, 4}, {4, 5, 6, 7, 8} };

二维数组不完全初始化时:未定义位置会初始化为0

注意:二维数组可以省略行数,不能省略列数!!!

(3)其他初始化方式

int arr[4][5] = {1,2,3,4,5,6,7,8,9,10};

编译器会按照指定数组大小将已定义的内容填充进去,剩余位置填充为0;

所以此时arr,第一行元素为:1 2 3 4 5,第二行元素为:6 7 8 9 10,剩余两行元素全为0。

注意:这种初始化也是“可以省略行,不可以省略列”!

2.2 二维数组的访问

二维数组也通过 操作符[] 访问元素内容,注意:行列下标都从0开始!

2.3 二维数组在内存中的存储

二维数组在内存中也是连续存放的,地址由低到高变化。

二维数组可以理解为:存放 “一维数组” 的数组。 

3 字符数组

3.1 字符数组的创建与初始化

  • 字符数组的创建
char ch[10];  //创建一个可以存储10个字符的字符数组
  •  字符数组的初始化

 (1)字符串值直接初始化

char ch1[] = "abc";
//ch1中存放元素:a b c \0;

char ch2[4] = "abc";
//ch2中存放元素:a b c \0;

char ch3[3] = "abc";
//ch3中存放元素:a b c ;

在长度定义准确或未定义长度时,字符串末尾会加上'\0'。

(2) 逐个字符初始化

char ch1[] = {'a', 'b', 'c'};
//ch1中存放元素:a b c;

char ch2[] = {'a', 98, 'c'};
//ch2中存放元素:a b c;
//(ch3中将整数98用于初始化字符数组,此时98会被解释为字符值,而98对应ASCII为b,因此此处ch3[1]初始化为b。)
//这种情况下虽然会发生转换,但不建议这么定义!

char ch3[] = {'a', 'b', 'c', '\0'}

 小结:字符串值直接初始化,通常末尾会有'\0';逐个字符初始化,如果不特意添加则不会有'\0'。

【字符串的结尾】:C语言中,字符串以 '\0'(空字符)作为结束标志。 

C语言中很多库函数(如:printf、strlen......)在处理字符串时依赖 '\0' 来确定字符串长度。如果字符串缺少这个结束标志,使用这类函数会导致未定义行为!如下图所示

3.2 字符数组在内存中的存储 

3.3 补充:字符串的两种定义方式

(1)使用字符数组:直接定义字符数组储存字符,这种情况编译器会自动加上结束符'\0'

int main() {
    // 定义字符数组,自动添加结束符 '\0'
    char str1[] = "Hello, world!";
    printf("%s\n", str1);  // 输出 "Hello, world!"
    str1[0] = 'Y';
    printf("%s\n", str1);  //输出 "Yello, world!"
    return 0;
}
  •  实际数组包含13个字符和1个结束符'\0';
  • 通过下标可以修改字符串内容。

(2)使用字符指针:通过字符指针指向一个字符串,字符串会在内存“代码段”存储,以'\0'结束

int main() {
    // 使用字符指针指向字符串字面量
    char *str2 = "Hello, world!";
    printf("%s\n", str2);  // 输出 "Hello, world!"
    return 0;
}

 

  •  字符指针指向的字符串存储在静态区中,不能被修改,并且空间在程序结束才会被操作系统回收。

4 数组大小的计算

4.1 sizeof操作符

sizeof用于获取数据类型(如:int、char、float、double、结构体......)or对象所占用的内存大小以字节为单位)。

int main() {
    printf("Size of int: %zu bytes\n", sizeof(int));       // 4
    printf("Size of char: %zu bytes\n", sizeof(char));     // 1
    printf("Size of float: %zu bytes\n", sizeof(float));   // 4
    printf("Size of double: %zu bytes\n", sizeof(double)); //8
    return 0;
}

【关于sizeof需要注意的点】 

  • sizeof是操作符,不是函数!
  • sizeof计算字符串大小时,不依赖于字符串的结束符'\0'。(有'\0'则计入,没有也不影响)

  

  • sizeof的计算发生在编译阶段
int main()
{
    int a = 5;
    short s = 11;
    printf("%d\n", sizeof(s = a + 2));    //2  【sizeof通过类型确定s大小是short类型,大小为2】
    //从数学角度看,a+2的值为整型,但一定要赋值给short类型的s,会发生截断,并且short类型占2字节
    //截断后s的值应该是7
    
    printf("%d\n", s);    //11  【sizeof在编译期间就完成处理,而s的运算结果在可运行程序中计算】
    return 0;
}
  • sizeof并不会去访问变量的实际内容,而是根据类型信息确定变量在内存中需要占用的空间大小。

4.2 数组大小 & 元素个数 计算

int main() {
	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	printf("%d\n", sz);  //输出:10
}

sizeof(arr) :整个数组大小;

sizeof(arr[0]) :数组首元素大小,即每个元素的大小。 

5 指针和数组 & 数组名代表的意义

  • 通常情况下,数组名表示首元素地址。

数组名各种操作访问到的内容:

因此,*(arr+i) 和 arr[i] 等价 。

  •  但是有以下两种特殊情况

 (1)sizeof(数组名)

数组名单独放在sizeof内部,此时 数组名 表示整个数组,计算出的是整个数组的大小。 

(2)&数组名

此时数组名也表示整个数组,取出的是整个数组的地址。 

这也代表着 &数组名 结合+-等操作访问到的是整个数组

上面提到:二维数组可以理解为 存放一维数组 的数组 。结合这个知识点加深理解:

6 数组传参

数组作为参数传递给函数时,实际上是将数组的指针传递给函数,即传递的时数组的地址。

数组名在参数列表中会被隐式转换为指向其第一个元素的指针。

(1)一维数组传参

//方式一
void printArray(int arr[], int size) {
    //函数内容略
}
//方式二
void printArray(int* arr, int size) {
    //函数内容略
}

int main() {
    int arr[] = { 1, 2, 3, 4, 5 };
    int sz = sizeof(arr) / sizeof(arr[0]); // 计算数组的元素个数
    printArray(arr, sz); // 传递数组和大小
    return 0;
}

(2)二维数组传参

//方式一
void printArray(int arr[3][4]) {
    //函数内容略
}
//方式二
void printArray(int arr[][4]) {
	//函数内容略
}

int main() {
	int arr[3][4] = { {1,2,3,4},{2,3,4,5},{3,4,5,6} };
	printArray(arr);
}

注意:二维数组传参时,形参列表中也是数组行信息可以省略,列信息不能省略! 

在传参过程中,如果粗要用到数组的元素个数,要在传参前计算出结果,将结果作为参数传递给函数。

如果在被调用函数内部计算元素个数,会出现错误。如下图所示

7 指针数组

即:存放指针的数组

char* arr[5];
int* arr2[6];

 示例:

8 数组指针

即:指向数组的指针

int (*p)[10];
  • p是一个数组指针,指向一个大小为10的整型数组
  • * :说明p是一个指针变量
  • [10] : 说明数组有10个元素
  • int :说明数组每个元素类型为int

标签:总结,初始化,arr,int,元素,C语言,数组,sizeof
From: https://blog.csdn.net/Evelyn_nn/article/details/142914025

相关文章

  • 计算机网络常见面试题总结(上)
    目录计算机网络基础网络分层模型常见网络协议计算机网络基础网络分层模型OSI七层模型是什么?每一层的作用是什么?OSI七层模型是国际标准化组织提出的一个网络分层模型,其大体结构以及每一层提供的功能如下图所示:每一层都专注做一件事情,并且每一层都需要使用下一层提......
  • JavaSE JAVA基础总结(一) 我的学习笔记
    JavaSEJAVA基础总结(一)一、Java基础语法1.数据类型2.运算符3.选择结构4.循环结构5.数组6.方法7.递归二、面向对象1.面向对象编程(1)为什么要学习面向对象(2)面向过程与面向对象区别(3)对象(4)类(5)成员变量与局部变量的区别(6)构造器(7)this的关键字(8)标准JavaBean(9)静态关键字:static......
  • lua脚本使用cjson转换json时,空数组[]变成了空对象{}
    一、前言项目lua使用工具:cjson问题:reids中部分数据的jsonkey存在为[]的值,使用cjson进行解析的时候将原本空数组[]解析成了空对象{}目标:原本[] 转[]二、解决方案在使用cjson类库时,先配置json转换要求--设置json转换格式cjson.encode_empty_table_as_object(fal......
  • 双指针大总结
    1.A-B数对P1102A-B数对-洛谷题目背景出题是一件痛苦的事情!相同的题目看多了也会有审美疲劳,于是我舍弃了大家所熟悉的A+BProblem,改用A-B了哈哈!题目描述给出一串正整数数列以及一个正整数\(C\),要求计算出所有满足\(A-B=C\)的数对的个数(不同位置的数字一样的......
  • TypeScript语法总结
    1.Typescript概述融合了后端面向对象思想的超级版的JavaScript语言。​TypeScript是JavaScript的超集,扩展了JavaScript的语法。特点:(1)TypeScript:【静态类型检查器】可在编译时检查错误(2)TypeScript为JS的超集(3)TS===JS+【类型】js有的ts都有(4)TS规范了JS......
  • 【C语言】动态内存管理及相关笔试题
    文章目录一、为什么有动态内存分配二、malloc和free1.malloc函数的使用2.free函数的使用三、calloc和realloc1.calloc函数的使用2.realloc函数的使用四、常见动态内存分配的错误五、动态内存经典笔试题题1题2题3六、总结C/C++中程序内存区域划分一、为什么有动态......
  • 大模型-AIAgent 智能体现状总结
    目录AutoGen面向多个agent的开源框架,agent可定制可对话能够无缝的允许人类参与有一个masteragent制定计划分发给不同的agent-->智能体去中心任意两个可直接对话(或者分层的结构上下两层之间才能交互)构建复杂的多智能体对话系统,基于llm工作流,agent可以是基于llm,工具或......
  • 灾难恢复:邮箱数据库操作总结:整理 查询邮箱数据库大小和空白数据大小(重要文档)
    灾难恢复:邮箱数据库操作总结:整理查询邮箱数据库大小和空白数据大小(重要文档)邮箱数据库整理查询邮箱数据库大小和空白数据大小AvailableNewMailboxSpace是指的这个总空间里可以被“减肥”掉的空间。Get-MailboxDatabase db0* -Status|FTName,@{n="MailboxCount";e={(Get-M......
  • swagger中参数为数组dataType的设置
    1.Swagge接口参数:@ApiImplicitParams({@ApiImplicitParam(name="id",value="项目ID",dataType="String",paramType="query",required=true),@ApiImplicitParam(name="useridlist",value=&......
  • 912.排序数组
    这一题我们要用归并排序的方法归并排序1)整体就是一个简单递归,左边排好序、右边排好序、让其整体有序2)让其整体有序的过程里用了排外序方法3)利用master公式来求解时间复杂度4)归并排序的实质时间复杂度O(N*logN),额外空间复杂度O(N)912.排序数组给你一个整数数组 nums,......