【问题标题】:Pointers in C, I'm so confusedC中的指针,我很困惑
【发布时间】:2020-11-26 20:38:03
【问题描述】:

目前我正在关注 C 指针上的 this pdf。下面的代码是为了演示,但让我难以置信。首先,我对数组指针的理解是,它们在堆内存中使用数组中第一个元素的内存地址进行初始化,因此为什么:ptr = &my_array[0]; /* and */ ptr = my_array; 是一回事。我对这段代码的第一个问题是他创建了一个不指向内存地址的指针。我认为指针初始化通常会涉及 & 并且 pA 的值将是数组第一个元素的内存地址,但是当他将指针打印到屏幕时,整个字符串都会打印出来。为什么?最重要的是,当我在 pA 上放另一个 * 时,它会打印数组的第一个字符。为什么?最重要的是,他在 *pA 和 *pB 上使用了 ++,这对我来说毫无意义,因为输出显示 *pA 是数组中的第一个字符而不是内存地址。

抱歉,这个概念让我难以理解。 PDF 只是不善于解释指针还是我严重误解了某些东西?

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

char strA[80] = "A string to be used for demonstration purposes";
char strB[80];

int main(int argc, char *argv[]){
    char *pA; /* a pointer to type character */
    char *pB; /* another pointer to type character */
    puts(strA); /* show string A */
    pA = strA; /* point pA at string A */
    puts(pA); /* show what pA is pointing to */
    pB = strB; /* point pB at string B */
    putchar('\n'); /* move down one line on the screen */
    printf("\n%c\n", *pA);
    while(*pA != '\0') /* line A (see text) */
    {
        *pB++ = *pA++; /* line B (see text) */
    }
    *pB = '\0'; /* line C (see text) */
    puts(strB); /* show strB on screen */
    return 0;
}

输出:

A string to be used for demonstration purposes
A string to be used for demonstration purposes


A
A string to be used for demonstration purposes

编辑: 感谢大家的帮助:),对于noobie问题感到抱歉。我阅读了 Stephen G. Kochan 的 Programming in C ,其中关于指针的精彩部分很有帮助。

【问题讨论】:

  • 您可能喜欢comp.lng.c faq 的第 6 部分...以及其他部分?
  • @smolpatchy 这个问题太宽泛了。为初学者阅读一本关于 C 的书。
  • 我会尽快回答您的具体问题并提供更多详细信息,但是是的,阅读 Kernighan 和 Ritchie 的“C 编程语言”的前 4 章是个好主意。

标签: arrays c pointers implicit-conversion puts


【解决方案1】:
char strA[80] = "A string to be used for demonstration purposes";

这会在可执行文件的全局部分中产生一个 80 字符长的空间 - 它不是动态分配的,因此不需要释放。如果 strA 用作指针,则它与您期望的一样等价于 &amp;strA[0]。

这 80 个字符由等号右侧的字符串填充 - 包括字符串末尾的终止字符。终止字符非常重要,因为这是您在数组中找到可打印数据的结尾的方式。

puts(strA); /* show string A */

当您puts(strA) 时,该函数会打印从传入的地址到该终止字符的每个字符 - 该函数内部有一个循环。

pA = strA; /* point pA at string A */
puts(pA); /* show what pA is pointing to */

然后pA 填充&amp;strA[0] 的值,然后当puts(pA); 被调用时,该函数接收到它上次接收到的相同地址,因此它执行与上次相同的操作:它打印每个字符从传入的地址到该终止字符。

printf("\n%c\n", *pA);
while(*pA != '\0') /* line A (see text) */

* 字符为您提供指针位置的内容 - 所以*strA 提供与strA[0] 相同的内容 - 字符串的第一个字符。而*pA 与*strA 相同,因为它们都指向同一个地址。

*pB++ = *pA++; /* line B (see text) */

*pA++ 让您感到困惑的原因是因为您不知道是先完成 * 还是 ++。发生的情况是:pA++ 增加指针并返回它的原始值。然后* 获取指针的原始值并给出其内容 - pA 最初指向的字符。

【讨论】:

    【解决方案2】:

    根据 C 标准(6.3.2.1 左值、数组和函数指示符)

    3 除非它是 sizeof 运算符的操作数或一元 & 运算符,或者是用于初始化数组的字符串文字,an 具有“类型数组”类型的表达式被转换为 类型为“pointer to type”的表达式,指向初始 数组对象的元素 并且不是左值。如果数组对象 有注册存储类,行为未定义。

    所以这两个赋值语句

    ptr = &my_array[0]; 
    

    和

    ptr = my_array;
    

    是等价的,因为用作初始化器的数组指示符my_array被隐式转换为指向其第一个元素的指针。

    在 C 中,字符串是包含以零结尾字符 '\0' 结尾的字符序列的字符数组。

    例如,字符串文字“Hello”存储为具有此字符序列的字符数组

    { 'H', 'e', 'l', 'l', 'o', '\0' }
    

    函数puts专门用于输出字符串。

    声明方式如下

    int puts(const char *s);
    

    函数调用

    puts(strA);
    puts(pA)
    

    是等价的,因为在第一次调用中用作参数的数组指示符strA 被隐式转换为指向其第一个元素的指针。

    在这个调用中取消引用指针

     printf("\n%c\n", *pA);
    

    你得到了指针指向的对象。当指针pA指向数组strA的第一个字符时,输出这个字符。

    【讨论】:

      【解决方案3】:

      作为对 Vlad 的回答的跟进,这很好,我只想添加一小段代码,这真的有助于我理解指针。我发现做出预测然后编写代码并进行观察直到我理解为止真的很有帮助。

      [ttucker@zim stackoverflow]$ cat ptr.c 
      #include <stdio.h>
      
      int main() {
          char *foo = "stuff";
          while (*foo != '\0') {
              printf("%lu: %c (%d), %lu\n", &foo, *foo, *foo, foo);
              foo++;
          }
          return 0;
      }
      
      
      [ttucker@zim stackoverflow]$ gcc -o ptr ptr.c
      
      
      [ttucker@zim stackoverflow]$ ./ptr 
      140730183740528: s (115), 94216543457284
      140730183740528: t (116), 94216543457285
      140730183740528: u (117), 94216543457286
      140730183740528: f (102), 94216543457287
      140730183740528: f (102), 94216543457288
      
      # A second execution is included to show that the memory addresses change.
      
      [ttucker@zim stackoverflow]$ ./ptr 
      140723360085232: s (115), 94198997999620
      140723360085232: t (116), 94198997999621
      140723360085232: u (117), 94198997999622
      140723360085232: f (102), 94198997999623
      140723360085232: f (102), 94198997999624
      
      

      【讨论】:

        【解决方案4】:

        首先,我对数组指针的理解是,它们在堆内存中使用数组中第一个元素的内存地址进行初始化,因此为什么:ptr = &amp;my_array[0]; /* and */ ptr = my_array; 是一回事。

        只有当您使用malloc、calloc 或realloc 或在后台调用它们的库函数时才会发挥“堆”内存的作用。 pA 和 pB 与在该范围内声明的任何其他类型的变量占用相同的内存。

        但是当他将指针打印到屏幕上时,整个字符串都会打印出来。

        puts 函数获取字符串中第一个字符的地址并“遍历”字符串,打印每个字符,直到它看到字符串终止符,有点像

        void myputs( const char *str ) // str points to the first character in a 0-terminated string
        {
          while ( *str )         // while we haven't seen the string terminator
            putchar( *str++ );   // print the current character and advance the pointer    
        }
        

        最重要的是,他在 *pA 和 *pB 上使用了 ++,这对我来说毫无意义,因为输出显示 *pA 是数组中的第一个字符而不是内存地址。

        线

        *pB++ = *pA++;
        

        大致相当于写作:

        *pB = *pA;
        pB = pB + 1;
        pA = pA + 1;
        

        后缀++ 的优先级高于一元*,因此*pA++ 被解析为*(pA++) - 您取消了pA++ 的结果。 pA++ 的结果是 pA 的当前值,并且作为 副作用 pA 递增。由于pA 和pB 是指针,加1 会使它们指向序列中的下一个对象,不一定是下一个字节(尽管在这种特殊情况下,下一个对象是下一个字节)。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-04-11
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多