《一》
懂得C语言的人都知道,C语言之所以强大,以及其*性,绝大部分体现在其灵活的指针运用上。因此,说指针是c语言的灵魂,一点都不为过。所以从我的标题加了个(一)也可以看出指针的重要性,我尽可能的向大家交代清楚我对于指针的理解。所以在讲解的过程中我尽可能的用代码加文字的描述方式,通过代码的分析来加深我们对于指针的理解,我给出的都是完整的代码,所以读者可以在看的过程中直接copy下去即可运行,希望下面的讲解能够对你有所帮助。
首先让我们来看看定义一个指针的一般形式为:
基类型 *指针变量名
看了上面的指针的定义形式,我们可能对于有些地方会有疑惑,如为什么要指定基类型呢?因为我们都知道整型和字符型在内存中占的字节数是不相同的,当我们进行指针的移动和指针的运算时,如果指针指向的是一个整型变量,那么指针移动一个位置就是移动4个字节,但是如果指针指向的是一个字符型的变量,那么指针移动的就是一个字节,因此我们必须规定指针变量所指向的基类型。
为了不枯燥的讲解我们来看看下面的代码吧。(注意:本博客的所有代码均使用vc6编译运行,所以可能有的规则跟C语言的稍有区别)
#include <stdio.h>
int main()
{
int a,b;
int *pointer_1,*pointer_2;
a=100;
b=200;
pointer_1=&a;
pointer_2=&b;
printf("--------------------变换前-------------------\n");
printf("a=%d\tb=%d\n",a,b);
printf("*pointer_1=%d\t*pointer_2=%d\n",*pointer_1,*pointer_2);
*pointer_1=300;
int c=500;
pointer_2=&c;
printf("--------------------变换后-------------------\n");
printf("a=%d\t*pointer_1=%d\n",a,*pointer_1);
printf("c=%d\tb=%d\t*pointer_2=%d\n",c,b,*pointer_2);
}
运行结果如下:
在此我们定义了两个整型指针int *pointer_1,*pointer_2;,它们分别指向变量a和b,值得注意的是*pointer_1和a、*pointer_2和b是共用同一个存储空间的,当我们在接下类的代码中改变 *pointer_1=300;时,由输出就可以看出来a的值也跟随发生了改变。但是当我们声明了一个 int c=500;之后,使用pointer_2=&c;,b的值不变,仅仅是改变*pointer_2,因为我仅仅是改变了*pointer_2指向了c的存储空间,如果有有兴趣的读者可以自己验证下如果我们修改了a的值之后*pointer_1的值会跟随一起改变,因为他们指向的是同一个存储空间。
接下来看看如何在函数的参数中来使用指针。
#include <stdio.h>
swap(int p1,int p2)
{
int temp;
temp=p1;
p1=p2;
p2=temp;
}
int main()
{
int a,b;
int *pointer_1,*pointer_2;
int c,d;
c=a;
d=b;
pointer_1=&a;
pointer_2=&b;
a=20;
b=30;
swap(a,b);
printf("a=%d\tb=%d\n",a,b);
printf("a=%d\tb=%d\n",*pointer_1,*pointer_2);
printf("c=%d\td=%d\n",c,d);
}
初步分析上面的代码,看似是要通过一个函数的调用来实现一个a、b的交换,还有就是通过c=a;、 d=b;来实现对c、d赋初值。先来看看下面的运行结果:
结果跟我们想象的不一样,a、b没有实现交换的原因是因为我们使用的是传值,而不是传址,所以调用的过程中做的处理就是把a、b的值复制到另外申请的两个空间p1、p2中去,因而交换操作是在p1、p2的空间中进行的,所以对于a、b的值并没有影响。c、d的初值为什么没有跟a、b的值一样呢,因为我们在初始化的过程中给c、d赋初值的时候a、b的并没有给定初值,所以a、b的初值是在编译的过程中由系统给定的,又因为我们申请的c、d的空间是跟a、b没有任何关系的,所以接下来再对a、b赋初值的时候c、d的初值并不会改变。
下一个代码:
#include <stdio.h>
swap(int *p1,int *p2)
{
int *temp;
temp=p1;
p1=p2;
p2=temp;
}
int main()
{
int a,b;
int *pointer_1,*pointer_2;
int c,d;
c=a;
d=b;
pointer_1=&a;
pointer_2=&b;
a=20;
b=30;
printf("********************调用前******************\n");
printf("a=%d\tb=%d\n",a,b);
swap(pointer_1,pointer_2);
printf("********************调用后******************\n");
printf("a=%d\tb=%d\n",a,b);
printf("*pointer_1=%d\t*pointer_2=%d\n",*pointer_1,*pointer_2);
printf("c=%d\td=%d\n",c,d);
return 0;
}
看看上面这个代码似乎满足了我们前面说的传址的要求,那先让我们来看看实验结果吧。
结果似乎也是出乎我们的意料之外,为什么使用了传值却还是没有能够实现呢?如果我们在调用函数中加上一句 printf("*p1=%d\t*p2=%d\n",*p1,*p2);,得到下面的结果:
从结果来看似乎告诉我们,我们已经实现交换了,但是为什么没有能够返回来呢?在这里要注意了,因为我们在函数的交换语句仅仅是改变了局部指针变量p1和p2的值,所以没有改变a、b的值,所以使用printf("*p1=%d\t*p2=%d\n",*p1,*p2);使得我们的确看到了a、b交换的假象,仅仅是改变了局部变量p1和p2的值。
下一个代码:
#include <stdio.h>
#include <stdlib.h>
swap(int *p1,int *p2)
{
int *temp;
temp=(int *)malloc(sizeof(int));
*temp=*p1;
*p1=*p2;
*p2=*temp;
free(temp);
}
int main()
{
int a,b;
int *pointer_1,*pointer_2;
int c,d;
c=a;
d=b;
pointer_1=&a;
pointer_2=&b;
a=20;
b=30;
printf("********************调用前******************\n");
printf("a=%d\tb=%d\n",a,b);
swap(pointer_1,pointer_2);
printf("********************调用后******************\n");
printf("a=%d\tb=%d\n",a,b);
printf("*pointer_1=%d\t*pointer_2=%d\n",*pointer_1,*pointer_2);
printf("c=%d\td=%d\n",c,d);
return 0;
}
看看也行结果:
最后终于出现了一个我们想要的结果了。从以上的分析读者自己也知道原因所在了吧,这里操作的才是p1、p2所指向的地址,才真正的做到了对于a、b存储空间的数值的交换。细心的读者可能看到了我们在代码中用了红色部分标记的代码,它完全可以用一句int temp;来替代,之所以我们在这里要用int *temp;无非是要大家牢记对于指针一些特殊的使用,如果我们没有这句temp=(int *)malloc(sizeof(int));,以上代码在编译的过程中是不会有任何错误的,但是在运行的过程中就会出现错误,所以通常情况下我们在使用指针的过程中,要特别注意野指针情况的出现,以免出现一些莫名奇妙的有错。
《二》
现在每篇博客都要求有摘要,但是如果我就这样直接接着上一篇的内容开始讲的话可能第一次看我博客的人都不知道我在说啥,所以我还是把前一篇博客的第一段作为摘要搬过来吧。
懂得C语言的人都知道,C语言之所以强大,以及其*性,绝大部分体现在其灵活的指针运用上。因此,说指针是c语言的灵魂,一点都不为过。所以从我的标题加了个(一)也可以看出指针的重要性,我尽可能的向大家交代清楚我对于指针的理解。所以在讲解的过程中我尽可能的用代码加文字的描述方式,通过代码的分析来加深我们对于指针的理解,我给出的都是完整的代码,所以读者可以在看的过程中直接copy下去即可运行,希望下面的讲解能够对你有所帮助。
在此也特地强调下,如果以后出现类似的情况时,我博客的第一段均作为摘要。如果已经在前面的博客中看过摘要的,那么重复的摘要部分可跳过不读,直接进入正文。
接着上一篇的指针部分,我们接下来看看数组的指针和指向数组的指针变量。数组的指针就是数组的起始地址,数组元素的指针是数组元素的地址。对于一个数组元素的引用我们通常可以使用两种方法:
1、下标法,如a[8]。
2、指针法。
其中使用指针法的优点是使得目标程序占内存少、运行速度快,从而使得其质量更高。为什么说指针具有这样的优点呢,我想还是有必要在此给出点我解释,因为指针在32位机器下占用4个字节,如果函数传输一个占用内存很大的对象例如:int a[2000],显然用指针引用传送简单,节省了内存,也节省了用于复制对象的时间;如果我们用下标法来引用数组,还得去取数组的起始地址,通过base + offset再转换为直接寻址,比指针多了操作。
从以上可以看出,指针能力很强,能完成许多事情,C的精髓就在于指针,使得C能接近ASM的效率。所以我们在写编写程序的时候有必要充分利用指针的优点,编写出高效的C语言代码。
下面来看看一个代码:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int a[8];
int *p;
//***************************用下标法打印a数组**************************//
printf("\n***************************用下标法打印a数组**************************\n");
for(int i=0;i<8;i++)
{
a[i]=i;
printf("a[%d]=%d\t",i,a[i]);
}
printf("\n****************************end end end******************************\n");
//****************************end end end******************************//
//***************************使用指针变量打印**************************//
printf("\n***************************使用指针变量打印**************************\n");
p=a;
for(int j=0;j<8;j++)
{
printf("p%d=%d\t",j,*p++);
}
printf("\n****************************end end end******************************\n");
//****************************end end end******************************//
printf("\n");
//************************用数组名指针运算打印*************************//
printf("\n************************用数组名指针运算打印*************************\n");
for(int k=0;k<8;k++)
{
printf("a[%d]=%d\t",k,*(a+k));
}
printf("\n****************************end end end******************************\n");
//****************************end end end******************************//
//****************************打印二维数组b的值地址********************//
printf("\n***************************打印二维数组b的值及地址*******************\n");
int b[4][4];
for(int n=0;n<4;n++)
{
for(int m=0;m<4;m++)
{
b[n][m]=n*m;
printf("%d\t",b[n][m]);
printf("%d\t",&b[n][m]);
}
printf("\n");
}
printf("\n****************************end end end******************************\n");
//****************************end end end******************************//
int *pp=&b[0][0];
int **ppp=&pp;
//*********************二维数组b的地址、以及pp和*ppp的值****************//
printf("\n*********************二维数组b的地址、以及pp和*ppp的值****************\n");
printf("\n&b[0][0]=%d\tpp=%d\t*ppp=%d\n",&b[0][0],pp,*ppp);
printf("\n****************************end end end******************************\n");
//****************************end end end******************************//
//*****************二维数组b[0][0]、以及*pp和**ppp的值******************//
printf("\n*****************二维数组b[0][0]、以及*pp和**ppp的值******************\n");
printf("\nb[0][0]=%d\t*pp=%d\t**ppp=%d\n",b[0][0],*pp,**ppp);
printf("\n****************************end end end******************************\n");
//****************************end end end******************************//
//***************************使用指针变量打印**************************//
printf("\n***************************使用指针变量打印**************************\n");
for(pp=&b[0][0];pp<(&b[0][0]+16);pp++)
printf("%d\t",*pp);
printf("\n****************************end end end******************************\n");
//****************************end end end******************************//
printf("\n&pp=%d\t&ppp=%d\n",&pp,&ppp);
printf("\nppp=%d\t*ppp=%d\n",ppp,*ppp);
printf("%d\t",*(*ppp-1));
return 0;
}
在写上面的代码时,我加上了很多的注释,和打印说明语句,使得代码看起来不怎么美观,但是它丝毫不会影响我们对于代码的阅读,下面先让我们来看看运行结果后再来对齐进行分析。
上面的图片可能有点偏大。因为图片看起来效果更好些,所以我还是把图片传上来的同时也把打印结果复制了一份如下,如果图片因为网络原因打不开就看下面的运行结果:
***************************用下标法打印a数组**************************
a[0]=0 a[1]=1 a[2]=2 a[3]=3 a[4]=4 a[5]=5 a[6]=6 a[7]=7
****************************end end end******************************
***************************使用指针变量打印**************************
p0=0 p1=1 p2=2 p3=3 p4=4 p5=5 p6=6 p7=7
****************************end end end******************************
************************用数组名指针运算打印*************************
a[0]=0 a[1]=1 a[2]=2 a[3]=3 a[4]=4 a[5]=5 a[6]=6 a[7]=7
****************************end end end******************************
***************************打印二维数组b的值及地址*******************
0 1244944 0 1244948 0 1244952 0 1244956
0 1244960 1 1244964 2 1244968 3 1244972
0 1244976 2 1244980 4 1244984 6 1244988
0 1244992 3 1244996 6 1245000 9 1245004
****************************end end end******************************
*********************二维数组b的地址、以及pp和*ppp的值****************
&b[0][0]=1244944 pp=1244944 *ppp=1244944
****************************end end end******************************
*****************二维数组b[0][0]、以及*pp和**ppp的值******************
b[0][0]=0 *pp=0 **ppp=0
****************************end end end******************************
***************************使用指针变量打印**************************
0 0 0 0 0 1 2 3 0 2
4 6 0 3 6 9
****************************end end end******************************
&pp=1244932 &ppp=1244928
ppp=1244932 *ppp=1245008
9 Press any key to continue
首先来看看我们使用的三种打印一维数组a的方法,都成功的对a数组中的每个元素进行了打印,接下类是一个打印二维数组b的过程,在打印数组b中每个元素的同时我们也打印出了它相应的地址,细心的读者可能发信地址间的规律,因为我们声明的是int型,所以每个元素占用4个字节,相邻元素间的地址之差为4。
接下来我们使用了一个指针pp和一个指向指针的指针ppp,在使用指针的指针ppp的过程中要尤其注意它的使用。通过打印语句我们打印出了&b[0][0],pp,*ppp,其都具有相同的结果,都为二维数组b[0][0]的地址,所以接下来打印的b[0][0],*pp,**ppp均为b[0][0]的值,接下来我们采用指针的方法来成功的打印了二维数组b。
在接下来我们打印出了指针pp、双指针ppp的地址,同时也打印了ppp和*ppp的值,注意了*ppp的值和最后一次打印的数组元素的地址的关系,为什么会出现这样的结果呢,因为我们前面使用了一句int **ppp=&pp;,使得*ppp和pp指向的是同一个存储空间,其地址为&pp=1244932 ,所以在改变pp的值得时候,*ppp的值也在跟随其改变。所以细心的读者可能发现了在最后一句打印语句printf("%d\t",*(*ppp-1));中,我们使用了*(*ppp-1)才能成功的打印出二维数组b的最后一个最后一个元素。
如何采用二维指针类打印数组呢,请看下面的代码:
#include <stdio.h>
int main()
{
//****************************打印二维数组b的值地址********************//
printf("\n***************************打印二维数组b的值及地址*******************\n");
int b[4][4];
for(int n=0;n<4;n++)
{
for(int m=0;m<4;m++)
{
b[n][m]=n*m;
printf("%d\t",b[n][m]);
printf("%d\t",&b[n][m]);
}
printf("\n");
}
printf("\n****************************end end end******************************\n");
//****************************end end end******************************//
int *pp=&b[0][0];
int **ppp=&pp;
//****************************使用二维指针的打印数组b*******************//
printf("\n***************************使用二维指针的打印数组b*******************\n");
for(*ppp;*ppp<(&b[0][0]+16);(*ppp)++)
printf("%d\t",**ppp);
printf("\n****************************end end end******************************\n");
//****************************end end end******************************//
return 0;
}
运行结果如下:
注意代码中我们的红色标记部分,很多人在采用二维指针**ppp进行打印的时候最容易出错的地方,很多人使用的是如下方式:
for(*ppp;*ppp<(&b[0][0]+16);*ppp++)
看似没有问题,似乎能得到正确的结果,但是我们仔细分析就会发现其中的问题所在,因为++的优先级高于*,所以首先进行的是ppp++运算,然后才是*ppp,这样的话就出现我们前面所讲的野指针的问题了。所以在调用printf("%d\t",**ppp);就会出现内存错误。所以在此我们需要加上一个括号(*ppp)++,这样*ppp中的才是b[0][0]的地址,接下来通过使用++操作和printf("%d\t",**ppp);才能成功的打印出二维数组b的元素。
《三》
但凡人都是急功近利和有惰性的,我就是个例子。不想每篇博客都写一个摘要和那些大段的文字描述,但是为了让一些新的读者朋友了解我的博客内容,我还是像前面说的那样,把第一篇关于指针的摘要搬过来,因为我写的这几篇都是关于指针的,所以没有必要每篇一个摘要,在此就偷偷懒了,如果读过我前面两篇C指针博客的朋友可以跳过这篇博客前面的摘要不读,直接进入主题部分。
懂得C语言的人都知道,C语言之所以强大,以及其*性,绝大部分体现在其灵活的指针运用上。因此,说指针是c语言的灵魂,一点都不为过。所以从我的标题加了个(一)也可以看出指针的重要性,我尽可能的向大家交代清楚我对于指针的理解。所以在讲解的过程中我尽可能的用代码加文字的描述方式,通过代码的分析来加深我们对于指针的理解,我给出的都是完整的代码,所以读者可以在看的过程中直接copy下去即可运行,希望下面的讲解能够对你有所帮助。
在C语言中我们可以使用两种方法来访问一个字符串。
1、用字符数组存放一个字符串
char str[ ]="this is str!!!";
在此str是一个数组名,代表字符串数组的首地址。
2、用字符指针指向一个字符串
char *str="this is str,too";
C语言对于字符串常量是按照字符数组的方式来进行处理的,在内存开辟了一个字符数组来存放字符串常量。在此的str被定义为一个指针变量,指向字符型数组,它只能指向一个字符变量和其他的字符数据。输出都是使用printf("%s",str);,在此过程中系统会先输出一个str所指向的字符数据,然后使用str加1的方法输出下一个字符,知道遇到'\0'串结束符为止,在内存中的字符串的最后都被自动加上一个'\0'。
字符串的传递可以使用传址的方法,用字符数组的名字或者用指向字符数组的指针变量作为参数。在被调用的函数中可以改变字符串的内容,主函数可以得到改变了的字符串。
一、用字符数组作为参数
接下来看看一个代码:
#include <stdio.h>
void copy_string(char from[],char to[])
{
int i=0;
while(from[i]!='\0'){
(to[i]=from[i]);
i++;
}
to[i]='\0';
return ;
}
int main()
{
char str[]="this is a string!";
printf("%s\n",str);
char dec_str[206];
copy_string(str,dec_str);
printf("%s\n",dec_str);
return 0;
}
运行结果为:
仅仅从代码实现的难易程度来看的话没有什么难点,但是可能有不少人可能会把我们代码中用红色标志的代码部分忘掉,从而出错。
在此也给出我的另外一种实现方式:
#include <stdio.h>
void copy_string(char from[],char to[])
{
int i=0;
while((to[i]=from[i++])!='\0'){
;
}
return ;
}
int main()
{
char str[]="this is a string!";
printf("%s\n",str);
char dec_str[206];
copy_string(str,dec_str);
printf("%s\n",dec_str);
return 0;
}
运行结果为:
值得注意的是这种实现方式的中的巧妙的利用了while语句while((to[i]=from[i++])!='\0')。千万不要写成 while((to[i++]=from[i++])!='\0'),这样的话每次执行判断语句是i++被执行了两次,导致最终的结果出错。
二、用字符指针变量
#include <stdio.h>
void copy_string(char *from,char *to)
{
int i=0;
while(*from!='\0')
*to++=*from++;
*to='\0';
return ;
}
int main()
{
char str[]="this is a string!";
printf("%s\n",str);
char dec_str[206];
copy_string(str,dec_str);
printf("%s\n",dec_str);
return 0;
}
运行结果为:
在此我也给出一种结合逗号表达式和for循环语句来实现的参考代码:
#include <stdio.h>
void copy_string(char *from,char *to)
{
int i=0;
for(;*to=*from,*from!='\0';from++,to++);
return ;
}
int main()
{
char str[]="this is a string!";
printf("%s\n",str);
char dec_str[206];
copy_string(str,dec_str);
printf("%s\n",dec_str);
return 0;
}
运行结果为:
代码的巧妙之处在于结合了逗号表达式和for循环语句来实现,因为逗号表达式的结果为最后一个表达式的结果,所以执行判断语句里边的逗号表达式时其所取的值依然为*from!='\0'。
有兴趣的读者自己可以尝试下其他的实现方法,下面再给出几种很巧妙的实现方法,有兴趣的读者可以自己研究下其实现原理,均为完整代码。
#include <stdio.h>
void copy_string(char *from,char *to)
{
int i=0;
for(;*to++=*from++;);
return ;
}
int main()
{
char str[]="this is a string!";
printf("%s\n",str);
char dec_str[206];
copy_string(str,dec_str);
printf("%s\n",dec_str);
return 0;
}
#include <stdio.h>
void copy_string(char *from,char *to)
{
int i=0;
while(*to++=*from++);
return ;
}
int main()
{
char str[]="this is a string!";
printf("%s\n",str);
char dec_str[206];
copy_string(str,dec_str);
printf("%s\n",dec_str);
return 0;
}
#include <stdio.h>
void copy_string(char *from,char *to)
{
int i=0;
while((*to++=*from++)!='\0');
return ;
}
int main()
{
char str[]="this is a string!";
printf("%s\n",str);
char dec_str[206];
copy_string(str,dec_str);
printf("%s\n",dec_str);
return 0;
}
#include <stdio.h>
void copy_string(char *from,char *to)
{
char*p1,*p2;
p1=from;
p2=to;
while((*p2++=*p1++)!='\0');
return ;
}
int main()
{
char str[]="this is a string!";
printf("%s\n",str);
char dec_str[206];
copy_string(str,dec_str);
printf("%s\n",dec_str);
return 0;
}
#include <stdio.h>
void copy_string(char from[],char to[])
{
char*p1,*p2;
p1=from;
p2=to;
while((*p2++=*p1++)!='\0');
return ;
}
int main()
{
char str[]="this is a string!";
printf("%s\n",str);
char dec_str[206];
copy_string(str,dec_str);
printf("%s\n",dec_str);
return 0;
}
就不在此一一贴出运行结果了,运行结果同上,如果有兴趣可以自己研究下以上代码,同时还有很多的实现方法,希望以上代码能够启发读者的灵感写出更多的实现方法和更好的实现方案来。
《四》
正如前一篇博客所说的,但凡人都是急功近利和有惰性的,都不喜欢花时间去阅读那些我们认为枯燥的文字描述,喜欢直接进入主题。但是有时候恰恰就是因为我们的这种急功近利和惰性,使得我们绕了很大一个弯,到最后还是回到了文字描述上来,所以我觉得适当的文字描述下,让读者对于文章的整体有个大概的认识之后,再去学习能收获更好的效果。我的前两篇关于C指针的博客用的摘要都是copy我第一篇C指针博客的摘要,当然这篇也不例外,还是会引用我第一篇博客的摘要,只是在引用摘要之前我要先交代件事儿,就是关于函数指针和指针函数、以及指针常量和常量指针我就不在此讲解了,有兴趣的朋友可以参考我之前写的两篇博客---C语言的那些小秘密之函数指针和C语言的那些小秘密之const修饰符。(我的博客地址:http://blog.csdn.net/bigloomy/)
懂得C语言的人都知道,C语言之所以强大,以及其*性,绝大部分体现在其灵活的指针运用上。因此,说指针是c语言的灵魂,一点都不为过。所以从我的标题加了个(一)也可以看出指针的重要性,我尽可能的向大家交代清楚我对于指针的理解。所以在讲解的过程中我尽可能的用代码加文字的描述方式,通过代码的分析来加深我们对于指针的理解,我给出的都是完整的代码,所以读者可以在看的过程中直接copy下去即可运行,希望下面的讲解能够对你有所帮助。
先让我们来看看一段非常熟悉的代码:
#include <stdio.h>
void main(int argc,char *argv[])
{
while(argc-->1)
printf("%s\t",*++argv);
}
运行结果如下:
可能还是有人不是很了解main函数里的参数argc和argv是什么意思,在此做一简单的讲解,argc为命令行输入的参数个数,在此argc=3,有三个参数,分别是:fdsa.exe fdsa asdf,argv是一个指针数组,在此相当于char *argv[0]="fdsa.exe"、char *argv[1]="fdsa"、char *argv[0]="asdf"。有了上面的解释相信能够很好的理解main函数的参数了。
现在来分析下上面的代码,程序中有一句argv++;但是我们上面的分析是argv是一个数组名,而数组名是不能进行这样的++运算的,难道出错了嘛?!但是明明运行结果已经就在眼前了,在解释之前我们再来看如下一段代码:
#include <stdio.h>
void main()
{
char *argv[]={"this","is","shuzu"};
*++argv;
}
编译发现出错了。
好了现在我们可以来解释为什么会出现如上两种情况了,有种特殊情况就是数组名作为函数参数,传递的是数组的首地址,系统会把形参当作变量来处理,所以如果我们吧main函数改写为main(int argc,char **argv);就好理解多了。
看了上面的代码,接下来我们看看指针数组和数组指针的区别所在。
一、指针数组指的是一个数组,数组中的每个元素都是指针类型,所有的指针都指向不同的地址,所指的地址的数据也不一定一样,但是所指的数据类型必须一样。
二、数组指针指的是定义的是一个指针,而指针指向的是数组,指针指向数组首单元的地址,对于数组内部元素的属性不了解,仅仅是规定了数组首单元的地址,通过它可以找到整个数组。
接下来看看一段代码:
#include <stdio.h>
void main()
{
int *p=new int [10];
int arr[10];
int (*ptr)[10];
for(int i=0;i<9;i++)
{
arr[i]=i;
printf("a[%d]=%d\t%d\t",i,arr[i],&arr[i]);
}
printf("\n");
int j=0;
ptr=&arr;
printf("\nptr=%d\t&arr=%d\t&arr[0]=%d\n",ptr,&arr,&arr[0]);
printf("\n");
for(;j<9;j++)
{
printf("ptr%d=%d\t%d\t",j,*((*ptr)+j),&(*ptr)[j]);
}
printf("\n");
}
运行结果如下:
在程序中我们打印了数组a中每个数组元素的值和其相应的地址,同时也定义了一个数组指针,int (*ptr)[10]; ,在引用数组指针的过程中必须要注意的是数组指针的维数必须要引用的数组维数相同,否从会出错。我们可以把int (*ptr)[10]; 拆开来看,把指针ptr看成是指向int [10];的类型,把int [10]视为一种新的类型,所以在使用指针ptr的时候类型必须要一致,即必须是一个int [10]这样的数组。
对以上的代码稍加修改:
#include <stdio.h>
void main()
{
int arr[4][4];
int (*ptr)[4];
for(int i=0;i<4;i++)
{
for(int j=0;j<4;j++)
{
arr[i][j]=i*j;
printf("a[%d]=%d\t%d\t",i,arr[i][j],&arr[i][j]);
}
printf("\n");
}
printf("\n");
int j=0;
ptr=arr;
printf("\nptr=%d\t&arr=%d\t&arr[0]=%d\t&arr[0][0]=%d\n",ptr,&arr,&arr[0],&arr[0][0]);
printf("\n");
for(;j<16;j++)
{
if(j%4==0&&j!=0)
printf("\n");
printf("ptr%d=%d\t%d\t",j,*((*ptr)+j),&(*ptr)[j]);
}
printf("\n");
}
运行结果如下:
可能有的读者看了int arr[4][4];int (*ptr)[4]; 这两句代码之后认为接下来的代码ptr=arr;有错,因为arr是一个二位数组,而ptr只是一个指向int [4]类型的指针。但是要注意了,从运行结果我们也可以肯定的是上面的代码是正确的,但是为什么是正确的呢?!首先我们把arr分为两部分来看,第一部分为int [4],第二部分为arr[4],这样就一目了然了,我们可以认为我们定义了一个一维数组arr[4],数组有四个元素,每个元素的类型为int [4],因此和一般的数组一样,我们可以将该数组名赋给指针,其实也就是第一个元素的地址付给指针。即: ptr=arr;或者ptr=&arr[0]。其余情况以此类推。
下面再来看看一段代码:
#include <stdio.h>
void sum(int s[])
{
int i;
printf("%d\n",s);
printf("%d\n",*(s+3));
printf("%d\n",&s);
printf("%d\n",*s++);
printf("%d\n",*s++);
printf("%d\n",*s++);
printf("%d\n",*s);
printf("\n");
}
void main(int argc,char *argv[])
{
int ss[4];
for(int i=0;i<4;i++)
ss[i]=i;
sum(ss);
printf("%d\n",ss);
printf("%d\n",&ss);
}
运行结果如下:
在main函数中我们使用了两句 printf("%d\n",ss);和 printf("%d\n",&ss);来打印数组ss的地址,值得注意的就是数组的首地址的几种表示方法,还可以是&ss[0];在调用ss函数的过程中,我们使用的是数组作为参数,同时为了加深读者对于数组作为参数的时候可以使用++这样的操作符印象,我在此使用s++的方法打印了整个数组。细心的读者会发现我使用的 printf("%d\n",s);和 printf("%d\n",&s);在此打印的结果不再一样了,跟在main函数中用这样的语句打印数组首地址的结果一致不符合,这也从侧面说明了当数组名作为参数的时候,系统是将它作为变量处理的,所以打印的结果不一致。
指针的讲解到此就告一段落了,由于本人水平有限,博客中的不妥或错误之处在所难免,殷切希望读者批评指正。同时也欢迎读者共同探讨相关的内容,如果乐意交流的话请留下你宝贵的意见。