C语言指针

什么是指针

在生活中,我们应邀去朋友家是不是首先要知道他家的地址,而他家的门牌号就可以让我找到一个在某个地方内唯一存在的地址,当我看到了这个门牌号,我就明白了我已经到了。在C语言中,指针与此类似,它是一个储存内存地址的变量,它指向一个内存位置,该位置存储了数据或另一个内存地址。

怎么来定义一个指针

下面这个程序定义了一个指针变量,*是在说明p是一个指针变量,int是指明了其类型,让我们来看一下它的运行结果

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

它们输出的的值居然是一样的,那么它们的地址是否也是一样呢,让我们来试一下

运行后我们发现它们的地址也是相同的,那它们的值是相同的也就可以说得通了,这也就和我开头写的“它是一个储存内存地址的变量,它指向一个内存位置,该位置存储了数据或另一个内存地址”相呼应了。

既然指针是一个储存内存地址的变量,那么它就具有变量的属性——可以修改,那么我们怎么来修改它呢,这里就要用到解引用操作符了,也就是(*),下面这个程序可以修改指针变量p的值,方便起见,就不运行了,读者想要尝试的话,可以手敲实践一下。

#include <stdio.h>
int main()
{
    int a=10;
    int*p=&a;
    printf("修改前p = %d \n",*p);
    *p=20;
    printf("修改后p = %d \n",*p);
}

指针也是有大小的,不过是取决于地址的大小。例如在32位平台下地址是32个比特位,即4个字节

在64位平台下地址是64个比特位,即8个字节。

指针的类型

一个变量可以有多种类型,比如浮点型,字符型,既然指针也是一种变量,那么它也应该具有类型,比如整数指针,浮点型指针,字符型指针。阅读了上面的文字后,你可能会问既然指针的大小取决于地址,那么指针类型有什么用呢?我们可以通过调试来观察一下其内存的变化。

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

我们可以观察到n的内存(图中红色数字)从0x11223344变为了0x00000000,至于图中数字为什么倒着放,是因为vs的是小端存储,感兴趣的可以自行了解。现在我来给这个程序做一点修改。

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

我们可以代码1中n的4个字节全部被改为0,而代码2中n只有一个字节被改为0,回到我们开始的问题,指针类型的作用就在于指针的类型决定了,对指针解引⽤的时候有多⼤的权限(⼀次能操作⼏个字节),int*的解引用就可以访问4个字节,而char*的解引用就只能访问1个字节。

指针运算

指针的基本运算有三种运算,分别是指针+-整数,指针-指针,指针的关系运算。为了减少篇幅,后续代码除必要外不展示运行结果截图。

(一)指针+-整数

因为数组在内存中是连续存放的,只要知道第⼀个元素的地址,顺藤摸⽠就能找到后⾯的所有元素,我们可以用它来访问数组的元素

#include <stdio.h>
int main()
{
    int arr[5]={1,2,3,4,5};
    int *p=&arr[0];
    int sz=sizeof(arr)/sizeof(arr[0]);
    for(int i=0;i<sz;i++)
    {
        printf("%d ",*(p+i));//这里就是指针加减整数
    }
    printf("\n");
    return 0;
}

我们再来写一个程序来观察指针+-整数的地址的变化

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

我们可以看出, char* 类型的指针变量+1跳过1个字节, int* 类型的指针变量+1跳过了4个字节。

(二)指针-指针

可以用指针-指针来计算字符串的长度

#include <stdio.h>
int my_strlen(char *s)
{
    char*p=s;
    while(*p!='\0')
    {
        p++;
    }
    return p-s; //指针-指针
}
int main()
{
    printf("%d\n",my_strlen("abcde"));
    return 0;
}

(三)指针的关系运算

1.相等性比较:可以使用相等运算符(==)和不等运算符(!=)来比较两个指针的值,这将比较它们是否指向相同的内存地址。

这里输出不相等。

#include <stdio.h>
int main()
{
    int arr[3]={1,2,3};
    int *ptr1=&arr[0];
    int *ptr2=&arr[1];
    if(*ptr1==*ptr2)
    {
        printf("相等\n");
    }
    else
        printf("不相等\n");
}

大小比较:指针也可以进行大小比较,但是这需要注意指针指向的类型。指针的大小比较是根据指向的内存地址而言的,指向地址较小的指针被认为是“较小”的。

这里输出*ptr1<*ptr2。

#include <stdio.h>
int main()
{
    int arr[3]={1,2,3};
    int *ptr1=&arr[0];
    int *ptr2=&arr[1];
    if(*ptr1<*ptr2)
    {
        printf("*ptr1<*ptr2\n");
    }
    else if(*ptr1>*ptr2)
    {
        printf("*ptr1>*ptr2\n");
    }
    else
        printf("*ptr1=*ptr2\n");
}

野指针

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

野指针成因:

(一)指针未初始化

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

(二)数组越界访问

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

(三)指针指向的空间释放

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

不要犯以上错误可以避免野指针的出现。

二级指针

指针变量也是变量,是变量就有地址,那指针变量的地址存放在哪⾥? 那就要用到二级指针了
#include <stdio.h>
int main()
{
    int a=10;
    int *p=&a;
    int**pp=&p;
    return 0;
}

这就是二级指针

指针数组

指针数组是指针还是数组? 我们类⽐⼀下,整型数组,是存放整型的数组,字符数组是存放字符的数组。 那指针数组呢?是存放指针的数组。
指针数组的每个元素都是⽤来存放地址(指针)的,指针数组的每个元素是地址,⼜可以指向⼀块区域。
  • 33
    点赞
  • 51
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值