2020-08-14

1、内存、地址与指针(第一节)

hello大家好,接下来就由我带领大家认识一下指针的世界。

1.1内存、地址

在介绍指针之前,先讲一讲计算机内存与地址。(换ppt)计算机内存可以看作是一辆火车,我们知道火车有很多节车厢,一节车厢就可以看作是一小块内存,坐过火车的人都知道,每一节车厢都有它自己的车厢编号,每一节车厢里呢,又有很多座位,每个座位也有属于他自己的独一无二的编号,通过座位号就可以唯一的确定一个座位,座位号就好像这一个内存块的偏移量,通过它可以唯一确定的数据存储的位置。

在这里注意一下,内存中的每个位置都有一个独一无二的地址标识,就好比火车上的座位都只有唯一的一个座位号与它对应一样,我们知道,火车票上都写着所对应的座位号,我们也应该知道,不可能一个座位对应着好几张火车票,也不可能一个火车票对应着很多座位。在计算机中也一样,内存中的每个位置都有一个独一无二的地址标识,内存中的每个位置都包含一个值。就像我们乘火车的时候,每个座位都有它自己唯一的座位号,这个座位号就是地址,每个座位号都对应一个座位,这个座位就是内存中的一个字节

好,简单了解了内存的定义以后,接下来我们讲一下什么是指针,什么是指针变量

-换ppt

1.2什么是指针,指针变量

上面我们已经简单了解了c语言中地址的含义了,我们要学的指针呢,其实就是地址的别称,也就是说,指针就是地址。

凡是出现“指针”的地方,都可以用“地址”代替,例如,变量的指针就是变量的地址。指针是地址的形象化,指针的意思是通

过它能找到以它为地址的内存单元。

我们来看这个例子,刚刚我说过,指针就是地址,凡是出现“指针”的地方,都可以用“地址”代替。在图中内存区域里,每个字节都对应着一个独一无二的地址标识,而这个地址,就是指针。我们再看下面这个例子,这里用了一个房子代表了一个内存块,房子的唯一的标识就是它的门牌号,右边这个小人儿想要去找住在某某路12号的小明该怎么走呢,很显然,知道了房子的地址,找到小明就只是时间的问题了。我们的指针就是扮演着指路的作用。在这里我需要强调一下,我们学习指针,必须要弄清楚储存单元的地址和储存单元的内容这两个概念的区别,就在这个例子中,房子的门牌号就相当于储存单元的地址,而房子里住的什么人,小明还是小红,就是储存单元的内容了。

我们知道,在c语言程序中,可以定义整型变量、浮点型变量、字符型变量等,同样的,我们也可以定义用来存放地址的变量,这就是指针变量。我们需要注意一下,有了基本数据类型的变量,就可以有指向这些类型变量的指针,就像这些例子,有了整形的变量,就可以有一个指向整形变量的指针,有了浮点型的变量,就可以有一个指向浮点型变量的指针。因此,指针变量是基本数据类型派生出来的类型,它不能离开基本类型而独立存在。大家看一下这个注意事项。

int    *ip;    /* 一个整型的指针 */
double *dp;    /* 一个 double 型的指针 */
float  *fp;    /* 一个浮点型的指针 */
char   *ch;     /* 一个字符型的指针 */

1.3操作符‘*’和‘&’

然后我介绍一下涉及到指针的两个运算符,取地址运算符和指针运算符,取地址运算符,顾名思义,就是取一个变量的地址,也就是取一个变量的指针,地址就是指针嘛,通过取地址运算符,就可以得到变量的地址。指针运算符也叫间接访问运算符,也就是说,通过指针运算符,可以得到指针所指向的对象,例如指针运算符加p代表指针变量p指向的对象。下面这两个例子,第一个呢就是把指针变量p指向的对象赋值给了K,第二个是把1赋值给了指针变量p指向的对象,也就是变量A。

然后给大家介绍一下怎么定义指针变量?和怎么引用指针变量?

定义指针变量的一般形式为:
类型名 * 指针变量名;注意一下,这里的* 是和变量名一起的而不是和类型名一起的,举个例子,(打开vc)int * a, b;这里只有a是指针变量,而b是整形变量。
引用指针变量的基本方式有这三种,
1)给指针变量赋值
p=&a;这里把a的地址赋值给了p
2)引用指针变量指向的变量
printf("%d",*p),这里用指针运算符把p所指向的对象用%d的形式打印了出来
3)引用指针变量的值
printf("%o",p),这里我们直接打印了p这个指针变量的值
在这里插入图片描述

在c语言程序中,有两种访问变量的方式,分别是直接按变量名进行的访问的“直接访问”方式。
和将变量i的地址存放在另一变量中,然后通过该变量来找到变量i的地址,从而访问i变量的“间接访问”方式

然后我们看一下他们对应的存取方式,第一种直接存取,将数字三直接存在了地址为2000的变量i中

第二种间接存取,i——pointer是一个指针变量,他的值为2000,我们用指针运算符得到这个指针所指向的对象,然后将3存入到了地址为2000的变量中

1.4 空指针,野指针

ok,接下来我介绍几种特殊的的指针,首先是野指针

野指针是指向一个已删除的对象或未申请访问受限内存区域的指针。
任何指针变量在刚被创建时如果未被赋值,它将会乱指一气,他有可能指向一个已经删除了的对象,或者系统中某个不能访问的关键的位置。这时候如果对未初始化的指针进行调用可能会发生莫名奇妙的错误。
如果定义指针变量的时候,还没想好给他赋予什么值,这时候我们可以借助NULL来对它赋值。标准 C 专门定义了一个标准预处理宏 NULL,其值为“空指针常量”,通常是 0 或者“((void*)0)”。 NULL 是可以赋值给任何类型指针的值,它的类型为 void*,而不是整数 0。这样,我们在定义指针变量的时候,如果没有实现想好要赋予什么值,就可以用NULL在赋值了。

(运行代码删掉NULL演示一遍)

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

1.5 void指针

void 指针是一种特殊的指针,它表示为“无类型指针”。由于 void 指针没有特定的类型,因此它可以指向任何类型的数据。也就是说,任何类型的指针都可以直接赋值给 void 指针,而无需进行其他相关的强制类型转换,如下面的示例代码所示:

    void *p1;
    int *p2;
    …
    p1 = p2;

虽然如此,但这并不意味着可以无需任何强制类型转换就将 void 指针直接赋给其他类型的指针,因为“空类型”可以包容“有类型”,而“有类型”则不能包容“空类型”。正如我们可以说“男人和女人都是人”,但不能说“人是男人”或者“人是女人”一样。
我们看一下错误代码,这里面p1是void指针它不可以赋值给指向整型的

    void *p1;
    int *p2;
    …
    p2 = p1;

由此可见,要将 void 指针赋值给其他类型的指针,必须进行强制类型转换。如下面的示例代码所示:

    void *p1;
    int *p2;
    …
    p2 = (int*)p1;

注意:1、避免对void指针进行算术操作
进行算法操作的指针必须确定知道其指向数据类型大小,也就是说必须知道内存目的地址的确切值。如下面的示例代码所示:我来运行一下这段代码看一下会出现什么样的结果

    char a[20]="qwertyuiopasdfghjkl";
    int *p=(int *)a;
    p++;
    printf("%s", p);

大家可以看到我们输出的字符串是从t开始的,这是为什么呢?

在上面的示例代码中,指针变量 p 的类型是“int*”,指向的类型是 int,被初始化为指向整型变量 a。

在执行语句“p++”时,编译器是这样处理的:把指针 p 的值加上了“sizeof(int)”(由于在 32 位系统中,int 占 4 字节,所以这里是被加上了 4),即 p 所指向的地址由原来的变量 a 的地址向高地址方向增加了 4 字节。但又由于 char 类型的长度是一个字节,所以语句“printf("%s",p)”将输出“tyuiopasdfghjkl”。
而对于 void 指针,编译器并不知道所指对象的大小,所以对 void 指针进行算术操作都是不合法的,但在GCC编译器中,指定“void*”的算法操作与“char*”一致。在真实的设计环境中,尽可能符合 ANSI 标准,尽量避免对 void 指针进行算术操作。

2、如果函数的参数可以是任意类型指针,应该将其参数声明为 void*
前面提到,void 指针可以指向任意类型的数据,同时任何类型的指针都可以直接赋值给 void 指针,而无需进行其他相关的强制类型转换。因此,在编程中,如果函数的参数可以是任意类型指针,那么应该使用 void 指针作为函数的形参,这样函数就可以接受任意数据类型的指针作为参数。

1.6 案例分析

定义一个指针变量、把变量地址赋值给指针、访问指针变量中可用地址的值。这些是通过使用一元运算符 * 来返回位于操作数所指定地址的变量的值

#include <stdio.h>
 
int main ()
{
   int  var = 20;   /* 实际变量的声明 */
   int  *ip;        /* 指针变量的声明 */
 
   ip = &var;  /* 在指针变量中存储 var 的地址 */
 
   printf("Address of var variable: %p\n", &var  );
 
   /* 在指针变量中存储的地址 */
   printf("Address stored in ip variable: %p\n", ip );
 
   /* 使用指针访问值 */
   printf("Value of *ip variable: %d\n", *ip );
 
   return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值