C语言核心之数组和指针详解二

来源:互联网 发布:递归算法自然数排列 编辑:程序博客网 时间:2024/06/07 08:41

 数组:

数组大家应该都很熟悉了,用途非常广泛。

int a[4]={2,4,5,9};

此语句定义一个4个空间大小的整型数组a并为它进行了初始化。

数组的基础知识可以参考其他相应的教材,我们在这主要讨论指针和数组的结合应用。

我们再来看个完整的例子:

#include<stdio.h>

void main()

{

       inta[4]={2,4,5,9};

       int *p;

       p=a;

       *p=*p++;

       printf("%d%d %d/n",*p,*p+6,*(p+1));

}

运行结果:4 10 5

分析:语句p=a;表示把数组a的第0个元素的地址赋给指针p,数组名a代表的是数组a的第0个元素的地址。

a[i]表示数组a的第i个元素,如果定义一个指针p,那么语句p=&a[0];表示可以将指针p指向数组a的第0个元素,也就是说p的值为数组元素a[0]的地址。那么*p+1)引用的是数组元素a[1]的内容,p+i是数组元素a[i]的地址,*p+i)引用的是数组元素a[i]的内容。对数组元素a[i]的引用也可以写成*(a+i)。可以得出结论:&a[i]a+i的含义相同,p[i]*p+i)也是等价的。

虽然数组和指针有这么多通用的地方,但我们必须记住,数组名和指针之间有一个不同之处。指针是一个变量,因此语句p=ap++都是合法的。但数组名不是变量,因此,类似于a=pa++形式的语句是非法的。

       下面来看一个我们常用的函数strlen(char *s)

int strlen(char *s)

{

       int n;

       for(n=0;*s!='/0';s++)

              n++;

       return n;

}

因为s是一个指针,所以对其执行自增运算是合法的。执行s++运算不会影响到strlen函数的调用者中的字符串,它仅对该指针在strlen函数中的私有副本进行自增运算。在函数定义中,形式参数char s[]char *s是等价的。

       我们再来看一下地址算术运算:如果p是一个指向数组中某个元素的指针,那么p++将对p进行自增运算并指向下一个元素,而p+=i将对p进行加i的增量运算,使其指向指针p当前所指向元素之后的第i个元素。同其他类型的变量一样,指针也可以进行初始化。通常,对指针有意义的初始化值只能是0或者是表示地址的表达式,对后者来说,表达式所表达的地址必须是在此之前已定义的具有适当类型的数据的地址。任何指针与0进行相等或者不相等的比较运算都有意义。但是指向不同数组的元素的指针之间的算术或比较运算没有意义。指针还可以和整数进行相加或相减运算。如p+n表示指针p当前指向的对象之后第n个对象的地址。无论指针p指向的对象是何种类型,上述结论都成立。在计算p+n时,n将根据p指向的对象的长度按比例缩放,而p指向的对象的长度则取决于p的声明。例如,如果int类型占4个字节的存储空间,那么在int类型的计算中对应的n将按4的倍数来计算。

       指针的减法运算也是有意义的,如果pq指向相同数组中的元素,且p<q,那么q-p+1就是位于pq指向的元素之间的元素的数目。我们来看一下strlen(char *s)的另一个版本:

int strlen(char *s)

{

       char*p=s;

       while(*p!='/0')

              p++;

       returnp-s;

}

程序中,p被初始化为指向s,即指向该字符串的第一个字符,while循环语句将依次检查字符串中的每个字符,直到遇到标识字符数组结尾的字符’/0’为止。由于p是指向字符的指针,所以每执行以此p++p就将指向下一个字符的地址,p-s则表示已经检查过的字符数,即字符串长度。

总结:有效的指针运算包括相同类型指针之间的赋值运算;指针和整数之间的加减运算;指向相同数组中元素的两个指针间的减法或比较运算;将指针赋值为0或指针与0之间的比较运算。其他所有形式的指针运算都是非法的。

         再来看两条语句:char  a[]=”I am  a  boy”; char *p=”I  am  a  boy”;

a是一个仅仅足以存放初始化字符串以及空字符’/0’的一维数组。数组中的单个字符可以进行修改,但a始终指向同一个存储位置。而p是一个指针,其初值指向一个字符串常量,之后它可以被修改以指向其他地址,但如果试图修改字符串的内容,结果是没有定义的。

         为了更容易理解数组和指针的关系,我们再来看一个函数:

void strcpy(char *s,char *t)

{

       int i;

       i=0;

       while((s[i]=t[i])!='/0')

              i++;

}

因为参数是通过值传递的,所以在strcpy函数中可以以任何方式使用参数st

下面是指针实现的几个版本:

void strcpy(char *s,char *t)

{

       while((*s=*t)!='/0'){

              s++;

              t++;

       }

}

 

最简版本:

void strcpy(char *s,char *t)

{

       while(*s++=*t++)

              ;

}

这里,st的自增运算放到了循环的测试部分中。表达式*t++的值是执行自增运算之前t所指向的字符。后缀运算符++表示在读取该字符之后才改变t的值。同样,在s执行自增运算之前,字符就被存储到了指针s指向的旧位置。上面的版本中表达式同’/0’的比较是多余的,因为只需要判断表达式的值是否为0即可。

接着看一个难点:指针数组和指向指针的指针

       这两个词次听起来挺新颖的,到底是什么意思呢?

       由于指针本身也是变量,所以它们也可以像其他变量一样存储在数组中。这一点很容易理解。

看例子:

#include<stdio.h>

#include<string.h>

void main()

{

       int i;

       charb[]={"wustrive_2008"};

       char*a[1];

       *a=b;

       for(i=0;i<strlen(b);i++)

              printf("%c",*(a[0]+i));

       printf("/n");

}

 

运行结果:wustrive_2008

这里库函数strlenstrlenstring类的标准库函数,所以要包含#include<string.h>

下面我们来自己写一个strlen函数,我们把上面的例子该成这样:

#include<stdio.h>

int strlen(char *s)

{

       char*p=s;

       while(*p!='/0')

              p++;

       returnp-s;

}

void main()

{

       int i;

       charb[]={"wustrive_2008"};

       char*a[1];

       *a=b;

       for(i=0;i<strlen(b);i++)

              printf("%c",*(a[0]+i));

       printf("/n");

}

这个运行结果和上个例子一样,不一样的只是我们自己实现了strlen函数,我们再编程时使用的库函数,都是语言的开发者或者系统为我们写好了的函数,其实我们也可以自己写。

这个例子很好的演示了指针数组的用法,指针数组a的值a[1]是一个指针,指向字符数组第一个字符。

指针的指针也很好理解,就是一个指针里放的是另一个指针的地址,而另一个指针可能指向一个变量的地址,还可能指向另一个指针。

指针和多维数组:

       看两个定义语句:int  a[5][10];   int  *b[5];

从语法角度讲,a[3][4]b[3][4]都是对一个int对象的合法引用。但a是一个真正的二维数组,它分配了50int类型长度的存储空间。但b定义仅仅分配了5个指针,并且没有初始化,它们必须进行显示的初始化,假设b的每个元素都指向一个有10个元素的数组,那么编译器就要为它分配50int类型长度的存储空间以及5个指针存储空间。指针数组的一个重要优点在于,数组的每一行长度可以不同,也就是说,b的每个元素不必都指向一个有10个元素的向量。

指向函数的指针:

       C语言中,函数虽然不是变量,但可以定义指向函数的指针。这种类型的指针可以被赋值,存放在数组中,传递给函数以及作为函数的返回值等。

如果下面的语句为一个函数的参数,表示什么意思:

int  (*p)(void *,void  *)

它表明p是一个指向函数的指针,该函数具有两个void*类型的参数,其返回值类型为int。语句if((*p)(v[i],v[left])<0)中,p的使用和其声明是一致的,p是一个指向函数的指针,*p代表一个函数。如果写成这样:int*p(void *,void *)则表明p是一个函数,该函数返回一个int类型的指针。

下面来看两个声明:

int  *f();      //f是一个函数,它返回一个指向int类型的指针

int   (*pf)();    //pf是一个指向函数的指针,该函数返回一个int类型的对象。

 

以上为指针和数组的几个难点知识,想要熟练掌握,需要多上机调试,多思考。