首页 > 代码库 > 数组与指针——参数

数组与指针——参数

引入:

#include <stdio.h>

#include <stdlib.h>

void fun(char **q)

{

exit;

}

void main()

{

    char **p,a[6][8];

    p = a;

           fun(a);

}

编译不能通过!

ERROR不能将”char (*)[8]”类型的值分配到”char **”类型的实体。

ERROR”char (*)[8]”类型和”char **”类型不兼容

 

一、指针:——参见与《C专家编程》第十章:再论指针

C语言中的指针是指专门用来存放内存地址的变量,每个指针都有一个与之关联的数据类型。

注:避免使用未初始化的指针,在定义指针时最好将它初始化为NULL,即明确指示当前该指针不指向任何变量。

与指针相关的两个运算符:

&:取地址运算符

*:取指针所指向的内存单元的值

指针与数组密切相关,数组名实际上就是指向数组第一个元素的指针。

在指向数组某个元素的指针上加上或减去一个整型数值,就可以指向同一数组中另外一个数组元素,前提是没有超出数组范围。简单点说,如果指针变量p指向数组中的一个元素,则p+1指向同一数组中的下一个元素。

注:在指针上进行加减运算后所得到的指针,必须指向同一数组或指向数组存储空间的下一个单元(但不能执行*p的运算)

区别几种运算:

*p++:先取得当前p所指向的变量值,再使p指向后一个变量,相当于a[i++].

*p--:先取得当前p所指向的变量值,再使p指向前一个变量,相当于a[i--].

*++p:先使p指向后一个变量,再取得p所指向的变量的值,相当于a[++i].

*--p:先使p指向前一个变量,再取得p所指向的变量的值,相当于a[--i].

注:不能对数组名执行++--操作,即a++是不合法的。因为a是数组名,它是数组的首地址,它的值在程序的运算过程中是固定不变的,是常量。

 

二、数组与指针参数是如何被编译器修改的?

注:“数组名被改写成一个指针参数”规则并不是递归定义的,所以数组的数组会被修改为“数组的指针”,而不是“指针的指针”。

实参                                所匹配的形参

数组的数组     char c[8][10];       char (*)[10];          数组指针

指针数组       char *c[10];         char **c;              指针的指针

数组指针(行指针)  char (*c)[10];     char (*c)[10];         不改变

指针的指针        char **c;          char **c;              不改变

 

三、指针数组与数组指针

1、数组指针——行指针

数组指针是数组名的指针,即指向数组首元素的地址的指针,其本质是指针(这个指针存放的是数组首地址的地址,相当于二级指针,这个指针不可动)。

int (*p)[n]——p即指向数组的指针

解释:()优先级高,首先说明p是一个指针,指向一个整型的一维 数组,且这个一维数组的长度是n,即p的步长;也可以说执行p+1 时,p要跨过n个整型数据的长度。

举例说明:将二维数组赋给指针如下

int a[3][4];

int (*p)[4];//定义一个数组指针,指向一个含有4个元素的一维数组

p=a;        //将二维数组的首地址赋给p,也就是a[0]&a[0][0]     

p++;        //p=p+1,p跨过行a[0][]指向行a[1][]

2、指针数组

顾名思义,指针数组就是数组的每个元素全为指针变量的数组,其本质是数组。

int *p[n];

解释:[]优先级高,[]先与p结合成一个数组,再由int*说明这是 一个整型指针数组,它有n个指针类型的数组元素。

不能执行p++p=a等运算,因为p是个不可知的表示,只存在p[0]p[1]p[2]...p[n-1],而且它们分别是指针变量可以用来存放变量的地址。

举例说明:将二维数组赋给指针数组如下

int *p[3];

int a[3][4];

for(i=0;i<3;i++)

p[i]=a[i];

int *p[3]表示一个一维数组内存放着三个指针变量,分别是p[1]p[2]p[3],必须分别赋值。

注:运算符*的优先级低于运算符[]

区别int (*p)[5]int *p[5]

前者是一个指针,它指向一个含有5个元素的数组;后者是一个数组,它的长度为5,数组中每一个元素指向一个整型变量。

数组指针只是一个指针变量,占用内存中一个指针的存储空间;指针数组是多个指针变量,以数组形式存在于内存中,占用多个指针的存储空间。

3、二维数组

二维数组仅仅是定义了一个二维数组,无论赋值与否,系统都会给它分配相应的空间,而且该空间一定是连续的。其每个元素表示一个字符,我们可以通过下标对其元素进行修改。

指针数组与二维数组的区别如图示:

    

分析:系统给数组a分配的3*8的空间,而且该空间是连续的;然而给n分配的空间则取决于具体字符串的长度,该空间不一定连续。

 

四、指针——函数参数      ——参加《C专家编程》

1、向函数传递一个一维数组

在C语言中,任何一个一维数组均可以作为函数的实参,形参被改写成指向数组第一个元素的指针,所以需要一个约定来提示数组的长度。一般有两个基本方法:(1)增加一个额外的参数,表示元素的数目(argc就是起这个作用),(2)赋予数组最后一个元素一个特殊的值,提示它是数组的尾部(字符串结尾的’\0’字符就是起这个作用的),这个特殊值不会作为正常的元素在数组中出现。

2、使用指针向函数传递一个多维数组

方法一:

my_function(int my_array[10][20])

只能处理固定数据类型、固定行列的数组。此时多维数组的一维的长度不必显式写明,函数必须知道数组其它维的确定长度和数组的基地址

方法二:

省略第一维的长度,声明多维数组如下:

my_function(int my_array[][20])

但是这样仍对每一行长度有明确的限制(每行必须正好是20个整数的长度),函数也可以类似的声明为:

my_function(int(*my_array)[20])

注:(*my_array)括号是必需的,以便确保其被翻译成一个指向20个元素的int数组的指针,而不是一个20个int指针元素的数组。

方法三:

补充:Iliffe向量,创建一个一维数组,数组中的元素是指向其它东西的指针。例如main(int argc, char *argv[]),其中第二个参数会被改写成char **。Iliffe向量这种数据结构的美感在于:它允许任意的字符串指针数组传递给函数,但必须是指针数组,而且必须是指向字符串的指针数组。这是因为字符串和指针都有一个显式的越界值(分别是NUL和NULL),可以作为结束标志。至于其它类型,并没有一种类似的通用且可靠的值,所以并没有一种内置的方法知道何时到达数组某一维的结束位置。及时是指向字符串的指针数组,通常也需要一个计数参数argc,记录字符串的数量。

my_function(char **my_array)

注:只有把二维数组改为一个指向向量的指针数组的前提下才可以这样做。

方法四:

放弃多维数组的形式,提供自己的下标方式,表示如下:

char_array[row_size * i + j] = ....

 

总结:当作为函数的参数的数组的长度是任意的时

一维数组——没有问题,但需要一个计数值或者一个能够标识越界位置的结束符。

二维数组——不能直接传递给函数,但可以把矩阵改写成一个一维的Iliffe向量,并使用相同的下标表示法。对于字符串来说,这样做是可以的,对于其他类型,需要增加一个计数值或者能够标识越界位置的结束符。同样,它依赖于调用函数和被调用函数之前的约定。

三维数组——都无法使用,必须把它分解为几个维数更少的数组。