飞道的博客

C语言指针初阶--详解

338人阅读  评论(0)

定义

指针实质上就是一个变量,是用来存放另一个变量的地址,就像其他变量或常量一样,必须在使用指针存储其他变量地址之前,对其进行声明。
举实例来理解指针:
指针必须结合内存来理解!
把内存想象成很长很长的大走廊,走廊上有很多很多房间,每个房间的大小都是一个字节。每个房间都有一个门牌号,门牌号是从0开始,依次递增,房间号就叫“地址”,可以用另外的一块内存空间来保存这个地址,这样的内存空间就称之为“指针变量”。
认识指针就要先认识内存,认识内存就要先知道地址

指针变量其实涉及两方面的信息:1.这个地址从哪开始 2.这个地址有多长。
有这两方面信息才能共同确定一块内存空间,地址多长就体现在指针类型里,例:int
* 对应的内存就是4个字节

指针和指针类型

指针变量的声明:

int* x;
float* y;

定义了指针变量x y,x是指向整数类型的指针,y是指向单精度型变量的指针变量
x的类型是 int*类型,y的类型是float*类型
一个指针变量占有的长度与指针类型无关,都是统一的长度

指针类型是在描述针对指针解引用后,得到的类型种类

指针语法

int num=10;
int* p=&x;
printf("%d\n",*p);

简单画图理解:

第三行的*p是解引用操作,解引用操作只能针对指针变量进行操作~
此操作必须针对有效的内存进行解引用,一旦解引用了无效内存,便会发生“未定义行为”~
为了避免发生“未定义行为”,就需要让指针里面保存的地址,是已经申请到的内存的地址即可(创建变量就是申请内存)

注意:

指针变量本身的长度是固定的,在32位windows下是4个字节,如果是64位的系统,指针就是8个字节
指针变量的类型描述的是它所对应的内存空间的长度,而与指针变量自身占据的空间无关,即在同一个系统上,不管什么类型,指针变量所占据的空间是固定的

指针类型的意义

1.整数+ -
指针的类型决定了指针向前或者向后走一步有多大(距离)。

2.解引用操作
指针类型决定了对指针解引用的时候能操作几个字节

野指针

如果一个指针里面存放的是一个无效的地址,就称这个指针为“野指针”~~

常见导致野指针的情况

  1. 引用未初始化的指针变量
int x=1;
int* p;
*p=x;

指针p未初始化,自动初始化为0,指向的是一块程序员无法把控的内存,程序运行时会报错

  1. 引用被赋值为NULL的指针变量
int x=1;
int* p=NULL;
*p=x;

NULL就是典型的野指针~NULL的空间是不可用的,往该内存空间写数据也是非法的

指针运算

先上结论:指针运算 弊大于利~~
&
解引用*
前两个在文章前面指针语法中已提到~
±整数
代码如下:

int main() {
   
	char* p = 0;// 等价于char* p = NULL
	short* a = 0;
	int* b = 0;
	double* c = 0;
	printf("%p\n", p);
	printf("%p\n", p + 1);
	printf("%p\n", a + 1);
	printf("%p\n", b + 1);
	printf("%p\n", c + 2);
	system("pause");
	return 0;
}
	char* p = 0x100;
	printf("%p\n", p - 1);

输出结果


由上述代码及输出结果可以看出,指针+1,并不是单纯的地址+1,而是指针要加上一个值,使得指针能够指向下一个元素 指针+1:跳过本身元素,指向下一个元素
此处要注意:%p是按照16进制打印,例如上述代码 打印c+2,应该是+16,逢16进1,故打印结果便是00000010~
意义:用于操作数组
代码举例:

int main() {
   
	int arr[] = {
    9,5,2,7,4 };
	int size = sizeof(arr) / sizeof(arr[0]);
	for (int* p = arr; p < arr + size; p++) {
   
		printf("%d\n", *p);
	}
	system("pause");
	return 0;
}

指针相减
大部分的指针-指针都是无意义的,除非两个指针指向同一个连续的内存空间(数组)~
指针相减的结果表示两个指针之间相隔多少个元素(和指针的类型密切相关)
代码举例:

int main() {
   
	int arr[] = {
    9,5,2,7,4 };
	int* p1 = arr;
	int* p2 = &arr[2];// 相当于arr+2
	printf("%d\n", p2 - p1);
	system("pause");
	return 0;
}

指针相减,相当于±整数的逆运算~~

指针比较相等
指针比较大小
即:指针的关系运算。
随意两个指针都可以进行==和!=的比较,而两个指针想要进行> < >= <=的比较,需要具备前提条件:两个指针要指向同一个连续的内存空间。
本质都是在比较指针变量里边储存的地址的值的大小关系~~

常见写法,指针与NULL 进行比较:

int main() {
   
	int num = 6;
	int* p = &num;
	if (p == NULL) {
   
		printf("这是一个无效地址\n");
	}
	system("pause");
	return 0;
}

指针 [ ]
指针 [ ]相当于±整数和解引用操作的简化写法
代码举例:

int main() {
   
	int arr[] = {
    9,5,2,7,4};
	int* p = arr;
	for (int i = 0; i < 5; i++) {
   
		printf("%d\n", *(p + i));
	}
	printf("\n");
	for (int i = 0; i < 5; i++) {
   
		printf("%d\n", p[i]);
	}
	system("pause");
	return 0;
}

由上述代码及输出结果不难看出:
p[i] 等价于 *(p+i)

指针和数组

数组名在有些时候能隐式转换成首元素的地址
触发隐式类型转换的情况:
1.函数传参
2.参与运算

特殊指针

在C中有一种特殊的指针,只设计类型,不涉及大小.
即:void* ,所以void* 不能进行解引用操作,不能±整数,也不能相减!
但void* 的作用很大,不同类型的指针,区别就在于内存的长度不同,假如我们需要某一个函数能同时兼容多种不同的指针时(即泛型编程) ,void*就可以解决,可以暂时忽略内存的长度。

二级指针

二级指针本质上是一个一级指针,而指向的元素又是一个一级指针。

int num = 10;
int* p = &num;
int** p2 = &p;


把二级指针当成一级指针理解即可。若设计高级指针,可使用typedef来定义,来增强代码的直观可读性。

指针数组和数组指针

指针数组:即用于存储指针的数组,也就是数组元素都是指针
数组指针:即指向数组的指针。

后一个词是本体,前一个词是修饰
二者相比,指针数组很常用
举例:
int(* p)[4] 因为()优先级高,首先p是一个指针,指向一个int类型的一维数组, 数组有4个元素,每个元素是一个int型整数,就是int ( * p)[4]是指一个指向数组的指针
而 int* p[4], [ ]优先级高,先与p结合成为一个数组,再由int* 说明这是一个int型指针数组,指针数组本身就是个数组,只不过这个数组里存放是一个指针类型( int* ),也就是地址

const和指针

int a=10;
const int* p=&a;
p=&a;

指针变量对应的另外一块内存空间不能修改

int a=10;
int* const p=&a;
*p=20;

指针变量自身不可修改

int a=10;
const int* const p=&a;

指针变量自身和指针变量对应的另外一块内存空间均不可修改

指针并不难,难的是对内存的理解~~


转载:https://blog.csdn.net/m0_47988201/article/details/116722438
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场