前言
本章内容总结了数组、指针的内容。
C语言 | 快速了解C的发展史🧡💛💚💙
C语言 | 【耗费一夜总结三本C语言系列】之 指针、数组 一文透彻~~~🧡💛💚💙
C语言 | 【耗费一夜总结三本C语言系列】之 结构体、联合、枚举🧡💛💚💙
C语言 | 【耗费一夜总结三本C语言系列】之 声明🧡💛💚💙
C语言 | 【耗费一夜总结三本C语言系列】之 作用域 在也不用担心分不清变量的作用域拉!!!🧡💛💚💙
C语言 | 【耗费一夜总结三本C语言系列】之 编译步骤 会用C还不知道C如何编译???🧡💛💚💙
C语言 | 【耗费一夜总结三本C语言系列】之 数据类型总结🧡💛💚💙
C语言 | 【耗费一夜总结三本C语言系列】之 位及进制的用法🧡💛💚💙
一、初识数组💤
数组与结构是C语言中的
聚合数据类型
。
- 数组由一组
相同数据类型
的元素组成;
int arr[10];
以上是声明一个数据类型为int的长度为10的数组;
- 虽然数组的长度为10,但是使用时,数组下标编号是由0开始的。即
arr[0]
是数组的第一个元素,arr[9]
为数组的最后一个元素;
小拓展:为何数组的下标是以0开始的呢???:
这都归咎于编译器设计者,由于该设计者使用底层语言开发。而计算机的
偏移量
以0开始计数,已在设计者心中根深蒂固。
1.1 数组名 —— 🔔一个数组的名称具有哪些含义
数组名是一个
指针常量
,是第一个元素
的地址(内存中数组的起始位置);
- 是一个指向(
数据类型
)的常量指针;- 且只有
数组名
在表达式中
使用时,编译器才会为它产生一个指针常量;- 指针常量则
内存是固定的
,不能修改
。即程序运行后不能修改;
由于数组名为指针常量,不能接收一个数组,但可用相应类型的指针来接收
int arr[2] = {1, 2};
int arr1[2];
int *arr2;
// 非法
arr1 = arr;
// 合法
arr2 = arr;
//或者根据数组名是数组的首地址
arr2 = &arr[0];
在俩种情况下数组名不为指针常量
- 作为
sizeof
操作数时,返回整个数组的大小;【故在将其值除以数据类型的大小,即可获取数组长度(长度包含结束符)】- 为
&
操作数时一个指向数组的指针;
1.2 了解了数组,该如何初始化?💿
声明数组时,将根据声明所指定的元素数量为数组
保留内存空间
,再创建数组名
,其值是一个常量
,指向
这个段空间的起始位置
;
int arr[3] = {4, 1, 9};
数组初始化按照以上格式,内容在
{}
内,内部使用,
分隔开。
- 除了初始化以外,不允许数组使用
{}
赋值;
int arr[3] = {0};
即可将数组初始化为0;
int arr[] = {1, 2, 3};
若省略
[]
内的数字,则编译器会根据初始化列表的项来确定大小;
1.2.1 何为只读数组???
声明时,加上
const
关键字即可将数组初始化为只读数组,不能修改内部元素;
const int arr[3] = {1, 2, 3};
int i=0;
for(; i<3; ++i)
printf("%5d", arr[i]);
1.2.2 C99新特性 —— 指定初始化器
C99新特定,可指定数组
内部元素
的初始化;
int arr[10] = { [4] = 10 };
以上数组初始化
第5个元素为10
,其余全为0
;
int arr[] = { [4] = 10, 11, 23 };
编译器会自动向后
扩大数组
,该数组的长度为7
。而arr[5] = 11
,arr[6] = 23
1.2.3 C99新特性 —— 变长数组
int n = 10;
int arr[n];
C99新增新型数组,为
变长数组
(VLA)。
1.3 编译器为何不对数组边界做处理???⭕
使用数组时,需要注意
数组下标的边界
。在使用过程中,数组越界编译器可能不会检查
该错误,而异常终止
(根据编译器而异);
1.3.1 为什么编译器不检查数组越界的问题呢?
由于编译器为了提高程序的
运行速度
。若需要检查下标,C涉及的开销比我们想象的要多,则编译器需要在运行时,添加额外的指令来对数组的每个下标检查表达式所指向的元素是否属于同一个数组,这样会减低程序的运行效率。而该错误只要在开发过程中稍加注意即可避免
;
1.4 多维数组💨
int arr[2][10];
该数组有
2个元素
,每个元素内有10个元素
;
1.4.1 多维数组初始化
初始化时,可省略第一个
[]
内的数值,外部的数值需要保留;
int arr[][3] = {{2, 3, 1}, {4, 5, 6}};
1.4.2 多维数组与函数传参
与初始化的含义类似,
[]
外部的数组需要保留,内部可省略;
二、会了数组,少不了指针💞
2.1 指针简介
C语言中的一个重要概念及其特点。由于计算机的
硬件指令
非常依赖地址
,而指针在某个程度上把程序员的指令更加接近机器
的方式表达,提高程序效率。
指针是一个值为内存地址
的变量。在同一CPU架构
下,不同数据类型的指针变量其占用存储单元的长度
是相同的,但其占用的存储空间
不同;
- 指针不仅能对
数据本身
,也可以对该变量的地址
进行操作;
2.2 与指针操作相关的运算符
*
运算符
该运算符也被称为
解引用运算符
;
- 该运算符能将指针所指向地址的
数值取出
;
&
运算符
&
加上变量名即可取出变量名的地址
;
- 该
地址
可直接赋值给相应类型的指针;
指针求差
两个指向统一个数组的不同元素可以通过求差来获取
元素之间的距离
;
- 结果为
数组类型
的单位值;
2.2.1 指针操作中运算符的优先级
自增运算符与*
两者优先级
相同
,遵循从左向右;
[]
与*
[]
优先级高于*
;
2.3 声明指针【需注意】💥
int *arr = NULL;
以上语句仅仅声明了指针,系统只给
指针本身分配内存(arr)
,并未分配存储数据的内存(&arr)
;
- 必须使用以分配内存初始化指针;
三、指针、数组看似相似,实际不是💦
3.1 指针与下标💫
int arr[10] = {0};
// 指针操作与数组下标的相互转换
arr+2 = &arr[2];
*(arr+2) = arr[2];
- 地址按
字节
编址;- 指针加1也就是增加一个
存储单元
(所指向的类型大小),即下一个元素的地址;
3.1.1 有时指针比数组下标更高效
为啥是有时呢?
- 只有指针在被
正确的使用
才能达到效率高
的效果,否则将会本末倒置,使程序的效率更低;
使用下标
int i, arr[3];
for(i=0, i<3; ++i)
arr[i] = i+1;
编译器需要在程序中插入相应的
指令
,获得i的值,在把它与相应的数据类型的长度相乘
,需要花费一定的时间和空间。
使用指针
int *p, i=0;
int arr[3];
for(p = arr; p < arr + 3; ++p, ++i)
*p = i;
每次循环需要换到下一个地址时,指向要加上1x数据类型的长度。与下标用法的区别在于,它只需要在
编译时执行一次
即可。
- 所以,在对此使用
地址偏移
时,指针比数组更加高效;
归纳
- 当声明为
寄存器变量
的指针一般会比其他的指针效率高;
3.2 const修饰指针
3.2.1 const对指针的几种限定方式
const限定一个变量不允许被改变,可用于修饰指针;
- const指针;
- 指向const指针
指向const的指针
不允许p修改指向的数据的
值
,即不能修改其值;
- 但可以通过
修改其地址
的指向来达到修改值的目的;
const int *p;
const指针
不允许
指针指向
别处,但可以修改它的值
;
int const *p;
const修饰指及其指向
即
不能修改值
,也不能修改指向
;
const int const *p;
3.2.2 const涉及指针的安全性
不安全的情况
int x = 3;
const int *y = &x;
int *p;
p = y;
不安全,把const赋值到非const上;
安全的情况
int x = 3;
int *y = &x;
const int *p;
p =y;
把非const赋值给const,前提是
一级解引用
;
- 但是p如果是二级指针,则不安全;
3.3 指针或数组做函数参数💢
3.3.1 函数形参
只有在函数定义头或者函数声明中才能使用
int arr[]
代替int *arr
;
- 两者都是指向一个int的指针;
3.3.1 数组在函数中传值
若在函数传参中,数组使用
传值
的方式,则效率会比较低
。
- 当按值传递数组时,编译器必须分配
足够的空间
来存储
原数组的副本
,在把原数组的内容拷贝到新的数组;造成空间上的浪费
以及时间上的损耗
;- 故使用
传递地址
的方法,效率大大提升;- 若想要保护数据不被修改,则加上const关键词;
3.4 指针与多维数组💭
3.4.1 多维数组与取地址、取值间的转换
int p[3][2];
- p为(p[0])数组首元素的地址;
p == &p[0]
,p[0]是第二维数组的首元素(p[0][0]);p[0] == &p[0][0]
p
:是占一个int
大小的地址;p[0]
:占用两个int
大小的地址;- 不论是占用一个int还是两个int起始的地址都是相同的;
p = p[0]
- 故当给
p+1
时相当于增加一个int长度,而给p[0]+1
则增加两个int长度;
若搞不懂*,&,[]
一般情况下,就记作
*
需抵消一个[]
,&
需增加一个[]
,*
可抵消[]
;
**p = *&p[0][0] = p[0][0]
3.4.2 指向多维数组的指针
[]
优先级高于*
;
int (*p)[2];
此时,p相当于一个
二维数组
int *p[2];
此时,内部都是
int指针的数组
;