首页 > 编程学习 > 初阶指针(1)

初阶指针(1)

发布时间:2022/11/10 13:26:23

初阶指针(1)

  • 1. 指针是什么?
  • 2. 指针和指针类型
    • 2.1 指针的解引用
    • 2.2 指针+-整数
  • 3. 野指针
    • 3.1 野指针成因
    • 3.2 如何规避野指针

1. 指针是什么?

指针是什么?

指针理解的2个要点:

1.指针是内存中一个最小单元的编号,也就是地址

2.平时口语中说的指针,通常指的是指针变量,是用来存放内存地址的变量

总结:指针就是地址,口语中说的指针通常指的是指针变量。

最小的内存单元,也就是一个地址的空间是一个字节。

指针变量

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

#include <stdio.h>
int main()
{
	int a = 10;
	int* pa = &a;
	printf("%p", pa);
	return 0;
}
//--------------------
//编译器运行结果为
//00F3FA6C

a变量占用4个字节的空间,这里是将a的4个字节的第一个字节的地址存放在pa变量中,pa就是一个之指针变量。

pa中存放的是a变量的地址,同样pa也有自己所对应的地址。

总结:

指针变量,用来存放地址的变量。(存放在指针中的值都被当成地址处理)

那这里的问题是:

一个小的单元到底是多大?(1个字节)

如何编址?

对于32位的机器,假设有32根地址线,那么假设每根地址线在寻址的时候产生高电平(高电压)和低电平(低电压)就是(1或者0);

那么32根地址线产生的地址就会是:

00000000 00000000 00000000 00000000

00000000 00000000 00000000 00000001

11111111 11111111 11111111 11111111

这里就有2的32次方个地址。

每个地址标识一个字节,那我们就可以给 (2^32Byte ==2^32/1024KB==2^32/1024/1024MB==2^32/1024/1024/1024GB == 4GB) 4G的空间进行编址。

同样的方法,那64位机器,如果给64根地址线,那能编址(2^64==2^32*2^32==2^32*4GB)2的32次方个4GB空间,

这里我们就明白:

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

总结:

  • 指针变量是用来存放地址的,内存单元的唯一标志是地址。
  • 指针的大小在32位平台是4个字节,在64位平台是8个字节

当想通过地址进行访问的时候使用解引用操作符(间接访问操作符)

#include <stdio.h>
int main()
{
	int a = 10;
	int* pa = &a;
	*pa = 20;
	printf("%d",a);
	return 0;
}
//--------------------
//编译器运行结果为
//20

2. 指针和指针类型

#include <stdio.h>
int main()
{
	int* pa;
	char* pc;
	float* pf;

	printf("%d\n", sizeof(pa));
	printf("%d\n", sizeof(pc));
	printf("%d\n", sizeof(pf));
	return 0;
}
//------------------
//编译器运行结果为
//4
//4
//4

char* 类型的指针是为了存放 char 类型变量的地址。

float *类型的指针是为了存放 short 类型变量的地址。

int* 类型的指针是为了存放 int 类型变量的地址。

既然不同类型的指针变量大小相同,为何不只创建一个类型?

那指针类型的意义是什么?

2.1 指针的解引用

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

上述两个代码都能存储a的地址,但是进行解引用操作的时候会有不同。

image-20220825164825840

总结:

指针的类型决定了,对指针解引用的时候有多大的权限(能操作几个字节)。

比如: char* 的指针解引用就只能访问一个字节,而 int* 的指针的解引用就能访问四个字节。

double*的解引用能访问八个字节。

2.2 指针±整数

#include <stdio.h>
int main()
{
	int a = 0x11223344;
	int* pa = &a;
	char* pc = &a;
	printf("pa=%p\n", pa);
	printf("pc=%p\n", pc);
	
	printf("pa+1=%p\n", pa + 1);
	printf("pc+1=%p\n", pc+1);

	return 0;
}
//--------------------
//编译器运行结果为
//pa = 012FFC40
//pc = 012FFC40
//pa + 1 = 012FFC44
//pc + 1 = 012FFC41

**总结:**指针的类型决定了指针向前或者向后走一步有多大距离(步长)。

int*指针+1,意思是跳过一个整型,也就是向后走4个字节。

char*指针+1,意思是跳过1个字节.

double*指针+1,意思是跳过8个字节。

short*指针+1,意思是跳过2个字节。

应用

存储1-10在数组中

方法一:

#include <stdio.h>
int main()
{
	int arr[10] = { 0 };
	int* pa = arr;
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		*pa = i + 1;
		pa++;
	}
	return 0;
}

方法二:

#include <stdio.h>
int main()
{
	int arr[10] = { 0 };
	int* pa = arr;
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		*(pa+i) = i + 1;	
	}
	return 0;
}

因为知道int*类型的步长,所以++以后对应的地址正好是下一个空间的地址。如果用char*只能访问10个字节。

3. 野指针

概念: 野指针就是指针指向的位置是不可知的随机的、不正确的、没有明确限制的)。

3.1 野指针成因

1.指针未初始化

#include <stdio.h>
int main()
{
	int* p;  //野指针
	*p = 20;
	return 0;
}

由于指针没有初始化,所以p存放的是随机地址,这是p就是野指针。

2.指针越界访问

#include <stdio.h>
int main()
{
	int arr[10] = { 0 };
	int i = 0;
	int* p = arr;
	int sz = sizeof(arr) / sizeof(arr[0]);
	for (i = 0; i <= sz; i++)
	{
		*p = i;
		p++;
	}
	return 0;
}

当指针指向的范围超出数组arr的范围时,p就是野指针。

3.指针指向的空间释放

#include <stdio.h>
int* test()
{
	int num = 100;
	return &num;
}
int main()
{
	int* p =test();
	*p = 200;
	return 0;
}

虽然返回了num的地址,但是num的空间已经被释放,再通过p访问**他人空间(原num空间)**时,p就是野指针。

3.2 如何规避野指针

1.指针初始化

2.小心指针越界

3.指针指向空间释放,及时置为NULL(空指针)

4.避免返回局部变量的地址

5.指针使用之前检查有效性

1.指针初始化

#include <stdio.h>
int main()
{
	int a = 10;
	int* pa = &a;  //明确初始化
	//NULL--本质是0,是用于初始化指针的
	int* p = NULL;  
	return 0;
}

NULL–空指针–本质是0,是用于初始化指针的。

2.小心指针越界

#include <stdio.h>
int main()
{
	int arr[10] = { 0 };
	int i = 0;
	int* p = arr;
	int sz = sizeof(arr) / sizeof(arr[0]);
	for (i = 0; i <= sz; i++)
	{
		*p = i;
		p++;
	}
	return 0;
}

3.指针指向空间释放,及时置为NULL

这里涉及到动态内存分配问题,仅简单介绍使用。

动态内存分配

//申请空间
int* p = malloc(40);
//使用空间

//使用后释放空间
free(p);//释放空间
p = NULL;//将p置为空指针,防止成为野指针。

4.避免返回局部变量的地址

#include <stdio.h>
int* test()
{
	int num = 100;
	return &num;
}
int main()
{
	int* p =test();
	*p = 200;
	return 0;
}

由于局部变量存放在栈区,所以也叫作避免返回栈空间的地址。

5.指针使用之前检查有效性

对于空指针是不能进行访问。

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

空指针进行解引用,也就是把0作为地址进行访问,这个时候程序会崩掉。

#include <stdio.h>
int main()
{
	int* p = NULL;

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

这是指针初阶的前半章节语法,至于初阶指针剩下的内容,我们在下一篇文章当中再进行介绍。

Copyright © 2010-2022 dgrt.cn 版权所有 |关于我们| 联系方式