深入理解指针

一.前言

个人简介:大数据本科在读

个人专栏:C语言系统学习

CSDN主页:Monodye

每日鸡汤:永远相信美好的事情即将发生

二.内存和地址

     在讲内存之前我们先来举这样一个例子,在大学里我们每个人都有自己的寝室,如果你的导员想找一个人的时候,那么他会先去找这个人在哪个公寓,那层楼,哪个寝室,这样就可以很快速大的 完成这个在存在大量数据时快速查找的问题。

     而计算机的内存我们常见的有8GB,16GB,32GB,等等在这么大的内存中,数据其实也是类似于我们上面说的这样,内存会编号,当我们需要调用内存中的一些数据的时候,就会很快的找到需要的数据,通常计算机的内存以一个字节为最小单位,并对其进行编号。而这个编号就是这个字节里存放内容的地址。

二. 指针变量和地址

      2.1取地址操作符

              理解了内存和地址之后,我们来说说C语言里的创建变量时内存的操作:

    【 比如这个简单的代码,他其实是先创建了一个a变量,在内存中开辟了一个大小为4个字节的空间,对齐进行编号,并把a的值存放进去。

   那么我们如何获取a的地址呢,那么我们需要学习一个符号“&”(取地址操作符)

printf("%p",&a);

      这样就会返回a四个字节的第一个字节,因为内存是连续存放的,所以我们只需要直到他的第一个字节,后面的地址就可以顺藤摸瓜进行访问。 

2.2指针变量

     经过我们上面的学习我们可以用&符号将一个变量的地址拿到,有时我们也需要把这个地址存放起来,这时我们就需要使用指针变量将其存放起来

int* ,中*说明p是一个指针变量,而int说明p返回的是整形数据。

 

我们说解引用操作符(&),可以取一个数据的地址,而指针又可以存放一个数据的地址,所以这两者经常一起使用

int a=10;
int*p=&a;//表示将a的地址传给了指针p

2.3指针变量的大小

    关于指针变量的大小,我们常常会根据机器32位或64位进行讨论,计算机里存放的是二进制,所以当32位转化位为地址时需要32个bite位,即4个字节。64位即64个bite位需要8个字节。

     所以如果【指针变量要存放地址的话那么他至少需要4个字节,注意指针变量的大小与其类型无关,无论是什么类型都是4或8个字节,而它的类型影响的是指针变量一次可以操作的最大限度,这个以后再说。

结论:
32位平台下地址是32个bit位,指针变量⼤⼩是4个字节
64位平台下地址是64个bit位,指针变量⼤⼩是8个字节
注意指针变量的⼤⼩和类型是⽆关的,只要指针类型的变量,在相同的平台下,⼤⼩都是相同的。

  2.4指针类型的意义

    既然每个指针变量的类型都是4或8个字节,那么这么多的指针类型又有什么用呢,仔细观察下面两个代码:

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

运行这两个代码你会发现,第一个代码会将n的值修改为0,而第二个代码则将会把n的值修改为0x11223300我们猜测: 

  •        int*的指针可以访问数据四个字节所以第一个代码将n修改为0
  •        char*的指针可以访问一个字节所以n的值修改了一个字节0x11223300

总结:

指针的类型决定了,对指针解引⽤的时候有多⼤的权限(⼀次能操作⼏个字节)。

 2.4 void*指针

下面给大家介绍一个特殊的指针void*指针,它可以存放各种类型的地址,但是他有一个特点:

   不能直接进行指针的+-整数,以及解引用(&)操作。

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

例如这个代码就会报错,因为对void*进行了解引用的操作。 

三.const的作用

    我们知道变量的值是可以进行修改的,变量将地址传给指针,通过指针也可以修改这个变量,那么如果我们想给变量加一个修饰,使它的值不可被修改是否可以呢,答案是肯定的:

    

#include <stdio.h>
int main()
{
 int m = 0;
 m = 20;//m是可以修改的
 const int n = 0;
 n = 20;//n是不能被修改的
 return 0;
}

经过const修饰后,n的值就不可以再发生变化,但如果我们同过指针间接的修改n的值其实也是可以的,但是这打破了语法规则,不建议使用,比如这样:

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

那么是否可以将指针的值也固定住呢,运行下面代码:

#include <stdio.h>
//代码1
void test1()
{
 int n = 10;
 int m = 20;
 int *p = &n;
 *p = 20;//ok?
 p = &m; //ok?
}
void test2()
{
 //代码2
 int n = 10;
 int m = 20;
 const int* p = &n;
 *p = 20;//ok?
 p = &m; //ok?
}
void test3()
{
 int n = 10;
 int m = 20;
 int *const p = &n;
 *p = 20;
 p = &m; //ok?
}
void test4()
{
 int n = 10;
 int m = 20;
 int const * const p = &n;
 *p = 20; //ok?
 p = &m; //ok?
}
int main()
{
 //测试⽆const修饰的情况
 test1();
 //测试const放在*的左边情况
 test2();
 //测试const放在*的右边情况
 test3();
 //测试*的左右两边都有const
 test4();
 return 0;
}

 

我们可以得到以下结论:

• const如果放在*的左边,修饰的是指针指向的内容,保证指针指向的内容不能通过指针来改变。
但是指针变量本⾝的内容可变。
• const如果放在*的右边,修饰的是指针变量本⾝,保证了指针变量的内容不能修改,但是指针指
向的内容,可以通过指针改变。

四.野指针

    野指针:即指针指向的地址是随机的,不确定的。(非常危险)

   4.1 野指针成因:

1.指针未初始化:

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

2.指针访问越界 :

#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;
}

  3.指针指向空间被释放:

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

五.assert断言:

assert()的作用是判断括号内的语句是否符合条件,如果不符合就会终止运行报错,可以用于程序的检验。

使用时要加:

#include<assert.h>

注意:Debug版本下,可以使用assert断言,而release版本下,直接被优化,这是为用户的体验效率考虑的。

六.传值和传址调用:

#include <stdio.h>
void Swap1(int x, int y)
{
 int tmp = x;
 x = y;
 y = tmp;
}
int main()
{
 int a = 0;
 int b = 0;
 scanf("%d %d", &a, &b);
 printf("交换前:a=%d b=%d\n", a, b);
 Swap1(a, b);
 printf("交换后:a=%d b=%d\n", a, b);
 return 0;
}

运行一下;

原因:实参传递给形参的时候,形参会单独创建⼀份临时空间来接收实参,对形参的修改不影响实
参。 所以Swap是失败的了。
如果我们使用指针将ab的地址传递的话会怎么样呢:

#include <stdio.h>
void Swap2(int*px, int*py)
{
 int tmp = 0;
 tmp = *px;
 *px = *py;
 *py = tmp;
}
int main()
{
 int a = 0;
 int b = 0;
 scanf("%d %d", &a, &b);
 printf("交换前:a=%d b=%d\n", a, b);
 Swap1(&a, &b);
 printf("交换后:a=%d b=%d\n", a, b);
 return 0;
}

ab的值交换成功。

 

  • 26
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值