C语言之一眼情深指针

        

目录

        一:指针基本知识

        1.指针的概念

        2.指针的类型

        3.指针的作用

        二:野指针

        1.野指针的产生原因

        2.如何避免产生野指

三:指针与数组

        1.指针数组

        2.数组指针

四:指针运算

        


一:指针基本知识

        1.指针的概念(什么是指针)

指针简单地来说就是地址。在内存中储存的每一个数据都有属于自己的地址。而为了保存一个数据在内存中的地址,我们就需要指针变量。

因此:指针是程序数据在内存中的地址,而指针变量是用来保存这些地址的变量。

但,为什么储存数据需要有地址呢?

这就需要我们从操作系统的角度来看这个问题。在程序员的眼中,用来存放数据的内存条应该是如下的。

 可以说内存是一个线性排列且递增的“平面地址”,其每一个字节的大小都是固定的,都是由8个2进制位组成。需要我们注意的是,每一个字节都有属于自己唯一的编号,且这个编号是从0开始的。上图是一个256M的内存,它一共有256x1024x1024=268435455个字节大小。相应的地址的取值范围也就是0~268435455.

        2.指针的类型

在我们之前学习C语言数组时曾了解过,数组有分为int类型数组,double类型数组,char类型数组;而对我们指针来说同样也分为int指针类型,double指针类型,char指针类型。

        3.指针的作用(用指针来做什么)

1.需要传入较大的数据时作参数。

2.传入数组后对数组作操作。

3.函数返回不止一个结果。

4.需要用函数来修改不止一个变量。

5.动态申请的内存。

        二:野指针

        1.野指针产生的原因

  1. 指针未被初始化

  2. 指针越界访问

  3. 指针指向的空间释放

前面两点比较好理解,我们来着重看一下第三点是什么意思

include<stdio.h>
int* test( )
{
	int a = 5;
	return &a;
}
int main()
{
	int* p = test();//将a地址传给*p
	*p = 10;
	return 0;
}

上段代码中我们自己定义了一个test()函数,并在test()函数中对a进行赋值,然后在main()将a的地址传给p,但是,我们应该知道在test()函数中定义的变量出了该函数之后便会“报废”,所以出了test()函数后a的地址释放,导致p变成了野指针。

        2.如何避免产生野指针

  1. 小心越界。
  2. 及时把指针赋成空指针。
  3. 避免返回局部变量的地址。
  4. 使用指针前检查有效性。 

三:指针与数组

        1.指针数组

定义:存放指针的数组(int* arr[])。我们知道有整型类型的数组int arr[],还有字符类型的数组char arr[],指针数组就是指针类型的数组

include<stdio.h>
int main()
{
	int a = 0;
	int b = 1;
 
	int* p1 = &a;
	int* p2 = &b;
 
	int* arr[] = { p1,p2 };//指针数组
	int* arr[] = { &a,&b };//指针数组
 
	return 0;
}

 上图中的数组中存放的每个元素都是指针。

        2.数组指针

定义: 指向数组的指针int (*)[]。

int (*arr)[10]={0,1,2,3,4,5,6,7,8,9}

其实要区别指针数组和数组指针也是有方法的,那就是看“*“与谁结合。

比如

int* arr1[5]={0,1,2,3,4}//此处星号“*”先与int结合,表明该数组中的所有元素都是int型的指针,那这便是指针数组

int (*arr2)[5] ={0,1,2,3,4}//此处星号“*”先与“arr2“结合,说明该数组一整个都是指针,这便是数组指针。

四:指针运算

        在讲指针运算之前,我们先来看一段代码

include<stdio.h>
int main()
{
    char ac[]={0,1,2,3,4,5,6,7,8,9};
    *p = ac;
    printf("p=%p",p);
    printf("p+1=%p",p+1);
    int ai[]={0,1,2,3,4,5,6,7,8,9};
    *q = ai;
    printf("q =%p",q);
    printf("q+1 =%p",q+1);
    return 0;
}

输出结果如下

p=0xbffbad5a

p+1=0xbffbad5b

q=0xbffbad2c

q+1=0xbffbad30

p和q都同时加上一个1,但为什么最终出来的结果不想同呢?

在此之前我们需要知道C语言中sizeof(int)= 4,而sizeof(char)= 1. 

并且在16进制中“c”代表12,而“0”则代表满了16往前面进了一位。0x2c=44,0x30=48.  48-44=4.

很巧的是sizeof(int)的大小也为4.

同样的,0x5a=91,0x5b=92.  92-91=1.恰好也是一个sizeof(char)的大小。其实在这里我们可以看出一点,在指针的运算中当我们给一个指针加一时,他并不是给指针所对应的地址加一,而是加上一个sizeof(int/char/double)的大小。

这里需要借助上文的图来理解一下了

地址的储存是以字节为单位的,int型的大小为4个字节,而char型的大小为1个字节单位。

所以在指针加减运算时不能单纯地对其地址进行相应的加减,需要对应该类型的字节大小在其地址上相加减。

可能也许会有人纠结,到底能不能对地址进行加减呢?其实呢这个问题是没有意义的。举一个很简单的例子吧

 拿int型来说,一个int型是4个字节大小,如果对其地址加一,但是其始终是有四个字节大小,这一点是不会改变的,在加一之后,该单元的3个字节难道会与后面一个单元的第一个字节再组成一个新的单位的int吗?这显然是不合理且无意义的。所以咱也不必过于纠结这一点。同样的对指针进行乘除也是没有意义的。

补充:0地址

0地址在我们内存中当然也是存在的,只不过我们不能接触到,所以在我们编写代码时0地址是不能出现在我们的代码当中的。

那么0地址是怎么一回事儿呢?要知道在我们所有的程序运行时,我们的操作系统都会给它一个从0地址开始的虚拟的地址空间,也就是说所有的程序在运行时都以为自己有一片从0开始的连续的地址空间,至于这空间的顶值是多少呢。如果是32位的机器的话,这个顶值就是4个g。

如果在写代码过程中需要用到0地址,NULL便是我们可以用来表示0地址的一个东西。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值