您的位置:首页 > 编程语言 > C语言/C++

浅谈C语言中数组和指针的互操作

2005-08-22 19:32 295 查看
浅谈C语言中数组和指针的互操作作者: 张桂权 曾听好多朋友说,C是一种怀旧的语言,因为它的历史很久远,然而自从各种面向对象的编程语言的相续出现让它的影响力日减。当然了,这是无可非议的,但是C的高效性其他语言无妨比拟的,所以我们有必要把握其中的精华与奥妙,也就有必要知道其中的基本的数据结构的比如数组,稍微有点深度的堆栈、列表、结构体等的操作和实现。指针也是C语言中的一个很优秀灵活的结构,对它的了解也是必不可少的。
我们一般都认为数组是一维的数据存储结构,因为二位数组或者说矩阵都可以看作是多个一维数组的组合结构,定义在其上的数据存储访问方式是一样的。所以一维数组是其中最基础的最重要的部分,只有理解了此类数据结构的本质才能触类旁通了。
数组(array)是若干同类变量的聚合,允许通过统一的名字饮用其中的变量。所以数组也就是一个同一类型的数据的有限集合。可以通过下表来访问数组中的某一/些数组元素。在C语言中数组都由连续的内存区域构成(有时候,不一定是这样),最低地址对应首元素,数组的下标是从0开始的,所以首元素也就是数组下标为0的元素,最高的地址对应最末的元素,即第N-1个元素(如果我们定义的数组为N元)。
数组的定义方式:
在C语言中允许在声明数组的时候同时对其进行初始化,也可以把声明和定义放在不同的位置,初始化的一般的类似于如下的表达式:
type_specifier array_name[size1]...[sizeN] = {value_list};
其中vlaue_list是由逗号(,)分隔的常量表,常量表必须和type_specifier兼容。最后由分号与下一个语句分隔。由此可见一维数组的定义方式为:
type_specifier array_name[size] = {value_list};
如下:char hello[12] = {'H','e','l','l','o',',',' ','w','o','r','l','d','/0'};
注意:字符数组是一"'/0'"收尾的,这是C标准的一部分。因为在操作字符数组的时候是以'/0'作为结束判断的标志。当然了,如果你定义的是一个字符串那就不用加这个'/0'了。因为有机制帮助你自动添加。上面的例子的串的生命方式为:string hello = "Hello, world";(当然了,具体的实现中你必须把"string.h"头文件加入到你的文件中),或者你也可以这样来声明:
char *hello = "Hello, world";或者char hello[] = "Hello, world"; 切换为字符指针数组,其效果是一样的);数组初始化的时候还可以不标明最大小,即
char hello[] = {'H','e','l','l','o',',',' ','w','o','r','l','d','/0'};,这时候编译器会根据后边的赋值情况为数组分配合适的内存空间,这个你不用担忧,除非机器正处于内存缺状态。
数组元素的访问:
可以利用循环结构来挨个访问数组的元素,比如:
[...]
int i;
char hello[12] = {'H','e','l','l','o',',',' ','w','o','r','l','d','/0'};
[...]
for(i = 0; i < 12; i++){
printf("%c",hello[i]);
}
printf("/n");
[...]
其中有一点必须注意了,那就是i的值不能取到12,因为我们的下标识从0开始的,即hello[0]是第一个元素,数组的下界,而hello[12]是第一个空元素,数组的上界。其实,数组元素的个数等于定义时的下标,也等于数组的上界(12)减去下界(0)得到的数值,还等于上界地址减去下界地址模sizeof(tyep_specifier)的值(假设数组空间是连续分布的,如果不是这样那么这种方法也就不成立了)。
在数组初始化的时候还允许在数组的最后加一个逗号(,),比如:
int month_lengths[] = {'31','29','31','30','31','30','31','31','30','31','30','31',};
因为在分行的情况下,编译程序是通过逗号作为标志的,所以这是合法的。
以上结构可以写为如下的形式:
int month_lengths[] = {'31','29','31','30','31','30',
'31','31','30','31','30','31',
};
还有一点必须注意了,字符和字符串的表达式不一样的。单引号用于字符表达式,而双引号则用于字符串表达式的。这是好多人,包括C的老手,最爱犯的错误。写一个换行语句是会
写成 :
printf('/n'); ,而在字符判断操作的时候却写成了:
int c;
[...]
if((c = getchar()) != "/n"){

do something here
}
[...]
这种低级的错误是很容易避免的,但是一不小心就出错了,而且还不少呢,在教科书、参考书、以及一些文献以及网络文字里经常出现,只要你细心一点就可以发现。这看似问题不大,但是
如果在嵌入式系统,尤其是实时系统有可能造成系统崩溃的,所以写程序时不要报以任何的侥幸心理,必须审慎每一个细节。程序设计不是一门技术,而是一门艺术

下面看看指针吧。
指针是(pointer)是存放内存地址的变量。 从这个定义中我们可以实力一个这样的概念: 与指针操作相关的最直接的要素是地址,内存地址。这里的地址指内存中另一个变量的位置。
如果一个变量含有一个变量的地址,则称一个变量指向第二个变量。
准备存放指针的变量必须在使用前声明好。 指针的声明由一个基类(base type)、一个星号(*)和变量名组成。 一般的形式为:
type * name;
其中,type是指针的基类类型,可以是任何有效的数据类型,name是指针变量的名称。当然了 type *name 和 type * name是等效的,一般的编译器都会在编译程序的时候忽略中间的空格。

指针的基类定义了指针可以指向的变量的类型。 技术上,任何指针类型都可以指向内存的任何位置。然而,指针的操作是基于指针的类型的,也就是说指针变量和其指向的地址的变量的类型必须兼容。即如下的操作时错误的:
int *p;
char ch = 'A';
p = &ch;
因为类型不匹配,所以这是错误的。其实,这句话不完全对。这只是一个标准罢了,具体还需要看你的编译器的实现了。上面这种方式的操作在好多编译器上是可以通过的,比如,Win-TC、LCC等(在Win-TC下直接通过,而在LCC下也只有一个警告罢了)。 不过采用这种方式设计的程序的可移植性一定是很糟糕的。还有如果在操作中如果你想把指针地址存给一个数组元素时,如果类型不匹配,那么你只有强制转换指针地址的数据类型了,直接赋值是不可能的。所以我们提倡还是采用标准的规定来进行操作。还有下面这一种操作也是错误的,虽然类型是匹配的:
int *p;
int i;
p = &i;
因为i仅被用户声明了,但是没有分配内存,所以这是错误的。一个变量只有在被初始化的时候,才有可能分配到内存的(大多数情况是初始化之后,就分配到内存,但是如果在内存不足的情况就不可能满足这个要求了)。
在指针声明中还存在一个误区,那就是错误的认为在如是的表达式 int *p,m,n; 中声明了3个指针变量,其实只有第一个(p)是指针变量,而其他两个(m,n)只是int型变量罢了。只有这样才能同时声明几个指针变量的: int *p, *m, *n, ...;

指针也可以像一般变量一样进行初始化的,但是你不能给一个一个指针直接的赋值哦。 比如
int *p;
p = 10; 只是错误的。不过你可以把指针赋值为空,即

int *p = 0; 或者
int *p;
p = NULL; 因为在许多的C语言的头文件,如<stdio.h>,定义了宏NULL,它是一个空指针常量,所以我们的表达式是合法的,效果和上面的一个相同。

我们可以把一个数组变量赋予一个指针,因为它们都是一种地址的映射罢了。这才是我们的主题。比如,
char str[100], *pointer;
pointer = str;
这里,pointer指向数组str的首元素,与str[0]或者访问变量str的实质是一样的,操作的都是数组的第1个元素,0位置上的元素。如果要访问str数组的第10个元素,那么操作如下:
str[9]

*(pointer + 9) 这是等效的。在指针中也有++,--这样的操作,尤其在指针和数组互操作时,用到的机率最大,因为操作很灵活。
比如:
#include <stdio.h>
int main(){
char hello[] = "Hello, world !";
char *p;
p = hello;
do{
printf("%c", *p);
p++;
} while(*p);
printf("/n");
return ;
}
这是一个典型的例子,即用了指针的“自增”操作,还是数组合指针互操作的好例子。你的操作还可以是:
p = &hello[3];
*p = hello[3];
*&hello[3] = *p;
printf("%d/n",*&hello[3]);
hello[3] = *p;
printf("%d/n",&hello[3]);
hello[3] = *p;
printf("%d/n",hello[3]);
等等,其中 *&hello[3] 和 &hello[3]的值是一样的,都是读取hello数组中3号元素(第四个元素)的地址的。而语句 p = &hello[3];是把3号元素的地址赋给指针变量,而hello[3] = *p;
是把指针p的值赋给hello[3]的。这也是互操作的一种方式。

注意了: *++P , ++*p 和 *p++ 以及 *--p , --*p 和 *p-- 表示的结果是不同的。因为++、--的优先级高于*的优先级,所以前边的表达式相当于 *(++p) , *(++p) 和 *(p++) 以及
*(--p) , *(--p) 和 *(p--)。建议牢牢的记住操作符的优先级,因为在这些细节上稍不注意就会产生问题,我们意想不到的。

数组和指针都可以当作参数来处理,但是才用指针的概率要高一些,因为指针较数组更加灵活。例如,你可以如下传递一个数组或指针变量:
#include <stdio.h>
int main(){
char hello[] = "Hello, world !/n";
char *p;
p = hello;
printf(hello);
printf(p);
printf("%s", p);
printf("%s", hello);
do{
printf("%c", *p);
p++;
} while(*p);
printf("/n");
return ;
}
在LCC中是完全通过的,其输出结果是:
Hello, world !
Hello, world !
Hello, world !
Hello, world !
Hello, world !
但是在有些编译器上,语句
printf(hello);
printf(p);
可能只会输出字符串的第1个元素,因为有些编译器采取的是对字符串进行“截取”的方式来处理,所以其结果有可能是:
HHHello, world !
Hello, world !
Hello, world !

可能这些东西并不一定有用,但是知道这些知识还是有必要的。如果我的辛劳能对你的学习或者开发有点帮助,那我也就满足了。(如果其中有不足的地方,还希望大家指明)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: