QQ登录

只需一步,快速开始

 找回密码
 注册

QQ登录

只需一步,快速开始

查看: 4024|回复: 18

关于指针的学习(还没写完)

[复制链接]
发表于 2003-9-6 23:16:02 | 显示全部楼层 |阅读模式
嗯……本文我还没写完,等有增加的新内容,我再回贴好了~
嘿嘿~这文章可是特地为这里写的~
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类型的函数指针
下面的问题就可以很简单了~各位还是自己看看,思考一下,我就不在这里费话了
发表于 2003-9-7 17:50:53 | 显示全部楼层
没写完?好长啊!“C语言(和C++)中有很多地方都是其他高级语言无法比拟的,其中的指针更是她的精髓所在”……也引来了很多问题呀,我就经常用不好。当然在高手手中指针是最灵活的,希望看到更多!学到更多!
回复

使用道具 举报

 楼主| 发表于 2003-9-7 18:50:50 | 显示全部楼层
呵呵~多谢支持~
其实写这文章的原因是,当初我因为指针使用和动态分配的错误,导致整个项目(1000多行的代码啊~~)几乎要重写~于是我花费了一天的时间,又请教了高手,加上以前看书的一些东西(这以前书上看的东西都是死记硬背放到脑子里面的,根本不会灵活应用)整理一下,这才感觉对指针有了初步了解~因此有了写本文的打算~也就是说~本文是为了祭奠我那夭折的一千多行代码的~
回复

使用道具 举报

发表于 2003-9-10 10:08:29 | 显示全部楼层
The <c trap and pit falls> where can be downloaded ? Please give me a URL. THx.
回复

使用道具 举报

发表于 2003-9-10 15:56:55 | 显示全部楼层
不错,清楚易懂,只是有些地方不够详细,可能是没有写完的原故吧
回复

使用道具 举报

 楼主| 发表于 2003-9-12 23:54:35 | 显示全部楼层
to rocklgk:真抱歉,我看的不是电子版,而且现在也没找到电子版
to Austern:多谢~我会努力写完的~
回复

使用道具 举报

发表于 2003-9-16 11:18:31 | 显示全部楼层
指针只能让我头疼,所以我才学java,虽然java的对象引用和指针几乎是一个意思,不过还是简单一点,我的智商......
回复

使用道具 举报

发表于 2003-9-16 12:25:16 | 显示全部楼层
晕,我觉得如果没有指针,很多数据结构实现起来就很不爽了,特别在空间使用的灵活性上就特别不爽了!
回复

使用道具 举报

发表于 2003-9-17 14:03:03 | 显示全部楼层
这个帖子对我来说很合适,有例子就好:)
回复

使用道具 举报

发表于 2003-9-17 18:17:54 | 显示全部楼层
晕,我觉得如果没有指针,很多数据结构实现起来就很不爽了,特别在空间使用的灵活性上就特别不爽了!

java的引用和指针是一个意思,不过理解起来好一些,产生错误的机会也会少很多
回复

使用道具 举报

 楼主| 发表于 2003-9-19 22:19:50 | 显示全部楼层
[quote:d6323ec378="sjinny"]晕,我觉得如果没有指针,很多数据结构实现起来就很不爽了,特别在空间使用的灵活性上就特别不爽了![/quote]呵呵~我很赞成的~java中的引用在C++中也有,但是就不能像指针那样灵活跳跃吧??比如:
char *a="abcdefghijklmnop";
if('n'==*(a+6))
{
//...
}
这样的语句在java的引用里面可以实现吗??(抱歉,我没看过java的东西,问一下)还有:动态分配java是怎么实现的啊???呵呵~随便问问,当作是知识拓展啦~
回复

使用道具 举报

发表于 2003-9-22 08:40:35 | 显示全部楼层
char *a="abcdefaffsadfasfasfjsdklfjsl;dfjs;adklfj
这样定义对吗,应该是个指针型数组吧
回复

使用道具 举报

发表于 2003-9-23 15:16:39 | 显示全部楼层
555555555555555555要去买书了,我的大洋啊!!!哪位能提供电子版的,不胜感激!   
回复

使用道具 举报

发表于 2003-9-28 01:11:42 | 显示全部楼层
有时候确实被指针搞糊涂的
对一般的应用还好
但是遇到指针指到函数入口什么的
以及多维数组用到指针的时候
就要郁闷了
最近在阅读ecos 原代码
就有这个问题
回复

使用道具 举报

 楼主| 发表于 2003-9-30 18:43:59 | 显示全部楼层
[quote:0f91b43f34="pepsion"]char *a="abcdefaffsadfasfasfjsdklfjsl;dfjs;adklfj
这样定义对吗,应该是个指针型数组吧[/quote]这个……定义好像不对啊~少了一个双引号~
如果加上那个双引号的话:
a里面存储的是一个地址,而"abcdefaffsadfasfasfjsdklfjsl;dfjs;adklfj"存储在函数的栈中,一旦函数执行完毕,这个指针a和a所指向的内容(就是"abcdefaffsadfasfasfjsdklfjsl;dfjs;adklfj")都将随着消失,至于a是不是数组,其实数组和指针的关系是这样的:
[code:1]char a[]="abc";[/code:1]这里面:a的地址是"abc"的首地址即'a'所在的地址;
[code:1]char *b="abc";[/code:1]这里面:b存储的是"abc"的首地址,即'a'的地址,注意:b存储的是地址,a存储的是'a'而不是'a'的地址~
其实,数组和指针有很多相似的地方,可以类比着学习~其实:这样一个函数:
[code:1]int a(int b[]);[/code:1]那么首先,会把b退化为指针即:
[code:1]int a(int *b);[/code:1]
呵呵~不知道我这么解释你是否能明白~
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

GMT+8, 2024-11-9 10:49 , Processed in 0.125347 second(s), 15 queries .

© 2021 Powered by Discuz! X3.5.

快速回复 返回顶部 返回列表