【问题标题】:Understanding pointer array syntax了解指针数组语法
【发布时间】:2015-07-05 08:42:32
【问题描述】:

我正在尝试编写一个简单的 c 程序来打印字符串数组中字符串的地址。这是程序:

#include <stdio.h>

int main(void)
{
    char *words[] = {"hello", "cruel", "world"};
    int count = sizeof(words) / sizeof(char*);

    int i;
    for(i = 0; i < count; i++){
        printf("address: %p\n", (words+i));
        printf("address: %p\n", &(words[i])); //<- why does this work?
        printf("address: %p\n", words[i]); //<- not the same as first print?
        printf("---\n");
    }  

    return 0;
}

和输出:

address: 0x7fff043110f0
address: 0x7fff043110f0
address: 0x4006b4
---
address: 0x7fff043110f8
address: 0x7fff043110f8
address: 0x4006ba
---
address: 0x7fff04311100
address: 0x7fff04311100
address: 0x4006c0
---

现在我是这样理解的:words 是一个指向指针的指针,指向这个数组数组的首地址。

那么words[i]是一个指向字符数组的指针,words[0]指向“hello”的首地址,words[1]指向“cruel”的首地址。

现在我正在尝试以不同的方式打印出字符串的地址。

第一种方法是有意义的:(指向words 的指针增加一个指针的大小,然后我打印出来,将其格式化为指针)

在第二个中,我认为应该发生的事情:words[i] 返回一个指向索引字的指针,并在其上使用&amp; 运算符返回地址。但我不清楚%p 是如何用于第一个打印语句中的指针类型和第二个语句中的地址类型的。

如果第三个打印语句给出了第二个打印语句的输出,对我来说有意义的是。 ((words+i)words[i]在指针上下文中是一样的吗?)

请有人帮我解决这个问题。谢谢

【问题讨论】:

  • “但我不清楚 %p 如何用于第一个打印语句中的指针类型和第二个语句中的地址类型。” - 您认为“地址类型”是什么,特别是,您认为它与指针类型有何不同?
  • 首先将sizeof(char) 修改为sizeof(char*) - 你的第一个问题真的应该是“为什么我得到的地址超过三组?”
  • @user2357112 指针是一个具有体系结构特定格式的变量,用于保存有关地址的信息?显然ptr&amp;ptr 在内存中不能相同。但我希望有人能写一个简单的解释而不是问更多问题
  • @jhabbott 是的,对不起。我之所以忽略这一点是因为我创建了一个“虚拟程序”来说明我的观点,但我心不在焉,完全忽略了我打印出太多东西的事实。它仍然没有解决我的问题
  • words[i] 等价于*(words+i),而不是(words+i)

标签: c arrays pointers


【解决方案1】:

当你编译你的 C 代码时,你会得到一个二进制文件。在不涉及太多细节的情况下(即我正在简化),这由不同部分中的一些代码和一些数据组成,.code 部分和.data 部分。想想.data 部分的内容会有所帮助。

char *words[] = {"hello", "cruel", "world"};

这会在您的 .data 部分中创建一些数据,您的编译器可能会以不同的方式执行此操作,但我将 char 使用 1 字节,将指针使用 4 字节:

0x00000000 'h' 'e' 'l' 'l' 'o' 0 ? ? <- Data for the "hello" string
0x00000008 'c' 'r' 'u' 'e' 'l' 0 ? ? <- etc.
0x00000010 'w' 'o' 'r' 'l' 'd' 0 ? ? <- etc.

左边的数字是数据段中的十六进制地址。我已将每个字符串对齐在 8 字节边界上,您的编译器可能会以不同的方式执行此操作,这正是我选择的方式。请注意,字符串以 nil 字节终止,问号表示在下一位数据开始之前的两个附加(未初始化)字节(由我的对齐决定引起)。

如果你的数组是一个全局的,你也会得到这个数组:

0x00000018 0x00000000 <- 4-byte pointer to "hello" string
0x0000001C 0x00000008 <- 4-byte pointer to "cruel" string
0x00000020 0x00000010 <- 4-byte pointer to "world" string

在您的代码中,但它不是全局的,它是在您输入函数时临时在堆栈上创建的。这就是为什么您看到的地址与字符串数据的地址如此不同的原因。我会假装它是一个全球性的解释,并使用上面的地址。

不用担心这里的第一个指针的值实际上是NULL。这些值在您运行的程序中会发生变化,具体取决于您的程序与哪些其他.data 链接以及您的程序加载到内存中的位置。关键是,我在这里解释的值是相对于您的.data 部分的。

现在words 正如你所说,是一个指向指针的指针。 words 的值是 0x00000018 - 它指向数组的开头(见上文)。

int count = sizeof(words) / sizeof(char*);

sizeof(words) 是 12(看上面的数据),sizeof(char*) 是 4,所以 count 是 3。

现在你需要了解 C 中的指针运算:

编译器知道所指对象的大小,并且加法以该大小的倍数起作用。考虑到words + i——编译器知道words指向一个指针,并且指针的大小是4,因此因为words等于0x00000018words + 1等于0x0000001C

现在words[i]*(words + i) 相同——这意味着(words + i) 的内容。当i = 1 (words + i) 将是0x0000001C 时,查看数据部分,该地址的内容(编译器知道它是一个4 字节指针)是0x00000008。这是一个指向"cruel" 字符串的指针。

您的另一个实验是使用&amp;words[i] - 这与words + i 相同,它是数组中元素的地址,而不是它指向的位置。

由于words 数组中的指针指向char,那么words[2][1] 的计算方式如下:

// First get words[2]
char *words2 = *(words + 2) <- Contents of 0x00000020 as char*
// words2 now holds 0x00000010 - the address of the "world" string
char result = *(words2 + 1)
// result now holds 'o'

请注意,words 是指向指针的指针,words2 是指向 char 的指针,所以 words2 + 10x00000011 - 它加了 1,因为 sizeof(char) 是 1。并且内容0x00000011'o'。如果你想要'o' 字符的地址,你可以使用&amp;words[2][1],你会得到0x00000011

【讨论】:

    【解决方案2】:

    words[i] 正在做它应该做的事情——返回数组的第 i 个元素——它指向第 i 个字符串实际所在的位置。

    words[0]0x4006b4,因为这是 hello 中的 h 所在的位置。请注意,words[0]words[1]words[2] 都相差 6 个。那是因为您的每个单词都是 6 个字符长(5 个“真实”字符和结尾的空字符)。

    (words + i) 告诉您数组的第 i 个元素在内存中的位置。请注意,它们相隔 8 个。那是因为指针的长度在您的系统上是 8 个字节。 words 是指向数组中第一个元素的指针。请记住,当您进行指针运算时,它会在递增时隐式使用类型的大小。所以words + i 并不意味着“基地址加 i 字节”。它的意思是“基地址加上i*sizeof(type)字节”。这里的类型是一个指针,它的大小是8字节。

    &amp;(words[i])(words + i) 相同,因为第一个表达式是“给我数组中第 i 个元素的地址”。 (words + i) 正在做同样的事情——因为这就是 C/C++ 语言规范所说的数组的布局方式。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-10-09
      • 2021-05-26
      • 1970-01-01
      • 2017-11-12
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多