- 论坛徽章:
- 0
|
C语言指针和数组宝典(2)第二章: 指针类型和数组
作者BLOG:chinacpp.tianya.cn
->==================================================================
第二章: 指针类型和数组
好的,让我们继续前进。我们为什么要定义一个指针所指向变量的类型呢?例如在: int *ptr;
定义变量类型是在后来的语句:*ptr = 2;时,编译器知道往指针所指向的位置拷贝多少个字节。如果ptr被声明成指向一个整型变量,
将被拷贝2个字节,如果是一个LONG型变量,将被拷贝4个字节。同样,当定义的是FLOAT,DOUBLE型时,将有合适数目的字节被拷贝。
同样,也有一些其它的方法来定义和使用指针变量以使编译器可以解释这些写法的代码。例如:假设有一块内存,连续存放了10个整型,
即有20个字节被留出以存放10个整型变量。
现在,让我们将我们的指针变量ptr指向这块内存中的第一个整型变量的地址上。进一步,我们假设这个整型变量被存放在内存地址
为100(十进制)的地址单元上。当我们写下面的语句时将表示什么含义:
ptr + 1;
因为编译器知道这是一个指针(即它的值是一个地址),并且该指针指向的是一个整型变量(它当前的地址-100就是这个整型变量
所存放的地址)。ptr上加2(整型在内存中存放需要两个字节)就表示指针指向了下一个整型变量,内存位置变为了102。同样,当ptr
被声明成LONG型指针(即指向LONG型变量地址的指针),ptr上要加4(LONG型在内存中存放需要4个字节)才表示指针指向了下一个LONG
型变量。这同样使用与其它数据类型的指针,包括DOUBLE,FLOAT和用户自定义的结构类型变量。这里的'+'并不是我们一般意义上的加法。
在C语言中,它有个专门的术语:指针算术。在后面的课程中我们还会提到。
同样的,++ptr和ptr++都等同于ptr + 1,增加一个指针使用一元操作符'++',无论是前缀还是后缀,在内存中地址将增加该地址单元
所存放的变量类型的长度(字节数),比如:整型是2个字节,LONG型是4个字节等。
因此,一块存放了10个连续整型的内存单元被定义时,既是定义了一个整型数组,这样就在数组和指针之间建立了一种有趣的关系。
假设如下:
int my_array[] = {1,23,17,4,-5,100};
在此我们定义了一个有6个元素的数组,我们使用该数组的下标来引用数组中的每个元素。也就是用my_array[0]..my_array[5]来表示
。但是我们也可以通过指针来引用数组中的每个元素。语句如下:
int *ptr;
ptr = &my_array[0]; /*将我们的指针指向数组的第一个整型元素上*/
我们可以用数组符号或者通过对指针解引用来打印出我们的数组。
下面的代码就说明了这个:
------------------------------------------------------
#include <stdio.h>
int my_array[] = {1,23,17,4,-5,100};
int *ptr;
int main(void)
{
int i;
ptr = &my_array[0]; /* point our pointer to the first
element of the array */
printf("\n\n");
for(i = 0; i < 6; i++)
{
printf("my_array[%d] = %d ",i,my_array); /*<-- A */
printf("ptr + %d = %d\n",i, *(ptr + i)); /*<-- B */
}
return 0;
}
----------------------------------------------------
编译并运行上面的程序代码,注意第A行和第B行,在两种情况下程序打印出了相同的结果。
也观察我们是如何对指针进行解引用操作的,我们首先将ptr加i,然后对得到的新指针进行解引用。将第B行修改成下面的语句
printf("ptr + %d = %d\n",i, *ptr++);
重新运行该程序,然后再改变为:
printf("ptr + %d = %d\n",i, *(++ptr));并重新编译运行。每次尝试预言输出结果并与真正的输出结果进行比较。
在C语言中,我们即可以使用ptr = &my_array[0];也可以使用 ptr = my_array; 这两个语句是等同的。这也使很多文章中说:
一个数组名就是一个指针。这种说法是欠妥的,正确的说法应该是:数组名是该数组中第一个元素的地址。不能把数组和指针混为一谈。
例如,我可以写:ptr = my_array; 但是不能写:my_array = ptr;
原因是ptr 是一个变量,而my_array是一个常量。
即是说:一旦 my_array[]被声明后, 数组my_array的第一个元素在内存中的地址位置将不能被改变。
一个对象(_object_)是一块被命名的存储区。
一个左值(_lvalue_)是引用一个对象(_object_)的表达式。
这就引出了一个有趣的问题,因为my_array是一个被命名的存储区,那为什么my_array在上面的赋值语句中不能成为左值呢?
要解决这个问题,有人就把my_array看作为一个"不能被修改的左值"。
改变上面的程序段:
ptr = &my_array[0]; to ptr = my_array; 然后再次运行它并去验证是否结果相同。
现在让我们去研究一下"ptr"和"my_array"在上面的使用中有什么不同?
有些程序员将一个数组名看作为一个常量(_constant_)指针。我们如何理解这个呢?在此,要理解术语"constant",让我们回头去
看看当时对术语"variable"的定义吧。当我们声明一个变量时,编译器将在内存中留出一块存储区,以便去存放适当类型的变量值。
一个变量的名字可以按照两种方式被编译器解释。一,当这个变量名被用在赋值操作符(=)的左边时,编译器就解释为:将赋值操作符
(=)右边的值拷贝到左边变量名所代表的内存位置。二,当这个变量名被用在赋值操作符(=)的右边时,编译器就将该变量名解释为
存放那个变量的值的内存地址值。
例如:int i, k;
i = 2;
在此,i 是一个变量,占去了计算机数据段的一段内存空间。2 是一个常量,它不是被存在计算机的数据段,而是直接被存放在
计算机的程序段。既是说,当我们写语句:k = i; 时,就是告诉编译器在运行时去创建机器代码,此机器代码将去寻找内存地址&i,
并将该地址值赋值给k;而代码:i = 2; 则表示编译器只是简单地放2到代码段,而不会引用数据段。即:k和i是个对象,但2并不是
一个对象。
同样,在上面,因为'my_array'是一个常量,编译器知道该数组被存储的位置,即知道array[0]的地址。
ptr = my_array;
在上面这个代码中,我们简单的用一个地址作为一个常量,并没有引用到数据段。
好,有很多内容需要消化吸收,我并不期望一个初学者在第一次读到这里时都能理解所有的内容,希望你在以后的学习和实践中能
回来重读第二章的内容。现在,让我们继续学习下面的内容。 |
|