return0;
}
但是我们上面的标注行
//P2ARR ap2a[2]={a,b};//------错误写法-------
则是应该这样理解:
数组就是数组,指针就是指针,虽然编译器需要的时候
会进行一定的转换,但是我们不能认为那些转换是“想当然“的。
上面这一行,编译器就没有为我们进行类型转换的,所以我们要自己动手了。
//P2ARRap2a[2]={&a,&b};//------正确写法-------
上面这些文字,主要一点就是说明了,有些行为是编译器完成的,而且还有一些
诡秘。
但是我们要知道编译器做了那些,那些是要完成自己做的。
重要的一点,
就是我们学会如何去简单使用typedef定义类型.
一、数组指针
(1)数组指针的声明
如:
int(*arrPtr)[10]=NULL; //arrPtr为一指针,指向一个具有10个元素的数组,这些元素的类型为int型
注意:
上面的括号不能省略,如果没有括号,则变成一个指针数组:
int*arrPtr[10];表示arrPtr是一数组(而非指针),且该数组元素的类型为int型指针。
(2)数组指针的操作
*arrPtr表示arrPtr所指向的数组,因此(*arrPtr)[i]会返回数组的第i+1个元素值。
更进一步,(*arrPtr)[i]是等价于*((*arrPtr)+i)的,因此当i=0时,**arrPtr就表示数组第一个元素的值了。
(3)给数组指针赋值
注意以下例子:
inta[10];
int(*arrPtr)[10]=NULL;
arrPtr=a; //错误:
指针类型符
原因是,数组的名字(比如上面例子中的a)会被隐式地转换为指针,但它指向的是数组的第一个元素,而不是指向整个数组(试想想,可以通过*(a+i)来获取某个元素的值,道理一样)。
因此上面的a会隐式地转换成int*类型(且指向a[0]),也就是说,若想把arrPtr指向数组a,需要作以下的显示类型转换:
arrPtr=(int (*)[10])a; //10可省略
再看看以下列子:
int(*arrPtr)[10]=NULL;
intmatrix[3][10];
arrPtr=matrix; //正确,因为matrix会隐式地转换为指向matrix[0][10]的指针,与arrPtr类型一致。
再往前走一步:
既然arrPtr=matrix,那么**arrPtr=**matrix,而**arrPtr表示的是matrix[0][10]第一个元素的值,因此**matrix也是表示matrix[0][10]第一个元素的值,即matrix[0][0],如此类推,如果matrix是一个多维数组(大于两维),那可通过**...**matrix这种形式来获取matrix[0][0]...[0][0]的值,即**...**matrix=matrix[0][0]...[0][0]。
二、指针数组
这比较常用,在此就不多赘述。
指针的指针,数组指针,指针数组(2008-11-1111:
26:
23)
标签:
杂谈
分类:
编程学习
指针的指针
voidFindCredit(int**);
main()
{
intvals[]={7,6,5,-4,3,2,1,0};
int*fp=vals;
FindCredit(&fp);
printf("%d\n",*fp);
}
voidFindCredit(int**fpp)
{
while(**fpp!
=0)
if(**fpp<0)break;
else(*fpp)++;
}
首先用一个数组的地址初始化指针fp,然后把该指针的地址作为实参传递给函数FindCredit()。
FindCredit()函数通过表达式**fpp间接地得到数组中的数据。
为遍历数组以找到一个负值,FindCredit()函数进行自增运算的对象是调用者的指向数组的指针,而不是它自己的指向调用者指针的指针。
语句(*fpp)++就是对形参指针指向的指针进行自增运算的。
但是因为*运算符高于++运算符,所以圆括号在这里是必须的,如果没有圆括号,那么++运算符将作用于二重指针fpp上。
指向指针数组的指针
指针的指针另一用法旧处理指针数组。
有些程序员喜欢用指针数组来代替多维数组,一个常见的用法就是处理字符串。
char*Names[]=
{
"Bill",
"Sam",
"Jim",
"Paul",
"Charles",
0
};
main()
{
char**nm=Names;
while(*nm!
=0)printf("%s\n",*nm++);
}
先用字符型指针数组Names的地址来初始化指针nm。
每次printf()的调用都首先传递指针nm指向的字符型指针,然后对nm进行自增运算使其指向数组的下一个元素(还是指针)。
注意完成上述认为的语法为*nm++,它首先取得指针指向的内容,然后使指针自增。
注意数组中的最后一个元素被初始化为0,while循环以次来判断是否到了数组末尾。
具有零值的指针常常被用做循环数组的终止符。
程序员称零值指针为空指针(NULL)。
采用空指针作为终止符,在树种增删元素时,就不必改动遍历数组的代码,因为此时数组仍然以空指针作为结束
指向指针的指针,很早以前在说指针的时候说过,但后来发现很多人还是比较难以理解,这一次我们再次仔细说一说指向指针的指针。
先看下面的代码,注意看代码中的注解:
#include
#include
usingnamespacestd;
voidprint_char(char*array[],intlen);
//函数原形声明voidmain(void){
//-----------------------------段1----------
char*a[]={"abc","cde","fgh"};//字符指针数组
char**b=a;
//定义一个指向指针的指针,
并赋予指针数组首地址所指向的第一个字符串的地址也就是abc\0字符串的首地址
cout<<*b<<"|"<<*(b+1)<<"|"<<*(b+2)< //-----------------------------段2----------
char*test[]={"abc","cde","fgh"};
//注意这里是引号,表示是字符串,以后的地址每加1就是加4位(在32位系统上)
intnum=sizeof(test)/sizeof(char*);
//计算字符串个数
print_char(test,num);
cin.get();
}
voidprint_char(char*array[],intlen)
//当调用的时候传递进来的不是数组,而是字符指针他每加1也就是加上sizeof(char*)的长度
{
for(inti=0;i {
cout<<*array++< } } 下面我们来仔细说明一下字符指针数组和指向指针的指针,段1中的程序是下面的样子:
char*a[]={"abc","cde","fgh"};
char**b=a;
cout<<*b<<"|"<<*(b+1)<<"|"<<*(b+2)< char*a[]定义了一个指针数组,注意不是char[],char[]是不能同时初始化为三个字符的,定义以后的a[]其实内部有三个内存位置,分别存储了abc\0,cde\0,fgh\0,三个字符串的起始地址,而这三个位置的内存地址却不是这三个字符串的起始地址,在这个例子中a[]是存储在栈空间内的,而三个字符串却是存储在静态内存空间内的const区域中的,接下去我们看到了char**b=a;这里定义了一个指向指针的指针,如果你写成char*b=a;那么是错误的,因为编译器会返回一个无法将char**[3]转换给char*的错误,b=a的赋值,实际上是把a的首地址赋给了b,由于b是一个指向指针的指针,程序的输出
cout<<*b<<"|"<<*(b+1)<<"|"<<*(b+2)<
结果是
abccdefgh
可以看出每一次内存地址的+1操作事实上是一次加sizeof(char*)的操作,我们在32位的系统中sizeof(char*)的长度是4,所以每加1也就是+4,实际上是*a[]内部三个位置的+1,所以*(b+1)的结果自然就是cde了,我们这时候可能会问,为什么输出是cde而不是c一个呢?
答案是这样的,在c++中,输出字符指针就是输出字符串,程序会自动在遇到\0后停止。
我们最后分析一下段2中的代码,段2中我们调用了print_array()这个函数,这个函数中形式参数是char*array[]和代码中的char*test[]一样,同为字符指针,当你把参数传递过来的时候,事实上不是把数组内容传递过来,test的首地址传递了进来,由于array是指针,所以在内存中它在栈区,具有变量一样的性质,可以为左值,所以我们输出写成了:
cout<<*array++<当然我们也可以改写为:
cout<这里在循环中的每次加1操作和段1代码总的道理是一样的,注意看下面的图!
到这里这两个非常重要的知识点我们都说完了,说归说,要想透彻理解希望读者多动手,多观察,熟能生巧。
下面是内存结构示意图:
最近好像是跟指针卯上了,发现以前真的学得不好,太多的东西是模模糊糊的。
可能是因为S现在做的项目有太多地方使用到指针,而且有的时候用到复杂的指针,所以才觉得有必要好好的研究下,这样可以减轻S的负担,也为我以后做准备吧。
要搞清一个指针首先必须搞清指针的四方面的内容:
指针的类型,指针所指向的类型,指针的值或者叫