指针深入分析
2009年11月06日

指针学习资料(zollty-2009)


1.指向普通变量或者某个数或者字符串。例如

int j,a[10];

int *pointer_1,*pointer_2;

pointer_1=&j;

pointer_2=&a[7];

 

2.指针作为函数参数。例如

 (主函数部分)

pointer_1=&a;pointer_2=&b;

if(a<b) swap(pointer_1,pointer_2);

(交换两数的函数)

void swap(int *p1,int *p2)

{ int temp;

  temp=*p1;

  *p1=*p2;

  *p2=temp;

}

swap接受主函数传来的两个数a和b的地址,用p1和p2去指代,然后交换p1和p2的内容,则在内存上引起了改变,所以原来的a和b所对应的值改变。

 

3.指向数组首地址【例1】

对于一维数组:p=a; 调用:*(a+i)或者*(p+i)

对于二维数组:p=a[0]; 调用:*(a[0]+i)或者*(p+i)

 

4.指向数组的每行【例2】

形如:

int a[3][4];

int (*p)[4]; p=a;

调用:*(*(p+i)+j)

 

5. 指向函数。例如

int max(int x,int y);

n=max(a,b);

可以改作:

int (*p)(int,int);

p=max;

n=p(a,b);

 

6.返回指针值的函数。例如

int *a(int x,int y);

 

7.指针数组。【例3】

它是一个数组,例如

int *p[4];

char *name[]={"BASIC","C++","PASCAL","COBOL","SQL"};

 

8.指向指针的指针【例4】

形如 char **p;

 

9.指针的运算

(1)减法:如果两个指针指向同一数组,则两个指针之差为它们之间间隔的元素个数,比如p1指向a[1],p2指向a[4],则p2-p1=3。而p1+p2无意义;

(2)比较,>、<、=,比较的是同一数组中的元素的先后顺序;

(3)赋值,同类型的指针才能相互赋值,若p1为int型,p2为float型,则可以强制转换p1=(int *)p2;

(4)可以令p=NULL,即使指针指向地址为0的单元,因为指针在未赋值以前指向的是一个未知的地址,这是非常危险的,所以不妨在引用它以前就把NULL赋值给它。

 

【例】

 

1.普通指针指向多维数组

#include <iostream>

using namespace std;

int main()

{ int a[3][4]={1,3,5,7,9,11,13,15,17,19,21,23};

  int *p;

  for(p=a[0];p<a[0]+12;)     //a[0]代表数组a[3][4]的首地址,而a代表的是数组第一行的首地址,所以指针a与指针a[0]不同.

         cout<<*p++<<" ";

  cout<<endl;

  return 0;

}

 

2.行指针指向多位数组

形如(*p)[10],用法为*(*(p+i)+j)

#include <iostream>

using namespace std;

int main()

{ int a[3][4]={1,3,5,7,9,11,13,15,17,19,21,23};

  int (*p)[4],i,j;

  cin>>i>>j;

  p=a;                         //a代表的是数组第一行的首地址.

  cout<<*(*(p+i)+j)<<endl;

  return 0;

}

 

3.用来装字符串的数组

方法一:(指针法)

#include <iostream>

using namespace std;

int main()

{ char *name[]={"BASIC","C++","PASCAL","COBOL","SQL"};

  cout<<name[0]<<endl;

  return 0;

注意:输出的时候用的是name[0]而不是name或者*name[0],后面(例4)将说明原因。

}

方法二:(字符数组法)

#include <iostream>

using namespace std;

int main()

{ char name[][30]={"BASIC","C++","PASCAL","COBOL","SQL"};

  cout<<name[0]<<endl;

  return 0;

}

注意:方法二不适合类似于int a[2][3]={ {5,8,6},{3,2,4} };的数组(此时若想输出a[0]则是错误的做法)。

 

方法三:(字符串数组法)

#include <iostream>

#include <string>

using namespace std;

int main()

{ string name[]={"BASIC","C++","PASCAL","COBOL","SQL"};

  cout<<name[0]<<endl;

  return 0;

}

 

4.

#include <iostream>

using namespace std;

int main()

{ char **p;

  char *name[]={"BASIC","PASCAL","C++","COBOL","SQL"};

  p=name+2;

  cout<<*p<<endl;

  cout<<**p<<endl;

  return 0;

}

分析:*p=name[2]= "C++"的首地址,(通常所说一个字符串的“地址”实际上指的是它的“首地址”或者“起始地址”),用cout 输出的不是字符串的首地址而是整个字符串(只要把一个字符串的起始地址给它,cout就一直往后面输出,直到遇到结束符'\0'为止),

为了证明这一点,请看:string a=“finish”;cout<<a<<endl;变量a中只是存放的“finish”的首地址,但是用cout却是把整个“finish”字符串全部输出。故:

例4,第一个*p输出"C++",第二个**p代表"C++"首地址的内容,即C。

例3中,name[0]为"BASIC"的首地址,输出"BASIC",若换成*name[0]则输出B,若换成name则代表的是整个数组的首地址(而不是单个字符串"BASIC"的地址),所以此时输出的是整个数组的首地址。若用*name则输出整个数组的首地址的内容,即"BASIC"(可见字符串的首地址内容为单个字符,一维数组首地址的内容为它的第一个元素,对于二维数组,比如a[3][4],a代表的是第一行的地址,所以cout<<a输出第一行的地址,而cout<<*a则是错误的用法,因为不可能将第一行的所有元素全部输出,提醒:注意一维数组和二维数组的区别)。



C/C++指针实战经验(zollty-2014)


一、两个典型例子

测试环境A:

电脑:笔记本电脑,WIN 7 32位 , Intel i7-2640 2.8GHz CPU,4G内存

软件:VC++ 6.0

 

1、指针和数组

#include <iostream>
using namespace std;
int main()
{ 
  int a[2]={2,8}; //一维数组
  int *p; //指向一维数组的指针
  p=a; //等价于p=&a[0]
  //调用方式:*(p+i)或*(a+i)
  cout<<*(++p)<<endl; 
  return 0;
}

蓝色部分输出结果为:

8

如果换成:

cout<<*(p++)<<endl;

则输出:

2

还有一种常见用法:

cout<<*p++<<endl;

其实是上面那种的简写,仍然等于2

 

2、下面程序的输出是

#include<iostream >
using namespace std;
void prt(int *y, int *z) {
    cout<<++*y<<","<<*(z++)<<endl;
}
int b=40;
int c=20;
int main() { 
    prt(&b,&c); // 41 20
    prt(&b,&c); // 42 20
    return 0;
}

输出:

41,20

42,20

解析:*(z++)这是指针地址后移一位,不影响*z的值。前者,++*y则会对指向的值造成影响。