深入理解C语言指针的奥秘

深入理解C语言指针的奥秘,第1张

深入理解C语言指针的奥秘,第2张

指针是一个特殊的变量,存储在其中的值被解释为内存中的地址。要理解一个指针,需要理解四个方面:指针的类型,指针所指向的类型,指针的值或者指针所指向的内存区域,指针本身所占用的内存区域。我们单独来解释一下。
先声明几个指针例如:
例1:
(1)int * ptr;
(2)char * ptr;
(3)int * * ptr;
(4)int(* ptr)再看另一个例子:;
(5)int *(* ptr)示例4:;
如果后面的例子你看不懂,可以参考我前段时间贴的文章。
指针的类型
从语法的角度来说,你只需要在指针声明语句中去掉指针名,剩下的就是这个指针的类型了。这就是指针本身的类型。我们来看看例1中每个指针的类型:
(1)int * ptr;//指针的类型是int *
(2)char * ptr;//指针的类型是char *
(3)int * * ptr;//指针的类型是int * *
(4)int(* ptr)[3];//指针的类型是int(*)[3]
(5)int *(* ptr)[4];//指针的类型是int*(*)[4]
怎么样?找出指针的类型简单吗?
指针所指向的类型
当访问指针所指向的内存区域时,指针所指向的类型决定了编译器将该内存区域的内容视为什么。
从语法上来说,你只需要去掉指针声明语句中的指针名称和名称左边的指针声明*,剩下的就是指针所指向的类型了。例如:
(1)int * ptr;//指针指向的类型是int
(2)char * ptr;//指针指向的类型是char
(3)int * * ptr;//指针指向的类型是int *
(4)int(* ptr)[3];//指针指向的类型是int()[3]
(5)int *(* ptr)[4];//指针所指向的类型是int*()[4]
在指针的算术运算中,指针所指向的类型起着重要的作用。
指针的类型(即指针本身的类型)和指针所指向的类型是两个概念。随着你对C越来越熟悉,你会发现,把混有指针的“类型”这个概念分成“指针的类型”和“指针所指向的类型”两个概念,是掌握指针的关键点之一。我看了很多书,发现了一些写的很差的书,于是混淆了指针这两个概念,于是书越看越糊涂。
指针的值,或者指针指向的内存区域或地址
指针的值是指针本身存储的值,编译器会把它当作地址,而不是一般的值。在32位程序中,所有类型指针的值都是32位整数,因为32位程序中的内存地址都是32位长的。指针所指向的内存区域是从指针的值所代表的内存地址开始,长度为si zeof(指针所指向的类型)的内存区域。以后我们说一个指针的值是XX,就相当于说这个指针指向一个以XX为首的内存区域;当我们说一个指针指向一个内存区域,就相当于说指针的值是内存区域的第一个地址。
指针指向的内存区域和指针指向的类型是两个完全不同的概念。在例1中,指针指向的类型已经存在,但是因为指针还没有初始化,所以它指向的内存区域不存在,或者说没有意义。
以后每次遇到指针都要问:这个指针是什么类型的?指针的类型是什么?指针指向哪里?
指针本身占用的内存区域
指针本身占用多少内存?你只需要用函数sizeof(指针的类型)来度量就可以了。在32位平台上,指针本身占用4个字节的长度。
指针本身占用内存的概念在判断指针表达式是否为左值时非常有用。
指针的算术运算
指针可以加减一个整数。指针的这种运算的意义和通常的数值加减运算的意义是不一样的。比如:
例二:
1、chara[20];
2、int * ptr = a;
...
...
3、ptr++;
在上面的例子中,指针ptr的类型是int*,它所指向的类型是int,它被初始化为指向整型变量a,在接下来的第三句中,指针ptr被加上1。编译器是这样处理的:它把sizeof(int)加到指针ptr的值上。在32位程序中,它加上4。因为地址是以字节为单位的,所以ptr指向的地址从原变量A的地址向高地址方向增加了4个字节。
由于char类型的长度是一个字节,原来ptr是指向数组A的单元0开头的四个字节,此时指向的是从数组A的单元4开始的四个字节,
我们可以用指针和循环来遍历一个数组。看例子:
例3:
int array[20];
int * ptr = array;
...
/这里省略了为整数数组赋值的代码。
...
for(I = 0;I {
(* ptr)++;
ptr++;
}
此示例将整数数组中每个单元格的值加1。因为指针ptr在每个循环中增加1,所以可以在每个循环中访问数组的下一个单元。

[3]

[4]

1、chara[20];
2、int * ptr = a;
...
...
3、ptr+= 5;
在本例中,ptr加上5。编译器是这样处理的:在指针ptr的值上加5乘以sizeof(int)意味着在32位程序中加5乘以4=20。因为地址的单位是字节,所以当前ptr指向的地址比ptr指向的地址加5后向更高的地址方向移动了20个字节。在这个例子中,加5之前的ptr指向数组a的单元0的前四个字节,加5之后,ptr已经指出了数组a的合法范围,这种情况虽然在应用上会产生问题,但在语法上是可能的。这也体现了指针的灵活性。

如果在上面的例子中,ptr减5,那么过程是类似的,只是ptr的值减5倍sizeof(int),新ptr指向的地址将比原ptr指向的地址低20个字节。
综上所述,指针ptrold加整数n后,结果是一个新的指针ptrnew,其类型与ptrold相同,ptrnew所指向的类型与ptrold所指向的类型相同。与sizeof(ptrold)的值相比,ptrnew的值将增加n倍sizeof(ptr old所指向的类型)字节。也就是说,ptrnew所指向的内存区域将向比ptrold所指向的内存区域更高的地址方向移动n倍sizeof(ptrold所指向的ptrold类型)字节。
指针ptrold减去整数n后,结果是一个新的指针ptrnew。ptrnew的类型与ptrold的类型相同,ptrnew指向的类型与ptrold指向的类型相同。ptrnew的值会比sizeof(ptrold)的值减少n倍sizeof(ptrold所指向的类型)字节,也就是说ptrnew所指向的内存区域会比ptr old所指向的内存区域在低位地址方向移动n倍sizeof(ptr old所指向的类型)字节。
运算符&和*
这里&是地址运算符,而*是...书中称之为“间接运算符”。
&a的运算结果是一个指针,指针的类型是A加a *,指针指向的类型是A,指针指向的地址是A的地址。
*p的操作结果多种多样。总之*p的结果就是p指向的东西。这个东西有这些特征:它的类型是p指向的类型,它占用的地址是p指向的地址
例5:
inta = 12;
intb;
int * p;
int * * ptr;
p = & a;
//&a的结果是一个int*类型的指针,指向int类型,指向地址是a的地址.
* p = 24;
//*p的结果,其中其类型为int,所占用的地址就是p所指向的地址,显然,* p就是变量a .
ptr = & p;
//p的结果是一个指针,它的类型是p加a *的类型,这里是int **。这个指针指向的类型是P的类型,这里是int*。指针指向的地址就是指针p本身的地址。
* ptr = & b;
//*ptr是一个指针,&b的结果也是一个指针,而且这两个指针的类型和所指向的是一样的,所以用& b赋值*ptr是没有问题的,
* * ptr = 34;
//* ptr的结果就是ptr指向的东西,这里是指针。再次对这个指针做*操作,结果是一个int类型的变量。
指针表达式
如果一个表达式的最终结果是一个指针,那么这个表达式就叫做指针表达式。
下面是一些指针表达式的例子:
例6:
inta,b;
int array我们来总结一下数组的数组名问题。在声明数组TYPEarray[n]时,数组名array有两层含义:一是代表整个数组,类型为type[n];二是类型为TYPE*的指针,指针指向的类型是TYPE,即数组单元的类型。指针指向的内存区域是数组单元0,它占用了一个单独的内存区域。请注意,它不同于数组单元0所占用的内存区域。这个指针的值不能修改,就是array++这样的表达式是错误的。
数组名array在不同的表达式中可以扮演不同的角色。
在表达式sizeof(array)中,数组名array代表数组本身,所以sizeof函数度量的是整个数组的大小。
在表达式*array中,array作为指针,所以这个表达式的结果就是数组的单元0的值。Zeof (* array)度量sizeof(*数组单位。
表达式array+n(其中n = 0,1,2,...),数组作为指针,所以数组+n的结果是指针,它的类型是TYPE*,它指向的类型是TYPE,它指向数组的第n个单元。所以sizeof(array+n)度量指针类型的大小。

int * pa;
pa = & a;//&a是指针表达式。
int * * ptr = & pa;//&pa也是指针表达式。
* ptr = & b;//*ptr和&b都是指针表达式。
pa = array;
pa++;//这也是一个指针表达式。
例7:
char * arr[20];
char * * parr = arr;//如果把arr看成指针,arr也是指针表达式
char * str;
str = * parr;//*parr是指针表达式
str = *(parr+1);//*(parr+1)是指针表达式
str = *(parr+2);//*(parr+2)是指针表达式
由于指针表达式的结果是指针,所以指针表达式也有指针所具有的四个元素:指针的类型、指针所指向的类型、指针所指向的内存区域、指针本身所占用的内存。

好的,当一个指针表达式的结果指针明显有指针本身占用的内存时,这个指针表达式就是左值,否则就不是左值。
在示例7中,&a不是左值,因为它没有占用显式内存。*ptr是一个左值,因为指针*ptr已经占用了内存。其实*ptr就是指针pa。既然pa在内存中已经有自己的位置,*ptr当然也有自己的位置。
数组和指针的关系
如果不太明白声明数组的语句,可以参考我前段时间贴的文章。
数组的数组名其实可以看作是一个指针。看下面的例子:
例8:
intarray [10] = {0,1,2,3,4,5,6,7,8,9},value
...
...
value = array[0];//也可以写成:value = * array
value = array[3];//也可以写成:value = *(array+3);
value = array[4];//也可以写成:value = *(array+4);
在上面的例子中,一般来说,数组名array代表数组本身,其类型为int[10]。但如果把array看做一个指针,它指向数组的第0个单元,类型是int*,所指向的类型就是数组单元的类型,即int。所以*array等于0并不奇怪。同样,array+3是指向数组第三个单元格的指针,所以*(array+3)等于3。其他等等。

例9:
char * str [3] = {
“您好,thisisample!”,
“嗨,早上好。”,
" hello world "
};
chars[80];
strcpy(s,str[0]);//也可以写成strcpy(s,* str);
strcpy(s,str[1]);//也可以写成strcpy(s,*(str+1));
strcpy(s,str[2]);//也可以写成strcpy(s,*(str+2));
在上面的例子中,str是一个三单元格数组,数组的每个单元格都是一个指针,每个指针都指向一个字符串。如果将指针数组名str作为指针,则指向数组的第0个单元,其类型为char**,所指向的类型为char*。
*str也是指针,它的类型是char*,它所指向的类型是char,它所指向的地址是字符串“Hello,thisisasample!”的第一个字符的地址,也就是‘h’的地址。Str+1也是一个指针,指向数组的第一个单元。它的类型是char**,它指向的类型是char*。

*(str+1)也是一个指针,它的类型是char*,它指向的类型是char,它指向“Hi,goodmorning”的第一个字符‘h’。”,等等。

[10]

位律师回复
DABAN RP主题是一个优秀的主题,极致后台体验,无插件,集成会员系统
白度搜_经验知识百科全书 » 深入理解C语言指针的奥秘

0条评论

发表评论

提供最优质的资源集合

立即查看 了解详情