|
嗯……本文我还没写完,等有增加的新内容,我再回贴好了~
嘿嘿~这文章可是特地为这里写的~
C语言(和C++)中有很多地方都是其他高级语言无法比拟的,其中的指针更是她的精髓所在,我
自知才疏学浅,因此不敢说真正掌握了指针,所以此文仅仅是我对以前学习的一个总结,并没
有任何的教学性质,只是希望能对初学者有帮助.如果文中出现任何错误,还请多多包含,
并能给我指出
感谢各位的支持
同时,本文的很多内容都是来自很多经典的书籍(因此,可以说不算是完全的原创),在最后,我
会把一些我个人很喜欢的经典书籍列出来,肯定是不全面的,但是相信那些书对初学者的帮助
绝对大于本文对初学者的帮助.
本文所有代码全部在Linux(Redhat8.0 Kernel2.4.18-14)+gcc(version 3.2 20020903)环
境下,通过编译(部分代码是用来说明错误的,因此自然不会通过编译啦~),同时,此文将
用到gdb(5.2.1-4)调试程序进行说明,如果你对Linux不是很了解,也不用担心,我会在每
一个命令之后加上注释,注释会以//开始,到行尾结束
下面步入正题~~
指针?那是什么?
首先,让我们了解一下,什么是指针---这在很多教科书上都有很详细的解释---所谓指针,
就是存放内存地址的变量---注意,是地址,不是内容,尽管很多人都清楚这一点,但仍然
会在使用的时候,把地址和指针所指向的内容混淆(我自己也犯过不少这样的错误...)
定义一个指针变量很容易---这个各位都应该会了,自然不用我在这里多费话~那么,请看下面
的程序:
/****************程序1:test1.c************************/
[code:1]
#include <stdio.h>
int main()
{
char *a="abcdefaffsadfasfasfjsdklfjsl;dfjs;adklfj";
printf("sizeof(int *)=%d,\nsizeof(char *)=%d,\nsizeof(float *)=%d,\
\nsizeof(a)=%d\n"
,sizeof(int *),sizeof(char *),sizeof(float *),sizeof(a));
return 0;
}
[/code:1]
/***************************************************/
执行结果如下:
/*****************程序1执行结果*********************/
[code:1]
[monnand@monnand test]# gcc test1.c //用gcc编译成可执行文件a.out
[monnand@monnand test]# ./a.out //执行程序a.out
sizeof(int *)=4,
sizeof(char *)=4,
sizeof(float *)=4,
sizeof(a)=4
[/code:1]
/***************************************************/
好拉~大家应该看到了,无论指针的基类型如何(int char float...),内容如何,它所占的内
存数量始终是4字节. 这是因为指针中,存储的是地址,明白这一点,对于指针的学习有莫大的帮助~---尽管所有的C/C++教科书上都应该写到了这一点,但这一点却是常常被初学者忽视的
.
前面讲过了,既然是变量,那么就可以对它进行加减乘除等等一系列运算,而这些运算,都是对
变量本身---对于指针变量来说就是地址---进行的,因此,对指针所指向的内容没有丝毫影响
.(后面会给出一些例子)
* & []等等这些符号都是干什么用的?
*这个符号吗?那我们就从头说起好了:
首先,我们定义一个指针变量:
char *a=NULL;
好了~现在已经定义了一个指针变量a,那么a的类型是什么呢?也许有些人认为是char,那么我
们来看看其他的类型变量的定义:
int b;
float c;
等等这些,我们都会发现,定义一个变量的方式都是: 类型 变量名 的格式,因此,上面的b,自
然就是int 类型,c则是float,那么char *a呢?
其实,char是a的基类型,而a的类型应该是 char * ---这个在教科书上也应该有相应的说明.
这种类型的变量是存储一个地址的---前面已经对它进行了说明.注意,这里的*和后面说的运
算符号*是两个不同概念,希望大家不要混淆.
其实可以用更灵活的方法去理解:
int *a;
//可以这么理解: *a的类型是int,a的类型是int *
int fun(char *);
//fun(char *)的类型是int
int (*func)(void);
//这样,func就是指向返回类型为int类型的函数指针,func类型就是int (*)()
这些在强制类型转换的时候很有用的~
那么,现在说一下*这个运算符.
*的名字是间接运算符(也许不同书上的名字不同)它的作用是:将所修饰的变量值作为存储或
获取数据的地址.也就是说,通过它,可以获得指针所指向的内容---同样,也可以获得某个特
定地址的内容---当然,最后的运算结果的类型,就是指针的基类型.
&运算符:
&可以返回它后面跟的对象的地址
[]呢?
其实这个运算符,就是一种简写,可以用等价的加法运算配合*运算符来获得相同的效果,
比如:
char *a="abc";
那么:
a[2]=0;
和
*(a+2)=0;
就是等效的,我们可以这样理解:a[x]的运算结果是*(a+x)
可以通过* & []以及对指针的加减,来对某个特定内存的内容进行修改
总结一下:
在定义中,*与前面的基类型结合,形成了指针的类型
那么其他情况下,*作为单目运算符时(...双目运算符的时候是乘法),它后面的内容必须是一
个地址---无论是地址变量(指针)还是地址常量(数组名)---它能够计算出相应地址的内容
&后面的内容是一个变量---这样可以计算出变量的地址
[]可以用*配合加法进行等效的运算,但是用[]会方便很多---至少少写了点东西~
说到这里,我看还是用一个经典的例子说明一下吧(本例选自Andrew Koenig所著的<<C陷阱与
缺陷>>---<<C Traps and Pit falls>>---第二章,很强的书啊~14年18次印刷~~强烈建议各
位看一看---无论是初学者还是大虾)
例子如下:
首先来看一个比较让人头疼的语句:
(*(void(*)())0)();
说实话,我看到这东西的时候第一反映便是不想看下去了,但是本着对自己负责的精神,我还
是硬着头皮往下看,其实并不是想象中的那么深不可测.
Koenig解释,这个语句的作用是:它出自一个独立运行于某种微处理器上的C语言程序.当计算
机启动的时候,硬件将调用首地址为0位置的子例.
其实我在前面讲*的时候已经大致讲了这种问题的思考过程,在这里,再来介绍一下:
首先~任何C变量的声明都由两部分组成:类型和一组类似表达式的声明符.声明符从表面上看
与表达式有些类似,对它求值应该返回一个声明给定类型的结果.比如:
float f,g;
这个声明的含义是:当对其求值时,表达式f和g的类型是float
同样逻辑,也适用于指针和函数:
float ff();
那么表达式ff()求知结果是一个float类型,也就是说,ff是一个返回值为float类型的函数
同样:
float *pf;
这个声明的含义是:*pf是一个float类型(注意,不要和我前面说的混淆:*pf是float类型,但
是pf是 float * 类型)也就是说,pf是一个指向float的指针.
以上这些形式还可以组合起来使用:
float *g(), (*h)();
这表示:g是一个返回值为float *类型的函数,那么h则是一个函数指针,返回值为float类型
一旦我们知道了如何声明一个给定类型的变量,那么该变量的类型转换符也就是说很容易得
到了(这个类型转换符是用来强制类型转换的):只需把声明中的变量名和声明末尾的分号去
掉,再将剩余的部分用括号括起来,就可以了.比如:
float (*h)();
表示h是一个指向返回值为float类型的函数指针,那么:
(float(*)())
表示一个"指向返回值类型为float的函数的指针"的类型转换符
好了~上面的是预备知识,现在,让我们看看刚才让我们头疼的那条语句吧~
里面的void(*)()便是说:指向返回值为void类型的函数指针,那么它---void(*)()---也就是
把0强制转化为一个指向返回值为void类型的函数指针
下面的问题就可以很简单了~各位还是自己看看,思考一下,我就不在这里费话了 |
|