C中的指针,我很困惑

Pointers in C, I'm so confused

目前我正在关注 this 关于 C 指针的 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 != '[=10=]') /* line A (see text) */
    {
        *pB++ = *pA++; /* line B (see text) */
    }
    *pB = '[=10=]'; /* 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

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

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

3 Except when it is the operand of the sizeof operator or the unary & operator, or is a string literal used to initialize an array, an expression that has type ‘‘array of type’’ is converted to an expression with type ‘‘pointer to type’’ that points to the initial element of the array object and is not an lvalue. If the array object has register storage class, the behavior is undefined.

所以这两个赋值语句

ptr = &my_array[0]; 

ptr = my_array;

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

在 C 中,字符串是包含以零终止字符终止的字符序列的字符数组 '[=17=]'

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

{ 'H', 'e', 'l', 'l', 'o', '[=12=]' }

函数puts专门用来输出字符串

声明方式如下

int puts(const char *s);

函数调用次数

puts(strA);
puts(pA)

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

像在这个调用中一样取消引用指针

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

你得到了指针指向的对象。由于指针 pA 指向数组的第一个字符 strA 则输出此字符。

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

这使得可执行文件的全局部分中有 80 个字符长 space - 它不是动态分配的,因此不需要重新分配。如果 strA 用作指针,则它等同于您期望的 &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 填充 &strA[0] 的值,然后当 puts(pA); 被调用时,函数接收到与上次接收到的地址相同的地址,所以它做同样的事情和上次一样:它打印从传入的地址到终止字符的每个字符。

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

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

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

*pA++让你困惑的原因是你不知道*还是++先完成。发生的事情是:pA++ 递增指针并 returns 它原来的值。然后 * 获取指针的原始值并给出其内容 - pA 最初指向的字符。

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

[ttucker@zim Whosebug]$ cat ptr.c 
#include <stdio.h>

int main() {
    char *foo = "stuff";
    while (*foo != '[=10=]') {
        printf("%lu: %c (%d), %lu\n", &foo, *foo, *foo, foo);
        foo++;
    }
    return 0;
}


[ttucker@zim Whosebug]$ gcc -o ptr ptr.c


[ttucker@zim Whosebug]$ ./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 Whosebug]$ ./ptr 
140723360085232: s (115), 94198997999620
140723360085232: t (116), 94198997999621
140723360085232: u (117), 94198997999622
140723360085232: f (102), 94198997999623
140723360085232: f (102), 94198997999624

Firstly, my understanding of pointers to arrays is that they are initialized in heap memory with the memory address of the first element in the array hence why: ptr = &my_array[0]; /* and */ ptr = my_array; are the same thing.

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

but when he prints the pointer to the screen THE WHOLE STRING PRINTS OUT.

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    
}

On top of that, he uses ++ on *pA and *pB which makes no sense to me because the output shows that *pA is the first character in the array not a memory address.

*pB++ = *pA++;

大致相当于写作:

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

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