【初阶】指针,了解一下?认识指针及其用法

本文详细介绍了C语言中的指针,包括指针的定义、查看地址的方法、指针类型与运算、野指针的成因及防范、指针与数组的关系、二级指针和指针数组的使用。通过实例展示了指针在内存中的工作原理,强调了指针初始化和避免野指针的重要性,并探讨了指针运算的规则。
摘要由CSDN通过智能技术生成

指针

本篇重点:认识指针及其用法,以内存的方式看代码

  1. 指针是什么?
  2. 指针和指针类型
  3. 野指针
  4. 指针运算
  5. 指针和数组
  6. 二级指针
  7. 指针数组

1.指针是什么?

要想了解指针是什么,就需要了解内存。

内存:电脑上的存储设备,程序运行时会加载到内存中,占用内存空间,可以按电脑上Alt+Ctrl+Delete,点击任务管理器查看运行的程序所占内存空间。

  • 我们买手机或者买电脑时,都会看到8G+256G,16G+512G,这里的8G,16G就是内存;
  • 在vs中我们可以看到有x86和x64环境,这里的x86指的是32位平台(32根地址线),x64指的是64位平台(64根地址线)。
    在这里插入图片描述

我们以x86机器(32位平台下)举例

在这里插入图片描述
总结:指针是内存单元的编号(地址),在32位平台下是4个字节,在64位平台下是8个字节。

电脑是怎么产生编号呢(以32位平台为例)?

在这里插入图片描述

1.1如何在VS中查看地址,如何存放地址?

  • 首先看代码
#include<stdio.h>
int main()
{
	int a = 0x44332211;//int在内存中占4个字节
	int* p = &a;//p是指针变量,用来存放a的第一个地址(最小的那个地址)
	*p = 20;//解引用操作,通过p中存放的地址找到a,可以理解为*p等价于a
	printf("%d %d", *p, a);
	return 0;
}

运行结果:
20 20

vs中查看地址:按F10调试----在最上方的调试中找到窗口----内存

在这里插入图片描述

2.指针和指针类型

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

指针(指针变量)类型:和数据类型一样,有整型,浮点型,字符型。

int main()
{
	int a = 10;
	int* p = &a;//int
	int b = 20;
	char* pr = &b;//char
	int c = 30;
	short* ps = &c;//short
	int d=25;
	float* pa=&d;//float
	//这里的指针变量大小都为4个字节,因为在x86平台下,指针大小为4个字节
	return 0;
}

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

2.1指针±整数

  1. 不同指针类型访问的大小不一样
int main()
{
	int a = 0x44332211;
	int* p1 = &a;
	*p1 = 0;
	printf("%d %d", *p1,a);
	return 0;
}

运行结果:
0 0

整型指针类型一次访问4个字节
在这里插入图片描述

int main()
{
	int a = 0x44332211;
	char* p1 = &a;
	*p1 = 0;
	printf("%d %d",*p1,a);
	return 0;
}

运行结果
0 1144201728

char类型的指针一次访问1个字节
在这里插入图片描述

2.不同的指针类型±整数的值不一样,即跨步不一样

int main()
{
	int a = 10;
	int* p1 = &a;
	printf("%p\n", p1);
	printf("%p\n", p1+1);
	int b = 20;
	char* p2 = (char *)&b;//最好加一下强制类型转换,不加虽然不报错,但也有警告
	printf("%p\n", p2);
	printf("%p\n", p2+1);
	return 0;
}

运行结果:
0085FB50
0085FB54
0085FB38
0085FB39

总结:

  • char*,一次访问1个字节,+1跳过1个字节;
  • int*,一次访问4个字节,+1跳过4个字节;
  • 其他类型同理;

3.野指针

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

3.1野指针的成因

1.指针未初始化

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

编译器报错


2.指针越界访问

#include <stdio.h>
int main()
{
    int arr[10] = {1,2,3,4,5,6,7,8,9,10};
    int *p = arr;
    int i = 0;
    for(i=0; i<=10; i++)
   {
        //当指针指向的范围超出数组arr的范围时,p就是野指针
        printf("%d ",*(p++));
   }
    return 0;
}

运行结果:
1 2 3 4 5 6 7 8 9 10 -858993460


  1. 指针指向的空间释放
#include<stdio.h>
int* test()
{
	int a = 10;
	return &a;
}
int main()
{
	int* p = test();
	printf("%d ", *p);
	return 0;
}

运行结果:
10

但是10只是巧合,我们稍微改改:

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

运行结果:
hehe
4

原因:函数return后,会释放空间,即a的地址被释放了(还给内存了),那p接收的就是随机值,即野指针;

3.2如何规避野指针

  1. 指针初始化;
  2. 小心指针越界;
  3. 指针指向空间释放,及时置NULL(空指针,即0);
  4. 避免返回局部变量的地址;
  5. 指针使用之前检查有效性,如果不是NULL再使用;

4.指针运算

指针运算:

  • 指针±整数
  • 指针-指针
  • 指针的关系运算

4.1指针±整数

#define N_VALUES 5
int main()
{
    float values[N_VALUES];
    float* vp;
    for (vp = &values[0]; vp < &values[N_VALUES]; vp++)
    {
        *vp = 0;
    }
    int i = 0;
    for (i = 0; i < N_VALUES; i++)
        printf("%.1f ", values[i]);
    return 0;
}

运行结果:
0.0 0.0 0.0 0.0 0.0

上面讲过;

4.2指针-指针

两个指针要指向同一块空间,|指针-指针|=其之间的元素个数

int main()
{
	int arr[5] = { 1,2,3,4,5 };
	printf("%d ", &arr[4] - &arr[0]);
	printf("%d ", &arr[0] - &arr[4]);
	return 0;
}

运行结果:4 -4

分析:
在这里插入图片描述
模拟strlen函数

int my_strlen(char* str)
{
	char* start = str;//创建新的指针用来存放初始地址
	while(*str != '\0')
		str++;
	return str - start;//指针-指针为其之间的元素个数
}
int main()
{
	char arr[] = "abcdef";
	int ret = my_strlen(arr);
	printf("%d ", ret);
	return 0;
}

运行结果:
6

4.3指针的关系运算

比较指针的大小

#define N_VALUES 5
int main()
{
    float values[N_VALUES];
    float* vp;
    for (vp = &values[N_VALUES]; vp > &values[0];)
    {
        *--vp = 0;//vp=vp-1,--vp=vp
    }
    int i = 0;
    for (i = 0; i < N_VALUES; i++)
        printf("%.1f ", values[i]);
    return 0;
}

运行结果:0.0 0.0 0.0 0.0 0.0

在这里插入图片描述
代码简化, 这将代码修改如下:

#define N_VALUES 5
int main()
{
    float values[N_VALUES];
    float* vp;
    for (vp = &values[N_VALUES-1]; vp >=&values[0];vp--)
    {
        *vp = 0;
    }
    int i = 0;
    for (i = 0; i < N_VALUES; i++)
        printf("%.1f ", values[i]);
    return 0;
}

运行结果:0.0 0.0 0.0 0.0 0.0

在这里插入图片描述
实际在绝大部分的编译器上是可以顺利完成任务的,然而我们还是应该避免这样写,因为标准并不保证它可行.
标准规定:

允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与指向第一个元素之前的那个内存位置的指针进行比较。

5.指针和数组

1.指针和数组是不同的对象

  • 指针是一种变量,用来存放地址的,大小是4或8个字节;
  • 数组是一组相同元素的集合,是可以放多个元素的,大小取决于元素个数和元素类型;

2.数组名是首元素地址(除那两种情况外);

  • 地址可以放在指针变量中,可以通过指针访问数组;
int main()
{
	int arr[10] = { 0 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	int* p = arr;
	//赋值
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		*(p + i) = i + 1;//arr[i]=i+1//*p=i+1,p++
	}
	//打印
	int j = 0;
	for (j = 0; j < sz; j++)
	{
		printf("%d ", *(p + j));
	}

	return 0;
}

注意:arr[i]---->*(p+i)

运行结果:
1 2 3 4 5 6 7 8 9 10

6.二级指针

指针变量也是变量,是变量就有地址,那指针变量的地址存放在哪里?
接下来就要介绍二级指针

int main()
{
	int a = 0;
	int* pa = &a;//pa放a的地址
	int** ppa = &pa;//ppa放pa的地址
	**ppa = 50;//*ppa访问pa----*pa访问a,即**ppa访问a
	printf("%d %d", **ppa, a);
	return 0;
}

运行结果:
50 50

注意:地址本身不占内存,但把它存在一个变量中就占内存,大小取决于是32位台(4B),还是64位平台(8B)。

7.指针数组

指针数组是指针还是数组?
答案:数组,存放指针(地址)的数组

int main()
{
	int a = 1;
	int b = 3;
	int c = 5;
	int d = 7;
	int* arr[4] = { &a,&b,&c,&d };//指针数组
	int i = 0;
	for (i = 0; i < 4; i++)
	{
		printf("%d ", *(arr[i]));
	}
	return 0;
}

运行结果:
1 3 5 7

注意:
在这里插入图片描述

单个变量在内存中不一定是连续的,但是放在数组中就连续了;

趁热打铁:用一维数组模拟二维数组

int main()
{
	int arr1[4] = { 1,2,3,4 };
	int arr2[4] = { 5,6,7,8 };
	int arr3[4] = { 9,10,11,12 };
	int* arr[3] = { arr1,arr2,arr3 };//数组名是首元素地址
	int i = 0;
	for (i = 0; i < 3; i++)
	{
		int j = 0;
		for (j = 0; j < 4; j++)
		{
			printf("%d ", arr[i][j]);//*(arr[i]+j)---*(*(arr+i)+j)
		}
		printf("\n");
	}
	return 0;
}

运行结果:
1 2 3 4
5 6 7 8
9 10 11 12

总结

以上就是本篇的所有内容了,如果喜欢本篇,不妨点点赞,如果想持续了解更多c语言知识,不妨点个关注,我会每周更新博客,让我们一起努力,一起进步,路虽远行则将至。

-----------------------------------------------------------------好了,拜拜!!!

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值