在C ++中,我无法掌握指针和类

时间:2008-09-18 19:58:50

标签: c++ class pointers

我刚从大学毕业,现在已经在C ++工作了一段时间。我理解C ++的所有基础知识并使用它们,但我很难掌握指针和类等更高级的主题。我已经阅读了一些书籍和教程,我理解其中的例子,但是当我看到一些高级的现实生活中的例子时,我无法弄明白。这让我很伤心,因为我觉得它让我无法将我的C ++编程提升到一个新的水平。有没有人有这个问题?如果是这样,你是如何突破它的? 有没有人知道任何真正描述指针和类概念的书籍或教程? 或者使用高级指针和类技术的一些示例代码具有良好的描述性注释? 任何帮助将不胜感激。

27 个答案:

答案 0 :(得分:19)

了解C / C ++中的指针

在能够理解指针是如何工作之前,有必要了解如何在程序中存储和访问变量。每个变量都有2个部分 - (1)存储数据的存储器地址和(2)存储数据的值。

内存地址通常称为变量的左值,存储的数据值称为rvalue(l和r表示左右)。

考虑声明:

int x = 10;

在程序内部,程序将内存地址与变量x相关联。在这种情况下,假设程序将x指定为驻留在地址1001(不是实际地址,但为简单起见而选择)。因此,x的左值(内存地址)为1001,x的右值(数据值)为10。

只需使用变量“x”即可访问右值。为了访问左值,需要“地址”运算符('&')。表达式'& x'被读作“x的地址”。

Expression          Value
----------------------------------
x                   10
&x                  1001

存储在x中的值可以随时更改(例如x = 20),但x(& x)的地址永远不会改变。

指针只是一个可用于修改另一个变量的变量。它通过为其右值设置内存地址来实现此目的。也就是说,它指向内存中的另一个位置。

创建指向“x”的指针如下:

int* xptr = &x;

“int *”告诉编译器我们正在创建一个指向整数值的指针。 “=& x”部分告诉编译器我们正在将x的地址分配给xptr的rvalue。因此,我们告诉编译器xptr“指向”x。

假设xptr被分配给1002的内存地址,那么程序的内存可能如下所示:

Variable    lvalue    rvalue
--------------------------------------------
x           1001      10   
xptr        1002      1001

下一个难题是“间接运算符”('*'),其用法如下:

int y = *xptr;

间接运算符告诉程序将xptr的rval解释为内存地址而不是数据值。也就是说,程序查找存储在xptr(1001)提供的地址处的数据值(10)。

全部放在一起:

Expression      Value
--------------------------------------------
x                   10
&x                  1001
xptr                1001
&xptr               1002
*xptr               10

现在已经解释了这些概念,这里有一些代码来演示指针的功能:

int x = 10;
int *xptr = &x;

printf("x = %d\n", x);
printf("&x = %d\n", &x);        
printf("xptr = %d\n", xptr);
printf("*xptr = %d\n", *xptr);

*xptr = 20;

printf("x = %d\n", x);
printf("*xptr = %d\n", *xptr);

对于输出,您会看到(注意:每次内存地址都不同):

x = 10
&x = 3537176
xptr = 3537176
*xptr = 10
x = 20
*xptr = 20

注意为'* xptr'赋值如何改变'x'的值。这是因为'* xptr'和'x'指的是内存中的相同位置,'& x'和'xptr'具有相同的值。

答案 1 :(得分:18)

指针和类在C ++中并不是真正的高级主题。它们非常基础。

对我来说,当我开始用箭头绘制框时,指针会凝固。为int画一个框。而int *现在是一个单独的框,箭头指向int框。

所以:

int foo = 3;           // integer
int* bar = &foo;       // assigns the address of foo to my pointer bar

使用我的指针框(条形图),我可以选择查看框内的地址。 (这是foo的内存地址)。或者我可以操纵我拥有的任何地址。那种操纵意味着我跟随箭头到整数(foo)。

*bar = 5;  // asterix means "dereference" (follow the arrow), foo is now 5
bar = 0;   // I just changed the address that bar points to

课程完全是另一个主题。有一些关于面向对象设计的书籍,但我不知道对于初学者来说是好的。您可能会对介绍Java书籍感到满意。

答案 2 :(得分:7)

This link有一个视频,用于描述指针是如何工作的。信息丰富,易于消化。

This page有一些关于班级基础的好信息。

答案 3 :(得分:4)

我曾经有一个问题以pascal的方式理解指针:)一旦我开始做汇编指针,真的是访问内存的唯一方法,它只是打我。这可能听起来像是一个远远的镜头,但尝试汇编程序(尝试理解计算机的真正意义总是一个好主意)可能会教你指针。课程 - 我不明白你的问题 - 你的学校是纯粹的结构化编程吗?一个类只是查看现实生活模型的一种逻辑方式 - 您正试图解决一个可以在许多对象/类中总结的问题。

答案 4 :(得分:3)

指针和类是完全不同的主题,所以我不会像这样把它们整合在一起。在这两者中,我会说指针更为基础。

了解指针的一个很好的练习如下:

  1. 创建链接列表
  2. 从头到尾迭代它
  3. 反转它,使得头部现在是背部,背部现在是头部
  4. 首先在白板上完成所有操作。如果你可以轻松地做到这一点,你应该没有更多的问题来理解指针是什么。

答案 5 :(得分:2)

我们只是在午餐时讨论C ++和OO的一些方面,有人(实际上是一位伟大的工程师)说除非你在学习C ++之前有一个非常强大的编程背景,否则它会毁了你。

我强烈建议先学习另一种语言,然后在需要时转换为C ++。它并不像指针有什么好处,它们只是一个残留的遗留物,当编译器很难在没有它们的情况下有效地组装转换操作。

现在如果编译器无法更好地优化数组操作,那么你可以使用指针,你的编译器就会崩溃。

请不要误会我的意思,我不是说C ++是可怕的或任何东西,并且不想开始倡导讨论,我已经使用它并偶尔使用它,我只是建议你开始用其他东西。

这真的不像学习驾驶手动车然后轻松地将其应用到自动驾驶车上,更像是学习驾驶这些巨大的建筑起重机之一,然后假设当你开始开车时会适用 - 然后你发现自己开着车在街道中央以5英里/小时开着你的应急灯。

[编辑]回顾最后一段 - 我认为这可能是我最准确的比喻!

答案 6 :(得分:2)

在其他答案中,指针似乎已被解决(没有双关语)。

课程是OO的基础。我遇到了巨大的麻烦,让我陷入OO - 就像十年失败的尝试一样。最终帮助我的书是Craig Larman的“应用UML和模式”。我知道这听起来好像是关于某些不同的东西,但它真的很能让你轻松进入类和对象的世界。

答案 7 :(得分:1)

在类的情况下,我有三种技术可以帮助我跳转到真正的面向对象编程。

第一个是我参与了一个大量使用类和对象的游戏项目,大量使用泛化(种类或是一种关系,例如学生是一种人)和组合(具有 - 一个关系,例如学生有学生贷款)。打破这段代码需要做很多工作,但确实让事情变得透彻。

第二件事就是在我的System Analysis类中,我必须制作http://www.agilemodeling.com/artifacts/classDiagram.htm">UML类图。这些我真的找到了帮助我理解程序中类的结构。

最后,我帮助在我的大学辅导学生编程。我真正能说的就是你通过教学和看到别人对问题的处理方法来学到很多东西。很多时候,学生会尝试一些我从未想过的事情,但通常很有意义,他们只是在实现他们的想法时遇到了问题。

我最好的建议是需要大量练习,编程越多,你就越了解它。

答案 8 :(得分:1)

没有替代练习。

通过阅读书籍或听讲座很容易,并且感觉你正在关注正在发生的事情。

我建议采用一些代码示例(我假设你将它们放在某处的磁盘上),编译它们并运行它们,然后尝试改变它们以做一些不同的事情。

  • 将另一个子类添加到层次结构
  • 向现有班级添加方法
  • 更改迭代的算法 通过集合前进去 落后了。

我认为没有任何“银弹”书可以做到这一点。

对于我来说,是什么驱动了什么指针在装配中起作用,并且看到指针实际上只是一个地址,并且指针并不意味着它所指向的是一个有意义的对象。

答案 9 :(得分:1)

学习汇编语言,然后学习C.然后你就会知道机器的基本原理是什么(以及前面的指针)。

指针和类是C ++的基本方面。如果你不理解它们,那就意味着你并不真正理解C ++。

就个人而言,我已经在C ++上停留了好几年,直到我觉得我已经牢牢掌握了C语言以及汇编语言下的内容。虽然这已经很久很久以前了,但我认为理解计算机在低级别工作的方式确实对我的职业生涯有所帮助。

学习编程可能需要很多年,但你应该坚持下去,因为这是一个非常有益的职业。

答案 10 :(得分:1)

对于指针:

我发现this post对指针进行了非常深思熟虑的讨论。也许这会有所帮助。您是否熟悉C#等引用?这实际上是引用到别的东西?这可能是理解指针的良好开端。

另外,请看下面肯特弗雷德里克的帖子,以另一种方式向自己介绍指针。

答案 11 :(得分:1)

要理解指针,我不能高度推荐K&R本书。

答案 12 :(得分:0)

我真正得到指针的地方是在FatMac上编码TurboPascal(大约在1984年左右) - 当时是原生的Mac语言。

Mac有一个奇怪的内存模型,当分配地址时,内存存储在堆上的指针中,但是本身的位置无法保证,而内存处理例程返回指针指针 - 参考作为一个句柄。因此,要访问分配的内存的任何部分,必须取消引用句柄两次。花了一段时间,但不断的练习最终将课程带回了家。

Pascal的指针处理比C ++更容易掌握,C ++的语法对初学者没有帮助。如果你真的和真正理解C中的指针,那么你最好的选择可能是获得一个副本aa Pascal编译器并尝试在其中编写一些基本的指针代码(Pascal足够接近C,你将在几个小时内获得基础知识)。链接列表等是一个不错的选择。一旦你对那些回归C ++感到满意,并且掌握了这些概念,你就会发现悬崖看起来并不那么陡峭。

答案 13 :(得分:0)

假装指针是一个数组地址。

x = 500; // memory address for hello;
MEMORY[x] = "hello"; 
print  MEMORY[x]; 

它的图形过于简单化,但在大多数情况下,只要你永远不想知道这个数字是什么或手工设置你就应该没问题。

当我理解C时,我有一些宏,我或多或少允许你使用指针就像它们 内存中的数组索引一样。但是我早就失去了那段代码而且很久以前就被遗忘了。

我记得它始于

#define MEMORY 0; 
#define MEMORYADDRESS( a ) *a;

并且它本身并没有用。希望其他人可以扩展这种逻辑。

答案 14 :(得分:0)

您的问题似乎是C ++中的C核,而不是C ++本身。让自己成为Kernighan& Ritchie( C编程语言)。吸气吧。这是非常好的东西,是有史以来最好的编程语言书之一。

答案 15 :(得分:0)

指针不是某种神奇的东西,你一直在使用它们! 当你说:

int a;

并且编译器为'a'生成存储,你实际上是说你要声明一个int而你想要将它的内存位置命名为'a'。

当你说:

int * a;

你声明一个可以保存int的内存位置的变量。 就这么简单。另外,不要总是害怕指针算术 当你处理指针并用术语思考时,请记住一个“记忆图” 遍历内存地址。

C ++中的类只是定义抽象数据类型的一种方式。我建议阅读一本好的OOP书来理解这个概念,然后,如果你有兴趣,可以学习C ++编译器如何生成代码来模拟OOP。但是,如果你坚持使用C ++足够长的话,这些知识将会及时到来:)

答案 16 :(得分:0)

您可能会发现此article by Joel具有指导意义。顺便说一句,如果你已经“在C ++工作了一段时间”并且已经在CS毕业,那么你可能已经去了JavaSchool(我认为你根本没有使用C ++;你已经一直在C中工作但使用C ++编译器)。

另外,只是为了得到hojou和nsanders的答案,指针是C ++的基础。如果你不理解指针,那么你就不了解C ++的基础知识(顺便说一下,承认这个事实是理解C ++的开始)。同样,如果你不理解类,那么你就不了解C ++的基础知识(或OO)。

对于指针,我认为用盒子绘图是个好主意,但在装配中工作也是一个好主意。任何使用相对寻址的指令都可以让你快速了解指针是什么。

对于类(以及更普遍的面向对象编程),我建议使用Stroustrups“The C ++ Programming Language”最新版本。它不仅是规范的C ++参考资料,而且在很多其他方面也有相当多的材料,从基本的面向对象的类层次结构和继承一直到大型系统中的设计原则。这是一个非常好的阅读(如果不是一点点厚实和简洁的点)。

答案 17 :(得分:0)

对于指针和类,这是我的比喻。我会用一副纸牌。卡片组具有面值和类型(心脏中的9个,黑桃中的4个等)。因此,在我们的C ++中,像“Deck of Cards”这样的编程语言,我们会说以下内容:

HeartCard card = 4; // 4 of hearts!

现在,你知道4颗心在哪里,因为在你的手中,你正拿着甲板,面朝上,它在顶部!因此,就剩下的牌而言,我们只会说心中的4个是开心的。所以,如果我问你在BEGINNING有什么牌,你会说,“当然是4个心!”。好吧,你只是“指出”了卡片的位置。在我们的“Deck of Cards”编程语言中,您还可以说以下内容:

HeartCard card = 4; // 4 of hearts!
print &card // the address is BEGINNING!

现在,转过你的牌组。背面现在是BEGINNING,你不知道卡是什么。但是,让我们说你可以随心所欲地制作它,因为你充满了魔力。让我们在“Deck of Cards”语言中做到这一点!

HeartCard *pointerToCard = MakeMyCard( "10 of hearts" );
print pointerToCard // the value of this is BEGINNING!
print *pointerToCard // this will be 10 of hearts!

嗯,MakeMyCard(“心中的十分之一”)是你在做你的魔法并且知道你想要指向BEGINNING,让这张卡成为心中的10个!你把卡翻了过来,瞧!现在,*可能会让你失望。如果是这样,请查看:

HeartCard *pointerToCard = MakeMyCard( "10 of hearts" );
HeartCard card = 4; // 4 of hearts!
print *pointerToCard; // prints 10 of hearts
print pointerToCard; // prints BEGINNING
print card; // prints 4 of hearts
print &card; // prints END - the 4 of hearts used to be on top but we flipped over the deck!

对于类,我们通过将类型定义为HeartCard来使用示例中的类。我们知道HeartCard是什么......这是一张有价值和心灵的卡片!因此,我们将其归类为HeartCard。每种语言都有类似的方式来定义或“分类”你想要的东西,但它们都有相同的概念!希望这有助于......

答案 18 :(得分:0)

你读过Bjarne Stroustrup的 C ++编程语言吗?他创建了C ++。

C ++ FAQ Lite也不错。

答案 19 :(得分:0)

从某种意义上说,你可以认为“指针”是软件中最基本的两种类型之一 - 另一种是“值”(或“数据”) - 存在于一大堆可唯一寻址的内存位置。想一想。对象和结构等在内存中并不存在,只有值和指针才能存在。实际上,指针也是一个值....内存地址的值,它又包含另一个值....依此类推。

因此,在C / C ++中,当你声明一个“int”(intA)时,你正在定义一个32位的内存块,它包含一个值 - 一个数字。如果然后声明一个“int pointer”(intB),则定义一个32位的内存块,其中包含int的地址。我可以通过声明“intB =& intA”来指定后者指向前者,现在定义为intB的32位内存包含与intA在内存中的位置相对应的地址。

当您“取消引用”intB指针时,您正在查看存储在intB内存中的地址,找到该位置,然后查看存储在那里的值(数字)。

通常情况下,当人们使用“&”,“*”和“ - >”失去他们正在处理的确切内容时,我遇到了困惑。运营商 - 是地址,价值还是什么?您只需要关注内存地址只是位置这一事实,并且该值是存储在那里的二进制信息。

答案 20 :(得分:0)

真正帮助我理解这些概念的一件事是学习UML - 统一建模语言。以图形格式看到面向对象设计的概念确实帮助我了解它们的含义。有时试图仅仅通过查看源代码实现它们来理解这些概念可能很难理解。

以图形形式看待面向对象的范例,例如继承,是掌握这一概念的一种非常有效的方法。

Martin Fowler的 UML Distilled 是一个很好的简短介绍。

答案 21 :(得分:0)

课程相对容易掌握; OOP可能需要你很多年。就个人而言,直到去年,我还没有完全掌握真正的OOP。 Smalltalk在大学里并不像应该的那样普遍存在,这太糟糕了。它真正推动了OOP关于对象交易消息的观点,而不是类是具有函数的自包含全局变量。

如果你真的不熟悉课程,那么这个概念可能需要一段时间才能掌握。当我第一次在10年级遇到它们时,直到我知道他们正在做什么并逐步完成代码并解释发生了什么,我才得到它。这就是我建议你尝试的。

答案 22 :(得分:0)

我读过的关于这些主题的最好的书是Bruce Eckel在C ++中的思考。您可以免费下载here

答案 23 :(得分:0)

对于课程:

我的突破时刻是我了解接口的时候。抽象出你如何编写的细节解决问题的想法,以及只提供与班级互动的方法列表是非常有见地的。

事实上,我的教授明确地告诉我们,他会通过将我们的课程插入他的测试工具来评定我们的课程。评分将根据他给我们的要求以及程序是否崩溃来完成。

长话短说,课程让你结束功能并以更干净的方式调用它(大多数时候,总会有例外)

答案 24 :(得分:0)

唐纳德·阿尔科克(Donald Alcock)提到的那本书是Illustrating Ansi C。它充满了手绘风格的框和箭头图,说明了指针,指针算术,数组,字符串函数等......

显然它是一本'C'书,但对于核心基础来说很难被击败

答案 25 :(得分:0)

为了更好地理解指针,我认为,查看汇编语言如何与指针一起工作可能会有用。指针的概念实际上是汇编语言和x86处理器指令体系结构的基本部分之一。也许它会让你失败,因为指针是程序的自然组成部分。

对于类,除了OO范例之外,我认为从低级二进制角度看类可能很有趣。在基础层面上,它们在这方面并不复杂。

如果您想更好地了解C ++对象模型下的内容,可以阅读Inside the C++ Object Model

答案 26 :(得分:0)

从lassevek对similar question on SO的反应:

  

对于许多人来说,指针是一个概念   起初可能会令人困惑   特别是在复制方面   指针值仍然存在   引用相同的内存块。

     

我发现最好的比喻是   将指针视为一块   带有房子地址的纸,和   它引用的内存块为   实际的房子。各种各样的操作   因此可以很容易地解释:

     
      
  • 复制指针值,只需在新纸上写下地址
  •   
  • 链接列表,房子里的纸张,下一个地址   房子就可以了
  •   
  • 释放内存,拆除房屋并删除地址
  •   
  • 内存泄漏,你丢失了一张纸,找不到房子
  •   
  • 释放内存但保留(现在无效)引用,拆除内存   房子,擦掉其中一块   纸,但有另一张纸   当你的时候,有旧的地址   去地址,你找不到   房子,但你可能会找到一些东西   类似于一个
  • 的废墟   
  • 缓冲区溢出,你移动更多的东西进入房子比你可以   可能适合,溢出   邻居家
  •