来自当知百科
跳转到: 导航搜索
  
4ca323a3bc5e5.jpg

目录

1、信息学的指针概述

指针

  在信息工程中指针是一个用来指示一个内存地址的计算机语言的变量或中央处理器(CPU)中寄存器(Register)。指针一般出现在比较近机器语言的语言,如汇编语言或C语言。面向对象的语言如Java一般避免用指针。指针一般指向一个函数或一个变量。在使用一个指针时,一个程序既可以直接使用这个指针所储存的内存地址,又可以使用这个地址里储存的变量或函数的值。

指针与C语言

  大家都认为,c语言之所以强大,以及其自由性,很大部分体现在其灵活的指针运用上。因此,说指针是c语言的灵魂,一点都不为过。同时,这种说法也让很多人产生误解,似乎只有c语言的指针才能算指针。basic不支持指针,在此不论。其实,pascal语言本身也是支持指针的。从最初的pascal发展至今的objectpascal,可以说在指针运用上,丝毫不会逊色于c语言的指针。

2、内存分配表

  计算机中的内存都是编址的,就像你家的地址一样。在程序编译或者运行的时候,系统(可以不关心具体是什么,可能是编译器,也可能是操作系统)开辟了一张表。每遇到一次声明语句(包括函数的传入参数的声明)都会开辟一个内存空间,并在表中增加一行纪录。记载着一些对应关系。(如图1所示)

  
----------------------------------------------------

  Declaration | ID Name Address Length

  ----------------------------------------------------

  int nP; | 1 nP 2000 2B

  char myChar; | 2 myChar 2002 1B

  int *myPointer; | 3 myPointer 2003 2B

  char *myPointer2; | 4 myPointer2 2005 2B

  ----------------------------------------------------

3、指针就是一个整数

  指针,是一个无符号整数(unsignedint),它是一个以当前系统寻址范围为取值范围的整数。32位系统下寻址能力(地址空间)是4G-byte(0~2^32-1)二进制表示长度为32bit(也就是4B)。

  int类型也正好如此取值。

例证(一)

  例证就是程序1得到的答案和程序2的答案一致。(不同机器可能需要调整一下pT的取值。)

  ----------------------------------------------------

  程序1

  #include <stdio.h>

  main()

  {

  char *pT;

  char t='h';

  pT=&t;

  putchar(*pT);

  }

  ----------------------------------------------------

  程序2

  #include <stdio.h>

  main()

  {

  char *pT;

  char t='h';

  pT=(char *)1245048;

  putchar(*pT);

  }

  ----------------------------------------------------

  加上(char *)是因为毕竟int 和char *不是一回事,需要强制转换,否则会有个警告。因为char*声明过的类型,一次访问1个sizeof(char)长度,double *声明过的类型,一次访问1个sizeof(double)长度。

  在汇编里int 类型和指针就是一回事了。因为不论是整数还是指针,执行自增的时候,都是其值加一。如果上文声明char*pT;,汇编语言中pT自增之后值为1245049,可是C语言中pT++之后pT值为1245049。如果32 位系统中, s上文声明int *pT;,汇编语言中pT 自增之后值为1245049,可是C 语言中pT++之后pT值为1245052。

  为什么DOS下面的TurboC,和Windows下VC的int类型不一样长。因为DOS是16位的,Windows是32位的,可以预见,在64位Windows 中编译,上文声明int*pT;,pT++之后pT值为1245056。

例证(二)

  那么,复杂的结构怎么分配空间呢?C语言的结构体(汇编语言对应为Record类型)按顺序分配空间。(如图2所示)

  
----------------------------------------------------

  int a[20];

  ----------------------------------------------------

  typedef struct st

  {

  double val;

  char c;

  struct st *next;

  } pst;

  ----------------------------------------------------

  pst pT[10];

  ----------------------------------------------------

  在32 位系统下,内存里面做如下分配(单位:H,16 进制);(如图3所示)

  
----------------------------------------------------

  变量 2000 2001 2002 2003 2004 2005 2006 … 204C 204D 204E 204F

  地址 a[0] a[1] … a[19]

  ----------------------------------------------------

  变量 2050 2051 … 2057 2058 2059 205A 205B 205C 205D 205E 205F

  地址 pst.val pst.c pst.next 无效 无效 无效

  ----------------------------------------------------

  这就说明了为什么sizeof(pst)=16而不是8。编译器把结构体的大小规定为结构体成员中大小最大的那个类型的整数倍。

  至于pT的存储,可以依例推得。总长为160,此不赘述。

  有个问题,如果执行pT++,答案是什么?是自增16,还是160?别忘了,pT 是常量,不能加减。

  所以,我们就可以声明:

  ----------------------------------------------------

  typedef struct BinTree

  {

  int value;

  struct BinTree *LeftChild;

  struct BinTree *RightChild;

  } BTree;

  ----------------------------------------------------

  用一个整数,代表一棵树的结点。把它赋给某个结点的LeftChild/RightChild值,就形成了上下级关系。只要无法找到一个路径,使得A->LC/RC->LC/RC...->LC/RC==A,这就构成了一棵二叉树。反之就成了图。

4、C 的按值传递

概述

  C中函数调用是按值传递的,传入参数在子函数中只是一个初值相等的副本,无法对传入参数作任何改动。但实际编程中,经常要改动传入参数的值。这一点我们可以用传入参数的地址而不是原参数本身,当对传入参数(地址)取(*)运算时,就可以直接在内存中修改,从而改动原想作为传入参数的参数值。

编程参数值

  ----------------------------------------------------

  #include <stdio.h>

  void inc(int *val)

  {

  (*val)++;

  }

  main()

  {

  int a=3;

  inc(&a);

  printf("%d" , a);

  }

  ----------------------------------------------------

  在执行inc(&a);时,系统在内存分配表里增加了一行“inc中的val”,其地址为新地址,值为&a。操作*val,即是在操作a 了。

5、*和&运算

  为了(*p)操作是这样一种运算,返回p 的值作为地址的那个空间的取值。(&p)则是这样一种运算,返回当时声明p时开辟的地址。显然可以用赋值语句对内存地址赋值。我们假设有这么两段内存地址空间,他们取值如下:(单位:H,16 进制)(如图4所示)

  
----------------------------------------------------

  地址 0000 ... 2000 2001 2002 2003 2004 ... 3000 3001 3002 3003 ...

  取值 ???? ... 01 30 00 00 30 00 03 20 9A

  ----------------------------------------------------

  假设有这么一段代码:(假设开辟空间时p 被分配给了3001H、3002H 两个位置)

  ----------------------------------------------------

  int *p;

  p=2003;

  *p=3000H

  ----------------------------------------------------

  **p的值为多少?

  **p=*(*(p))=*(*(2003H))=*(3000H)=0300H。

  那么&&p、*(&p)和&(*p)又等于多少?

  &&p=&(&(p))=&(3001H),此时出错了,3001H是个常数怎么可能有地址呢?

  *&p=*(&(p))=*(3001H)=2003H,也就是*&p=p。

  &*p=&(*(p))=&(3000H),读者可能以为&*p=p 此时出错了,3000H是个常数怎么可能有地址呢?

5'、另类的*和&

  两个地方要注意:

  在程序声明变量的时候的*,只是表明“它是一个整数,这个整数指向某个内存地址,一次访问sizeof(type)长度”。这点不要和(*)操作符混淆;

  在C++程序声明变量的时候的&,只是表明“它是一个引用,这个引用声明时不开辟新空间,它在内存分配表加入新的一行,该行内存地址等于和调用时传入的对应参数内存地址”。

  这点不要和(*)声明符,(&)操作符混淆。

  清华大学软件学院免试研究生面试就有问过这个问题。

6、双重指针(指向指针的指针)

  双重指针又是怎么一回事儿呢?综合2 的BTree 定义,和3的说法。对于一棵树,我们通常用它的根节点地址来表示这棵树。这就使“擒贼先擒王”。找到了树的根,其每个节点都可以找到。

  但是有时候我们需要对树进行删除节点,增加节点操作,往往考虑到删除根节点,增加的节点取代原来的根节点作为新根节点的情况。为了修改根节点这个“整数”,我们需要退一步,使用这个“整数”的内存地址,也就是指向这个“整数”的指针。在声明时,我们用2个*号,声明指向指针的指针。它的意思是“它是一个整数,这个整数指向某个内存地址,一次访问sizeof(int)长度,其值是一个整数,那个整数值指向某个内存地址,一次访问sizeof(BTree)长度。”。详见“数据结构.rar”有关“树”(06 文件夹)的程序代码。

7、指针数组、数组指针和指向函数的指针

  指针数组:就是一个整数数组,那个数组的各个元素都是整数,指向某个内存地址。

  数组指针:数组名本身就是一个指针,指向数组的首地址。注意这是一个常数。

  指向函数的指针:从二进制角度考虑,数组名是该数组数据段首地址,函数名就是该代码段的首地址,可以用“int*fun()”。在二进制层面,代码段和数据段什么区别?没什么区别。

  ----------------------------------------------------

  #include <stdio.h>

  void inc(int *val)

  {

  (*val)++;

  }

  main()

  {

  void (*fun)(int *);

  int a=3;

  fun=inc;

  (*fun)(&a);

  printf("%d" , a);

  }

  ----------------------------------------------------

8、指针的作用

  指针可以用来有效地表示复杂的数据结构,可以用于函数参数传递并达到更加灵活使用函数的目的.使C语言程序的设计具有灵活、实用、高效的特点。

9、指针网

  一个专门用于图书搜索的网站,指针图书网。自称全球最大中文图书检索网站。

其他解释:钟表的指针

  在日常生活中指针是仪器或钟表上的,可动的,一般狭长的,往往在一段尖的用来指示测量的数据的装置。

个人工具
名字空间

变换
查看
操作
导航
工具箱