【问题标题】:How to Approach Pointers in C? [duplicate]如何在 C 中处理指针? [复制]
【发布时间】:2010-10-04 19:02:06
【问题描述】:

我对 C 语言非常陌生,并且在学习指针方面遇到了一些问题。我尝试了交换,这就是我能用它们做的所有事情:) 我知道每个变量在内存单元中都有自己的地址(这是我的讲师告诉我们的),每个变量的值都可以通过转到其关联地址然后获取存储在其中的值。我见过一些函数头,例如:

int doSomething(char **hihi);

我的脑袋很混乱。我知道指针也是一个变量,它只将地址信息存储在它的内存单元中。我读到它们与数组密切相关

arr = &arr[0];

这就是我对指针的全部了解,我想知道如何加深对指针的看法。我在网上搜索,我找不到任何有用的备忘单涵盖指针。而且我还想知道为什么它们如此重要,有没有办法在不使用printf() 打印它们的地址(p)和值(\*p)的情况下了解实际发生了什么??

【问题讨论】:

  • 您可以在 * 之前添加一个反斜杠来转义它,以便显示文字 * 而不是将两个 * 之间的文本更改为斜体。

标签: c pointers


【解决方案1】:

打印地址和值是查看它们的合理方式。但是,如果您可以启动并运行调试器,那就更好了,因为您可以更快地跟踪指针,观察它们在您执行时的变化等等。

如果您熟悉 Windows 中的“快捷方式”或 linux 文件系统中的软链接,那么在您刚开始时,将指针视为指向另一个对象的快捷方式(软链接)可能会有所帮助(该对象是否是结构、内置类型、另一个指针等)。

快捷方式仍然是一个文件。它在磁盘驱动器上占用自己的空间,它引用另一个文件,并且可以修改它以引用与以前不同的文件文件。类似地,C 中的指针是一个占用内存的对象,包含另一个内存位置的地址,并且可以通过分配来更改为包含不同的地址。

一个区别是如果你双击一个快捷方式,它的行为就像你双击了它所指向的东西一样。指针不是这种情况——你总是必须用“*”或“->”显式地取消引用指针才能访问它指向的东西。另一个不同之处在于,在 C 语言中,指针指向某些东西的指针是很常见的。

至于行话,不幸的是你必须学习它。 “int doSomething(char **hihi)”的意思是“一个名为 doSomething 的函数,它返回一个整数,并将一个指向 char 指针的指针作为参数”。关键是“char ** hihi”的意思是“指向字符的指针。我们称指针指向字符的指针hihi”。你说hihi的“类型”是char**,而*hihi的“类型”(解引用指针时得到的)是char*,而**hihi的类型是char。

在 C 中,指向 char 的指针通常表示字符串(换句话说,它是指向 NUL 终止数组中的第一个 char 的指针)。所以“char *”经常意味着“字符串”,但它不是必须的。它可能只意味着指向一个字符的指针。有点像 Windows 中 1 字节文件的快捷方式(好吧,无论如何都是 FAT32),指向 C 中 char 的指针实际上比它指向的东西大:-)

同样,char** 通常不仅意味着指向一个字符串指针的指针,还意味着指向一个字符串指针数组。可能不会,但如果确实如此,那么下面的小图可能会有所帮助:

嗨嗨 ____ ____ ________ _________ _______ |____| -----> |____| *嗨 ---> |___A____| |___B_____| |___C___| |____| *(hihi+1) ------------------^ ^ |____| *(hihi+2) ---------------------------------| | ...|等等。

hihi 指向塔式工作,这是我表示指针数组的方式。正如您已经指出的,我可以用 hihi[0] 代替 *hihi,用 hihi[1] 代替 *(hihi+1),等等。

这是一个连续的内存块,它的每个指针大小的块都包含(即,它“指向”)另一个内存块的地址,关闭天知道在哪里,包含一个或多个字符.所以,hihi[0] 是字符串 A 的第一个字符的地址,hihi[1] 是字符串 B 的第一个字符的地址。

如果 hihi 不指向数组,只是一个指针,那么塔楼就是平房。同样,如果 *hihi 不指向字符串,仅指向一个字符,则长而细的块是正方形。您可能会问,“我怎么知道塔楼有多少层?”。这在 C 编程中很重要——通常函数文档会告诉你(它可能会说“1”或“12”,或者“足够你告诉我做的事情”,否则你会通过楼层数作为额外参数,否则文档会告诉您数组是“NULL终止”,这意味着它将继续读取直到它看到地址/值NULL,然后停止。主函数实际上同时执行第二和第三件事 - argc 包含参数的数量,and 只是为了安全起见,argv 以 NULL 结尾。

因此,每当您看到指针参数时,您都必须查看函数的文档以查看它是否需要指向数组的指针,如果需要,数组必须有多大。如果您对此不小心,您将创建一种称为“缓冲区溢出”的错误,其中一个函数期望一个指向大数组的指针,你给它一个指向小数组的指针,它在结尾处乱写你给它的东西并开始破坏内存。

【讨论】:

  • 很好的解释,但请您将图表中的 (*hihi)+1 等更改为 *(hihi+1)。
  • 好点,做得好。如果有人对此感到困惑,(*hihi)+1 是指向 A 的第二个字符的指针。也就是说,它比 *hihi 的值大 1。
【解决方案2】:

我认为这是古典书籍比大多数在线资源更有用的地方。如果你能得到一份副本,请非常非常仔细地阅读C 编程语言(又名 K&R)。如果您想了解更多信息,请前往 Expert C Programming: Deep Secrets(只需 google 即可)。

【讨论】:

  • Expert C Programming: Deep Secrets 是一本关于这个主题的非常好的书,它有一整章讨论了为什么指针和数组不是一回事(虽然仍然相关......)
  • +1 表示 K&R。很棒的 C 参考资料。
【解决方案3】:

指针是一个地方。

数组是一组连续的地点。

一个地方总是有价值的。 (可能是剩下的垃圾)。

每个变量都有一个位置。

对于指针变量,其位置的值是一个位置

这就像一场寻宝。 “在 13 号邮箱中查找一张便条,告诉您哪个邮箱包含您的生日贺卡。”

如果邮箱 13 包含一个写着“13”的便条,那么您的生日贺卡将是很久以后的事了! (这是循环指针引用引起的错误。;-)

【讨论】:

  • 很好的解释,但我发现第一行有问题——我认为你应该使用“地址”这个词而不是“指针”,因为后一个词总是暗示一个变量。 (或者只是删除该行。)
  • 我同意第一行本身有点令人困惑,但它是准确的。所有变量都有一个位置,指针在这方面没有什么不同。
【解决方案4】:

虽然在这里阅读 K&R 可能是最好的选择,但我会尽量让它更清楚一点:

指针本身就是一个变量。但是它不存储一个值,而是只存储一个地址。把它想象成一个索引:就像在你的地址簿中你正在搜索的东西的索引(比如,某个名字的电话号码)它指向信息的存储位置。在您的通讯录中,它可能会显示“查看第 23 页以找到 Joe 的电话号码”。如果是指针,它只是说“查看内存地址 1234 以检索我指向的信息”。由于指针值本身只是一个内存地址,您可以用它进行算术运算——比如添加值(这与访问数组元素相同:如果指针指向数组,则指针后面的地址指向将访问数组中的下一个元素)。 您的示例函数 int doSomething(char *hihi) 将 hihi 指向您在调用它时传递的内存地址。如果您想传递大量数据,这很有用 - 而不是复制数据(这发生在像 void blah(int a) 这样的函数中,其值为 a)您只复制其位置。

我在上面省略了一些细节,但我希望它至少能给你一些基本的理解。我强烈建议阅读 K&R 或有关该主题的类似书籍。

【讨论】:

  • 我同意你所说的,但我认为语言有点混乱。首先你说“它只存储一个地址而不是存储一个值”,然后“他的指针值本身只是一个内存地址”。指针确实具有/存储值 - 这些值是地址。
【解决方案5】:
/* Given a string of characters like this one: */
char *string = "Hello!\n";

/* Memory will contain something like:
0x00100 'H'
0x00101 'e'
0x00102 'l'
0x00103 'l'
0x00104 'o'
0x00105 '!'
0x00106 '\n'
0x00107 '\0'
*/

/* And the program code will say: */
string=0x00100;

/* C doesn't really have arrays */
char c=string[3];
/* is just syntactic sugar for: */

char c=*((char*)((void*)string + 3 * sizeof(char)));
/* ie. 0x00100 + 3 * 1 */
/* ie. 0x00103 */
/* and * dereferences 0x00103, this means char_in(0x00103) */

/* When you pass a pointer you are actually passing the value
   of the memory position */

int a;          /* allocates space for a random four byte value in
                   0x00108 */
scanf("%d",&a); /* &a = 0x00108 scanf now knows that it has to store
                  the value in 0x0108 */

/* Even if you declare: */
int b[23];
/* b will be just a pointer to the beginning of 23 allocated ints.
   ie. 0x0010C */

/* pointer arithmetic is different from normal types in that: */
b++;
/* is actually: */
b+=4; /* b+=1*sizeof(int); */
/* So pointers in C work more like arrays */

【讨论】:

    【解决方案6】:

    Davos,你在上大学吗?你是计算机科学专业的吗?您可能会考虑的一件事是学习汇编语言课程(MIPS、x86)。我是电气工程专业的,我被要求参加那些低水平的课程。我观察到的一件事是,当我开始学习 C++ 时,对汇编语言有清晰的理解确实对我很有帮助。尤其是让我对指针的理解更加清晰。

    指针和取消引用是汇编语言级别的基本概念。如果您是第一次学习指针,我发现在某些方面“C”隐藏了太多,实际上它在汇编语言级别变得更加清晰。然后,您可以获取较低级别的知识,看看“C”语言是如何在其上添加一些语法的。

    只是一个想法。除此之外,正如一些人所提到的,K&R 是一本很棒的书,而且,实现一些抽象数据类型(如链表)可能很有用,特别是如果您绘制显示内存布局的图表,它可以帮助澄清这个想法。

    【讨论】:

      【解决方案7】:

      恕我直言,“获取”指针的最佳方法是进行一些汇编语言编程。一旦您习惯于考虑原始寄存器内容(数据和地址之间除了您如何使用它们之外没有真正的区别)和加载存储指令,C 的指针类型将更有意义(以及您对C 为你做的事情会得到很大的改进)。

      【讨论】:

        【解决方案8】:

        上面给出了许多很好的答案。其他一些见解是指针始终是无符号整数类型。它在内存中指向的对象或变量可以是任何类型。

        在 32 位操作系统中,整数是一个 4 字节的数字,并且必须在范围内

        0

        在 64 位操作系统中,整数是一个 8 字节的数字,并且必须在范围内

        0

        指针值 = 0 被解释为称为 NULL 的特殊标志值,表示该指针未指向可用变量。

        指针用于间接寻址。 CPU 中的一些寄存器充当指针,例如程序计数器(PC)和地址寄存器。

        【讨论】:

          【解决方案9】:

          如果你真的想了解指针, 你需要一个好的大学讲座。

          我见过的最好的是this one

          没有什么可比的。

          还可以查看有关图灵的讲座。做得很好。

          【讨论】:

            【解决方案10】:

            听起来您已经掌握了基础知识。在您进一步阅读一些文献之前,我不会跳到备忘单上。 使用指针时要小心的原因是它们允许您直接访问特定的内存地址,如果您的代码这样做,错误的事情将会中断。数组将指向第一个位置,根据数组类型,您可以通过使用相同数组存储值类型的指针来访问数组中的更多位置。

            我首先要了解变量、左值、右值和赋值,然后是作为变量类型的指针,然后是指针解引用和进一步的指针操作。详细说明这一点需要相当多的时间,并且已经有很多很好的参考资料可供参考。

            你可以找到一个介绍教程here(真的很详细的解释)。 PDF Direct link.

            【讨论】:

              【解决方案11】:

              你读过K&R吗?如果你还没有,我会说这是你应该开始的地方。

              【讨论】:

                【解决方案12】:

                有点不合常规的建议:http://www.youtube.com/watch?v=Rxvv9krECNw

                这是我去年在我大学的 Higher Computing 1 的讲座。前几分钟会有点无用(主题管理员类型的东西),但除此之外,这是一个非常好的解释

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 2011-11-14
                  • 2021-12-24
                  • 1970-01-01
                  • 1970-01-01
                  • 2023-04-05
                  • 1970-01-01
                  • 1970-01-01
                  • 2021-10-17
                  相关资源
                  最近更新 更多