zl程序教程

您现在的位置是:首页 >  其他

当前栏目

初阶指针——全面了解指针(全)

2023-03-07 09:43:25 时间

小菜菜的gitee网址:https://gitee.com/dashboard,记录了菜鸟的学习过程,希望一起加油!!!❤❤❤

目录

指针是什么

要了解指针的含义,首先我们必须要先了解内存,内存是电脑上的存储器,是用来存储程序和数据的,程序的运行都在内存,而内存又被划分为很多个内存单元,每个内存单元大小为一个字节,并且每一个内存单元都有自己的编号,这个编号就是地址,指针就是内存中一个最小的单元编号,也就是说,指针的本质就是地址。 话不多说,看图:

而我们平常口头中所说的指针,其实就是指针变量

我们可以通过&(取地址操作符)取出变量的内存其实地址,把地址可以存放到一个变量中,这个变量就是指针变量

#include<stdio.h>

int main()
{
	int a = 1;
	//在内存中为变量a开辟一块空间

	int* p = &a;
	//这里使用&(取地址操作符),a变量占用4个字节的空间,这里是将a的4个字节的第一个字节的地址存放在p变量
	//中,p就是一个之指针变量。

	return 0;
}

指针变量就是存放地址的,在这里,p就是指针变量,它存放的是变量a的第一个字节的地址。

另外 在32位的机器上,地址是32个0或者1组成二进制序列,那地址就得用4个字节(32bit)的空间来存储,所以一个指针变量的大小就应该是4个字节。 那如果在64位机器上,如果有64个地址线,那一个指针变量的大小是8个字节(64bit),才能存放一个地址。

总之一句话,指针的大小在32位平台是4个字节,在64位平台是8个字节,不管是int类型的指针,还是short类型的指针,还是char*类型,指针大小始终都是4或8个字节。(切记!!!)

举个例题:

#include <stdio.h>

int main()
{
	int a = 10;
	char b = 'w';

	int* p1 = &a;
	char* p2 = &b;
	printf("%d\n", sizeof p1);
	printf("%d", sizeof p2);

	return 0;
}

在这里,由于这是x86(32位)环境,所以p1与p2的大小都为4个字节。

指针和指针类型

我们知道,变量的类型有很多种,int 、char、double、short…,而指针也有很多类型与之对应,int*、char*、double*、short*…用来存放对应变量的地址,那么有一个问题在这里:指针类型的意义到底是什么?

这里我们来看一个案例:

#include <stdio.h>

int main()
{
 int n = 10;
 // ():强制类型转换,将本来的int*转换为char*类型
 char *pc = (char*)&n;
 int *pi = &n;
 
 //%p:打印地址
 printf("%p\n", &n);
 printf("%p\n", pc);
 printf("%p\n", pc+1);
 printf("%p\n", pi);
 printf("%p\n", pi+1);
 return  0; }

大家来观察一下结果,如下图:

我们通过观察,可以发现::指针的类型决定了指针向前或者向后走一步有多大(距离)。

大家继续观察:

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

这里进行解引用操作,我们观察这两种类型的指针,会发生什么呢? 如下:

所以,我们有如下结论:指针的类型决定了,对指针解引用的时候有多大的权限(能操作几个字节)。 比如: char* 的指针解引用就只能访问一个字节,而 int* 的指针的解引用就能访问四个字节。 再画个图来表示一下:

野指针

野指针,光看这个“野”字,就知道这东西不好惹,事实上也确实如此,那么什么是野指针呢? 野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的) 通俗点就是你不知道这个指针指向哪里,也就是说,当解引用的时候,就会访问一个不确定的地址,所以结果是不可知的。 就跟一个野狗一样,谁也不知道它会不会给你来一口(玩笑)

那么野指针都是怎么形成的呢?这里列出了几点:

1、指针未初始化 看例子:

#include<stdio.h>

int main()
{
	int* p;
	*p = 20;

	return 0;
}

2、指针越界访问

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

3. 指针指向的空间释放(点击此处先了解

指针运算

1、指针 ± 整数 大家看例题:

int main()
{
	int arr[5] = { 1,2,3,4,5 };
	int* ps;
	for (ps = &arr[0]; ps < &arr[5];ps++)
	{
		*ps = 0;
	}

	return 0;
}

这是表示的什么呢?ps用来存放数组首元素地址,ps+1,就访问到了ps首元素后面那个元素,对它解引用,*ps=0,ps<&arr[5]表示,ps的地址一直增加到arr[5]元素前面那个元素的地址,即arr[4]的地址,解引用,*ps=0,最终结果就是arr的从下标为0的元素,一直到下标为4的元素全都变成了0。

2、指针关系的运算 C99标准规定: 允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与指向第一个元素之前的那个内存位置的指针进行比较。

画个图来解释一下即:

3、指针-指针 举个简单的例子:

#include<stdio.h>
int main()
{
	int arr[5] = { 1,2,3,4,5 };
	int* ps = arr;//数组名数组表首元素地址

	int* ps1 = &arr[4];
	printf("%d", ps1 - ps);

	return 0;
}

这里就表示指针之间的元素个数,大家看图:

既然知道这个功能,那我们是不是就可以用它来求字符串长度(不使用库函数),答案是肯定的,下面简单写一个:

#include<stdio.h>
int my_strlen(char* s) 
{
	char* p = s;
	while (*p != '\0')
		p++;
	return p - s;
}

int main()
{
	char arr[] = { "abcdef"};
	int len=my_strlen(arr);

	printf("%d", len);
	return 0;
}

指针和数组

数组名表示数组首元素地址(这话已经说n遍了) 再来验证一下

#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]);
    int i = 0;
    for (i = 0; i < sz; i++)
    {
        printf("&arr[%d] = %p   <====> p+%d = %p\n", i, &arr[i], i, p + i);
    }
    return 0;
}

大家可以发现,通过指针p+整数可以实现数组元素的访问,那么我们就可以这样来打印数组:

#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]);
 int i = 0;
 for (i = 0; i<sz; i++)
 {
 printf("%d ", *(p + i));
 }
 return 0; }

指针数组

指针数组是指针还是数组?当然是数组,指针数组看名字就可以推测出来是用来存放指针的数组,话不多说,看例题:

#include<stdio.h>

int main()
{
	int arr1[5] = { 1,2,3,4,5 };
	int arr2[5] = { 2,3,4,5,6 };
	int arr3[5] = { 3,4,5,6,7 };

	//数组名表示地址,将arr1、arr2、arr3的首元素地址存放在arr中
	int* arr[3] = { arr1,arr2,arr3 };
	int i = 0;
	int j = 0;
	for (i = 0; i < 3; i++)
	{
		for (j = 0; j < 5; j++)
		{
			printf("%d ", arr[i][j]);
		}
		printf("\n");
	}

	return 0;
}

大家看打印的结果,是不是有点像之前说过的二维数组,确实如此,arr[i]也就是arr1、arr2、arr3,也就是这三个数组的首元素地址,通过这三个地址,就可以访问到对应的整个数组,这不就对应着二维数组每一行的首元素嘛。

大家也可以这么理解:

二级指针

我们上面讲到了用指针变量来存放地址,那么,作为存放地址的指针变量,它也是变量,而只要是变量就有地址,那指针变量的地址存放在哪里? 这就提到了二级指针 <- -点击可了解。 该如何表示呢?举个例子:

#include<stdio.h>

int main()
{
	int a = 10;
	//a的地址存放在p中
	int* p = &a;
	//p的地址存放在pp中
	int** pp = &p;

	//由于pp存放的是p的地址,对pp解引用就访问到了p,再对*pp解引用就相当于*p,**pp=30即*p=30,而p访问的是a的地址,对p解引用就相当于a=30;
	**pp = 30;
	printf("%d", a);

	return 0;
}

最终结果就是30.

end

生活原本沉闷,但跑起来就会有风!愿诸君加油! 期待您的关注与鼓励!❤❤❤