深度剖析C/C++经典著作指针——深入浅出解读指针,告诉你为什么一要学指针【建议初学者收藏】

为什么需要这木书
市面上已经有了许多优秀的讲述C语言的书籍,为什么我们还需要这一本呢?我在大学里教授C语言编程已有1个年头,但至今尚未发现 本书是按照我所喜欢的方式来讲述指针的。许弟节籍用一章的篇幅专门讲述指针,而且往往出现在全书的后半部分。但是, 仅仅描述指针的语法、并用一些简单的例子展示其用法是远远不够的。我在授课时,很早 便开始讲授指针,而且在以后的授课过程中也经常讨论指针。我描述它们在各种不同的上 卜’文环境中的有效用法,展示使用指针的编程惯用法(programming idiom)=,我还讨论了 ,些 相关的课题如编程效率和程序可维护性之间的权衡。指针是本书的线索所在,融会贯通于 全持之中。

指针为什么如此重要?我的信念是:正是指针使C威力无穷.有嗖任务用其他语言也可 以实现,但C能够更有效地实现;有些任务无法用其他语言实现,如直接访问硬件,但C却 可以。要想成为一名优秀的C程序员,对指针有一个深入而完整的理解是先决条件。

然面,指针虽然很强大,与之相伴的风险却也不小。跟指甲轮相比,链锯可以更快地切 割木材,但链锯更容易使你受伤,而且伤害常常来得极快,后果也非常严重.指针就像链锯 一样,如果使用得当,它们可以简化算法的实现,并使其更富效率;如果使用不当,它们就 会引起错误,导致细微而令人困惑的症状,并且极难发现原因。对指针只是略知一二使放手 使用是件非常危险的事。如果那样的话,它绐你带来的总是痛苦而不是欢乐。本书提供了你 所需要的深入而完整的关于指针的知识,足以使你避开指针可能带来的痛苦。

有时,在Web上,我会进行无数次有关C或C ++等语言的指针的讨论,关于使用指针是否值得甚至是明智的争论不断。 这个问题似乎一直持续到今天。 因此,希望澄清一些事情,我决定创建这篇文章。 我不会对这个问题过于熟练,也不会虔诚地要求每个人 new必须具有相应的 delete或每个人 malloc必须具有相应的 free。

本文适用于C和C ++,您除了处理原始指针外别无选择。 其他语言(例如Java,C#等)将所有的伏都教魔术隐藏在幕后。 它也适合那些不使用ASM,C或C ++编写代码但被主题迷惑的初学者。

我将尝试涵盖所有情况,而不会太深入技术细节。 如果您希望对指针有非常深入的技术知识,那么从Wikipedia到众多博客都可以找到很多信息。 cplusplus dot com网站上有一篇很棒的关于指针的文章,因此在这里我不再重复。 鉴于您正在使用C或C ++进行开发,因此我将在以下场景中进行介绍:

  • 什么时候不使用指针就不可能实现某些东西
  • 使用指针的优点
  • 通过值,引用或指针传递变量
  • 悬空指针

什么是指针?

指针是一个整数变量保持在所述特定的宽度(一个地址 float, double, int, struct, class,等等)值被存储在计算机存储器中。 因此,指针是计算机在金属层上本机可以理解的“事实”对象。 指针始终是宽度为8、16、32、64、128等位的无符号整数。 这在很大程度上取决于CPU的主寄存器宽度。 它也由操作系统运行时位对齐指示。 完全有可能在64位CPU上运行16位OS,但反之亦然。 但是,在16位OS中,即使CPU为64位宽,您也将被限制在16位地址空间中。

旁记:由于64位寄存器的宽度足以满足上述16位OS的需要,因此可以编写一个特殊的分段内存管理器,该管理器可以窥视超过64Kb的限制,但前提是物理寄存器必须足够宽(哦,旧的16位Windows) )。 16位内存段和偏移量的主题在今天已经过时了。 在当前的讨论中,我仅针对平面内存。

指针的宽度与该指针可以容纳的可寻址内存直接相关,并且其宽度的乘幂为2减1。

通过查看此表,您可以看到过去和将来。

如果您要在sifi小说领域中醒来,首先要检查的是它们使用的计算机上的可寻址指针存储器,以了解您要处理的内容 。

深度剖析C/C++经典著作指针——深入浅出解读指针,告诉你为什么一要学指针【建议初学者收藏】_第1张图片
当今的64位处理器中确实存在128位寄存器,通常用于SIMD(单指令多数据)操作码。 长话短说–它们允许并排加载四个32位整数到同一个寄存器中,并在一个CPU周期(赫兹)中执行多个指令,例如MulDiv等。 值得一看:1 EB等于一百万TB。

好。 那么什么是指向指针的指针?
指向指针的指针是一个整数变量,它保存指针的地址。 它通常用作函数的返回值。 它也用于可能出现悬挂指针的危险的地方。

// Calling a function that returns a pointer
//
void* ptr = nullptr;
// declared as void SomeFuncReturnsPtr(void** p) { *p = value; }
SomeFuncReturnsPtr(&ptr);
// ptr is no longer null
ptr->DoStuff();

悬空指针
作为直接指针(又称为指针副本)传递给不同函数的指针面临着以下危险:如果该函数删除了该指针,则该指针在另一个函数中的另一个副本不会无效。 它仍然保留已擦除内存的地址,因此成为悬空指针,因为它的状态似乎是有效的(不是 null)。 此类指针结果的任何用法都是未定义的运行时行为。 在非托管环境中,这是一个真正的危险,因为它在运行时很难找到,因此最好在编码阶段需要面对。

以下代码防御性地处理了指针悬空的可能性:

// Disastrous main
void main()
{
     
        A* ptr = new A;
        NukeA(ptr);
        // ptr now is a dangling pointer
        assert(ptr == nullptr); // Kaboom. Still points to wiped memory
        delete ptr; // Kaboom
}
 
// Safe main
void main()
{
     
        A* ptr = new A;
        NukeSafelyA (&ptr);
        // ptr now is null
        assert(ptr == nullptr);
}
 
void NukeA(A* p)
{
     
        p->DoThis();
        p->DoThat();
 
        delete p; // Kaboom! A dangling pointer is born
 
        p = nullptr;
}
 
void NukeSafelyA(A** p)
{
     
        (*p)->DoThis();
        (*p)->DoThat();
        delete *p; // Nice
        *p = nullptr;
}

实际上,如果您使用指针来动态分配内存,则永远不要将指针传递给另一个函数,尤其是该函数可以或可能删除它时。 仅通过指针将其传递给指针,然后 了该指针并 中将 则您的指针将变为 null如果实际上删除 在另一个函数 其无效。 这是一个非常简单的示例,但确实演示了何时可以发生。

也许干巴巴的文字看起来有写枯燥,如果单看文字不是很容易消化的话,可以进群【1106675687】来跟大家一起交流学习,群里也有许多视频资料和技术大牛,配合文章一起理解应该会让你有不错的收获。

推荐一个不错的C/C++ 初学者课程地址,这个跟以往所见到的只会空谈理论的有所不同,这个课程是从六个可以写在简历上的企业级项目入手带领大家学习c/c++,正在学习的朋友可以了解一下。

指针从哪里开始?
计算机金属根本不在乎甚至不知道变量是什么或变量是什么。 它所了解的只是CPU寄存器和内存地址-指针。 话虽这么说,编译器通过变量声明创建了一种错觉,使CPU可以将其与内部寄存器以及从中加载该值的内存地址相关联。

局部变量和指针之间的差异
所有局部变量都被声明并驻留在函数堆栈框架中。 指向动态分配内存的指针也位于堆栈帧上,但它指向程序的全局堆内存。 在不受管理的全局堆中,程序有责任对其进行管理。 因此,任何泄漏的内存最终都会导致资源匮乏,并使您的程序迟早崩溃。

那么什么时候没有指针就不可能实现?
我可以列举几种这样的情况,但是让我在进一步尝试之前先解决一下。 每个程序都有多个功能堆栈框架和一个全局堆。 全局堆基本上是OS管理的虚拟内存。 堆栈是LIFO(后进先出)受限大小的数据结构。 可以想象,到相邻堆栈的任何溢出都将有效擦除已保存的数据并使程序崩溃。

但是,有那么多神奇的智能指针!
记住这一点。 没有任何代码看起来和行为像由不知道指针实际是什么或代表什么的人编写的智能指针所骑的代码一样糟糕。 在尝试使用智能指针沉迷程序之前,请阅读以下所有情况。 另外,返回并重新阅读“悬空指针”部分。 当通过资产而不是通过脑细胞处理原始指针到智能指针的连接/分离时,智能指针会创建此类底层而臭名昭著。

什么是调用堆栈和堆栈溢出
堆栈是LIFO数据结构。 实际上,编译程序也称为“堆栈计算机”。 每个进程和线程都有自己的堆栈。 每个堆栈都细分为调用堆栈。 调用堆栈的数量与程序中函数的数量完全匹配。 调用堆栈是堆栈中的较小块。 调用堆栈大小有一个限制,通常为1Mb。 在UNIX系统上,它是一个环境变量(我相信)。 Visual C ++编译器允许您使用 来更改调用堆栈的大小 /F标志 。

堆栈溢出最好通过以下 伪代码来表征 _chkstk()函数 :

;***
;_chkstk - check stack upon procedure entry
;
;Purpose:
;       Provide stack checking on procedure entry. Method is to simply probe
;       each page of memory required for the stack in descending order. This
;       causes the necessary pages of memory to be allocated via the guard
;       page scheme, if possible. In the event of failure, the OS raises the
;       _XCPT_UNABLE_TO_GROW_STACK exception.
;
;       NOTE:  Currently, the (EAX < _PAGESIZE_) code path falls through
;       to the "lastpage" label of the (EAX >= _PAGESIZE_) code path.  This
;       is small; a minor speed optimization would be to special case
;       this up top.  This would avoid the painful save/restore of
;       ecx and would shorten the code path by 4-6 instructions.
;
;Entry:
;       EAX = size of local frame
;
;Exit:
;       ESP = new stackframe, if successful
;
;Uses:
;       EAX
;
;Exceptions:
;       _XCPT_GUARD_PAGE_VIOLATION - May be raised on a page probe. NEVER TRAP
;                                    THIS!!!! It is used by the OS to grow the
;                                    stack on demand.
;       _XCPT_UNABLE_TO_GROW_STACK - The stack cannot be grown. More precisely,
;                                    the attempt by the OS memory manager to
;                                    allocate another guard page in response
;                                    to a _XCPT_GUARD_PAGE_VIOLATION has
;                                    failed.
;
;*******************************************************************************

由于堆栈大小有限,因此会监视溢出到相邻堆栈上的内容,如果发生这种情况,则会引发异常。 A _PAGESIZE_在32位操作系统上为4Kb,在64位操作系统上为8Kb,因此,如果任何变量大小大于页面大小,则会对其进行检查,但不一定会导致堆栈溢出,Alloca()函数在运行时从本地堆栈中拉出内存。

由于篇幅有限,需要的高清原资料的可以点击这里,看到会通过哦
深度剖析C/C++经典著作指针——深入浅出解读指针,告诉你为什么一要学指针【建议初学者收藏】_第2张图片
深度剖析C/C++经典著作指针——深入浅出解读指针,告诉你为什么一要学指针【建议初学者收藏】_第3张图片

你可能感兴趣的:(指针,堆栈,c++,编程语言,c语言)