我正在尝试编写一个简单的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]
指向“残酷”的首地址。
现在我尝试以不同的方式打印出字符串的地址。
第一种方法是有意义的:(指向words
的指针按一个指针的大小递增,然后我将其打印出来,将其格式化为指针)
在第二个中,我认为应该发生的情况:words[i]
返回指向索引单词的指针,并在其上使用 &
运算符返回地址。但我不清楚 %p
如何用于第一个打印语句中的指针类型和第二个语句中的地址类型。
对我来说有意义的是,如果第三个打印语句给出了第二个打印语句的输出。 ( (words+i)
和 words[i]
在指针上下文中是相同的?)
请有人帮我解决这个问题。谢谢
最佳答案
编译 C 代码时,您会得到一个二进制文件。无需讨论太多细节(即我正在简化),它由不同部分中的一些代码和一些数据组成,即 .code
部分和 .data
部分。思考 .data
部分中的内容会有所帮助。
char *words[] = {"hello", "cruel", "world"};
这会在您的 .data
部分中创建一些数据,您的编译器可能会以不同的方式执行此操作,但我使用 1 字节表示 char
,使用 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
等于0x00000018
, words + 1
等于 0x0000001C
。
现在 words[i]
与 *(words + i)
相同 - 这意味着 (words + i)
的内容。当i = 1
时(words + i)
将是0x0000001C
,并且查看数据部分,该地址的内容(编译器知道是一个 4 字节指针)是 0x00000008
。这是一个指向“cruel”
字符串的指针。
您的另一个实验是使用 &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 + 1
code> 是 0x00000011
- 它添加了 1,因为 sizeof(char)
是 1。0x00000011
的内容是 'o '
。如果您想要该 'o'
字符的地址,您可以执行 &words[2][1]
并得到 0x00000011
。
关于c - 了解指针数组语法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29886538/