C专项——数组和指针

一 、数组

数组是一组相同类型的元素的集合。

1、创建与初始化

数组创建, [ ] 里必须是常量(字面常量、#define定义的标识符常量、枚举常量)或常量表达式,不能使用变量。

若数组创建的时候不想指定数组的确定大小,那么就要对数组进行初始化,数组就会根据初始化的内容自动确定数组的大小。对于二维数组初始化时,行值可以省略,列值不行

不论是一维数组还是二维数组,创建好之后它的地址存放都是一块连续的内存单元,且随着下标值的变大,地址也会由低地址到高地址(内存块里,高地址在上,低地址在下)

//定义
type_t arr_name (const_n);
//直接创建一定大小
int arr[6];    
//通过初始化数组内容确定大小
int arr[] = {1,2,3,4,5,6};
  • 对于确定数组大小有一个易混淆的点(对我来说是这样),这里拉出来说一下:
    strlen ( ) 是用来求数组有效长度的函数,长度的结束标志符是 ‘\0’ ,结束标识符不计入长度。而sizeof是用来计算数组所占空间大小的操作符, ‘\0’ 也会占一个字节。
    下面具体举例:
char arr1[] = "zuo";
char arr2[] = {'z','u','o'};
char arr3[] = {'z','u','o','\0'};
//strlen ( ) ,结果分别为3,随机数,3;
//sizeof,结果分别为 4,3,4

2、数组的使用

(1)下标访问

数组访问通过下标,下标的值从0开始。数组大小可以通过计算得到,一般使用的有两种方法(以arr [ ] 举例):

  • sizeof ——sizeof(arr) / sizeof(arr[0]);
  • strlen——strlen(arr);
    注意:在自定义函数内部求元素个数时,最好不要用方法1

(2)函数参数

数组做自定义函数参数,传入的是是指向数组首元素地址的指针,并不是整个数组的内容。所以当要在函数内部使用与大小有关的值时,最好在主函数定义好大小的表达式,把结果作为参数传给这个函数。

另外,数组名一般情况下都是数组首元素地址,只有两个情况表示整个数组,是例外:

  • sizeof(数组名) ——计算数组大小
  • &(数组名)——取出数组地址

当然,若是数组做形参写入了函数里,这时它的数组名只能表示元素首元素地址,再写sizeof或者&都只是元素首元素的东西

虽然首元素地址打印出来都是第一个元素的地址,但是对于一维数组来说,首元素的地址就是第一个元素的地址;对于二维数组来说,首元素地址则是第一行元素的地址,区别在它们做运算的时候移动的步幅不同。

二、指针

存放地址的变量叫指针变量。通过指针就可以找到以该指针为地址的内存单元。指针的大小在32位平台是4个字节,64位平台是8个字节(地址都是一串16进制的数字,32位平台是32位,64位平台64位)。

1、指针和指针类型

(1)指针定义

type * 变量名

(2)指针类型

既然指针是用来存放变量的,那么它有没有自己的类型呢?

不知道你有没有想过这个问题,虽然字节大小一样,但严格意义上是有的。看一下它的定义就知道它的类型是根据它所指向的变量的type决定的。若指向的元素是整型,就是整型指针;指向的元素是字符型,就是字符指针。

而类型的存在有什么意义呢?

  • 给变量赋值解引用时的权限,即一次可以访问几个字节。整型指针一次访问4个字节,字符型指针一次访问1个字节
  • 决定给指针作整数加减时的步幅,即指针前后移动时每次移动几个字节。整型指针一次移动4个字节,字符型指针一次移动1个字节

2、野指针

野指针是什么?它又是怎么出现的呢?野指针的出现是否会打乱程序结构?要怎么避免呢?

(1)定义

如果这个指针表示的地址即指向的位置是不可知、随机、不正确或者没有明确限制的,若此时解引用的话,得到的结果也是未知的。这样的指针就叫做野指针。

(2)成因

  • 指针未初始化,默认是随机值
  • 指针越界访问,如访问数组元素时,指向的数组元素下标超出数组范围,此时数组溢出,这个指针也就是野指针。
  • 指向的空间被释放,这个用一小段程序来形象说明一下:
int * test(){
    int a = 10;
    return &a;    //a作为局部变量已经在这步之后被销毁,传回去的只是存放它的地址,不存在指向的变量
}
int main(){
    int * p = test();    //此时P接收到的也是一个没有指向的地址
    return 0;
}

(3)规避

指针的出现确实打乱了程序结构,那么要如何避免?以下几个好习惯送给你!

  • 每次定义指针的时候记得初始化
  • 小心指针越界,尤其操作数组的时候
  • 当指针指向的空间被销毁释放一定要及时置空
  • 使用一个指针前检查是否有效

3、指针运算

(1)加减一个整数

指针前后移动,改变指向的变量。如在数组的遍历访问时,只需要通过对数组名(数组首元素地址)做加减运算即可。

(2)指针之间相减

指针直接相减得到的是两个指针之间相差的元素个数,具体通过代码呈现:

#include <stdio.h>
int main() {
	int a[] = { 1,2,3,4,5 };
	int* p = &a[0];
	int* s = &a[4];
	int ret = s - p;    //首尾元素差的元素个数
	printf("%d\n", ret);
	printf("%d\n", *p + ret);    //指针的加法运算,首元素加上与尾元素相差的元素个数得到尾元素
	return 0;
}

运行结果

(3)关系运算

对我来说,一开始确实不知道指针居然可以做比较!它主要是和数组一起结合使用,下面就把我知道的介绍一下,首先来一段代码:

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

这里是在遍历打印数组使用的,比较的是两个指针的大小即地址大小。我前面说过,数组的存储是一段由低到高的连续的存储单元,所以可以这样操作。

不知道你有没有注意到一个问题,由于数组下标是0到数组元素减1,所以在上面比较时p_arr [5] 其实是没有有效元素的。

实际上与数组下标为0的元素的前一个元素比较,也可以实现上面的功能,但是一般却不会这样做。标准规定:允许指向数组元素的指针与指向数组最后一个元素之后的内存单元比较,但是不允许指向数组元素的指针与指向数组第一个元素之前的内存单元比较。

4、二级指针

指针变量也是一种变量,是变量就有地址,而指针变量存放地址的地方就是二级指针。换句话说,有点像套娃,指针是指向一个变量的箭头的话,那么二级指针就是指向这个箭头的一个箭头。

不管是赋值还是解引用,二级指针所有操作和(一级)指针格式一模一样。

好的,理解了二级指针之后,三级指针,四级指针,…,n级指针就同理可得就好!

5、指针数组

顾名思义,存放指针的数组就是指针数组,虽然它是客观存在的,但我不理解它存在的意义。下面就放一段代码帮助理解:

#include <stdio.h>
int main() {
	int a = 1;
	int b = 2;
	int c = 3;
	int i = 0;
	int* arr[3] = { &a,&b,&c };
	for (i = 0; i < 3; i++) {
		printf("%d ", *arr[i]);
	}
	return 0;
}

三、数组与指针

专门写一个标题,再来强调一下(不是为了字数)

数组名一般情况下都是数组首元素地址,只有两个情况表示整个数组,是例外:

  • sizeof(数组名) ——计算数组大小
  • &(数组名)——取出数组地址

当然,若是数组做形参写入了函数里,这时它的数组名只能表示元素首元素地址,再写sizeof或者&都只是元素首元素的东西

综上,今天的整理到此结束!欢迎评论区指正!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值