C语言初阶之指针

一、什么是指针

在计算机科学中,指针是编程语言中的一个对象,利用地址,它的值直接指向存在电脑存储器中另一个地方的值。由于通过地址能找到所需的变量单元,可以说,地址指向该变量单元。因此,将地址形象化的称为“指针”。意思是通过它能找到以它为地址的内存单元。

可以这样理解内存:

指针是个变量,存放的是内存单元的地址(编号) 。

#include<stdio.h>
int main()
{
    int a = 10;
    int *p = &a;//创建一个指针变量,取地址符将a的地址取出用来初始化p
    return 0;
}

也就是说指针就是变量,用来存放地址的变量(存放在指针中的值都被当做地址处理)。

二、指针与指针类型

变量有各种各样的类型,整形,浮点型等。指针有没有这样的类型呢?准确来讲是有的。

char  *pc = NULL;//指针创建时,必须要初始化,将一个变量的地址给它或者赋值为NULL
int   *pi = NULL;
short *ps = NULL;
long  *pl = NULL;
float *pf = NULL;
double *pd = NULL;

这里可以看到,指针的创建是type + *的。其中,char*类型的指针存放的是char类型变量的地址。short*类型的指针是为了存放short类型变量的地址。int*类型的指针是为了存放int类型变量的地址。

那么指针的类型到底有什么意义呢?

#include <stdio.h>
int main()
{
	int n = 10;
	char* pc = (char*)&n;
	int* pi = &n;
	printf("%p\n", &n);
	printf("%p\n", pc);
	printf("%p\n", pc+1);
	printf("%p\n", pi);
	printf("%p\n", pi+1);
}

 输出结果为:

 也就是说指针的类型决定了指针向前或向后走一步会移动几个字节。

指针的解引用

#include<stdio.h>
int main()
{
    int n = 0x11223344;
    char* pc = (char*)&n;
    *pc = 0;
    return 0;
}

这里char*类型的指针只能控制一个字节。

 

#include<stdio.h>
int main()
{
    int n = 0x11223344;
    int* pc = &n;
    *pi = 0;
    return 0;
}

指针的类型决定了,对指针解引用的时候有多大的权限(能操作几个字节)。 比如: char* 的指针解引用就只能访问一个字节,而 int* 的指针的解引用就能访问四个字节。

三、野指针

野指针就是一个指向的位置是不可知的指针变量。在定义的时候没有初始化指针变量,其值就是随机的,指针变量的值是别的变量的地址,意味着野指针指向了一个地址不确定的变量,这时候如果解引用那就是去访问一个不确定的地址,结果是不可知的。

野指针的成因:

指针未初始化

当声明一个指针变量但没有给它赋值之前,默认情况下它的值是不确定的。如果此时使用这个指针,就可能导致野指针问题。

#include <stdio.h>
int main()
{
	int* p;//局部变量指针未初始化,默认为随机值
	*p = 20;
	return 0;
}

指针越界访问

#include <stdio.h>
int main()
{
	int arr[10] = { 0 };
	int* p = arr;
	int i = 0;
	for (i = 0; i <= 11; i++)
	{
		//当指针指向的范围超出数组arr的范围时,p就是野指针
		*(p++) = i;
	}
	return 0;
}

指针指向空间的释放

如果指针指向的是一个局部变量的地址,并且这个变量的作用域已经结束,那么这个指针也就成为了野指针。

int *p = malloc(sizeof(int));
free(p);
printf("%d", *p); // 内存已释放,但仍然尝试访问

如何规避野指针?

  1. 指针在创建时初始化它。
  2. 小心指针越界。
  3. 指针指向的空间释放时,将其置为NULL。
  4. 指针使用之前检查其有效性。
#include <stdio.h>
int main()
{
	int* p = NULL;
	int a = 10;
	p = &a;
	if (p != NULL)//检查其有效性
	{
		*p = 20;
	}
	return 0;
}

四、指针和数组

什么是数组名?

#include <stdio.h>
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,0 };
	printf("%p\n", arr);
	printf("%p\n", &arr[0]);
	return 0;
}

 输出结果为:

数组名和数组首元素的地址是一样的。也就是说数值名表示的是数组首元素的地址。

既然可以把数组名当成地址存放到一个指针中,我们使用指针来访问一个数组就成为可能。
#include <stdio.h>
int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9,0 };
	int* p = arr; //指针存放数组首元素的地址
	int sz = sizeof(arr) / sizeof(arr[0]);
	for (int i = 0; i < sz; i++)
	{
		printf("&arr[%d] = %p <====> p+%d = %p\n", i, &arr[i], i, p + i);
	}
	return 0;
}

输出结果为:

所以p+i计算的是数组arr下标为i的地址。那我们就可以直接通过指针来访问数组。

#include <stdio.h>
int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9,0 };
	int* p = arr; //指针存放数组首元素的地址
	int sz = sizeof(arr) / sizeof(arr[0]);
	for (int i = 0; i < sz; i++)
	{
		printf("&arr[%d] = %d\n", i, *(p + i));
	}
	return 0;
}

 五、二级指针

指针变量也是变量,只要是变量就有地址,那指针变量的地址存放在哪里?这就是二级指针。

int a = 10;
int *p = &a;//a的地址存放在p中
int **pa = &p;//p的地址存放在pa中
//p是一级指针,pa是二级指针

 二级指针的运算

#include <stdio.h>
int main()
{
	int a = 10;
	int* p = &a;
	int** pa = &p;
	printf("%p\n", &a);
	printf("%p\n", *pa);
	printf("%d\n", **pa);
	return 0;
}

*pa对pa中的地址进行解引用,这样找到的是p,p中存放的是a的地址。

**pa,先找到p,再对p进行解引用,那找到的就是a。

六、指针数组

指针数组是一个存放指针的数组。与整型数组,字符数组等类似,指针数组里的每一个元素都是一个指针变量,不过这种数组的元素可以指向不同类型的数据,也可以指向相同类型的数据。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值