我正在尝试编写一个简单的c程序来打印出字符串数组中字符串的地址。这是程序:
<meta property="og:image" content="http://URL-TO-YOUR-IMAGE" />
和输出:
#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]
指向“hello”的第一个地址,words[0]
指向“残忍”的第一个地址。
现在我试图以不同的方式打印出字符串的地址。
第一种方式是有道理的:(指向words[1]
的指针增加一个指针的大小,然后我将其打印出来,将其格式化为指针)
在第二个中,我认为应该发生的事情:words
返回指向索引字的指针,并使用words[i]
运算符返回地址。但我不清楚&
如何用于第一个print语句中的指针类型和第二个语句中的地址类型。
对我来说有意义的是,如果第三个print语句给出了第二个print语句的输出。 (%p
和(words+i)
在指针的上下文中是相同的吗?)
有人请帮我澄清一下。感谢
答案 0 :(得分:0)
words[i]
正在做它应该做的事情 - 返回数组的第i个元素 - 它指向第i个字符串实际存在的位置。
words[0]
为0x4006b4
,因为h
中的hello
生活在words[0]
。请注意words[1]
,words[2]
和(words + i)
相隔6个。这是因为你的每个单词都是6个字符长(5个“真实”字符和尾随空格符号。)
words
告诉你内存中的哪个阵列的第i个元素。注意那些是8分开。那是因为指针的长度是系统上的8个字节。 words + i
是一个指向数组中第一个元素的指针。请记住,当您执行指针运算时,它会在递增时隐式使用类型的大小。所以i*sizeof(type)
并不意味着“基地址加上i字节”。它表示“基地址加&(words[i])
字节”。这里的类型是一个指针,它的大小是8个字节。
(words + i)
与(words + i)
相同,因为第一个表达式是“给我数组生命的第i个元素的地址”。 shift
正在做的事情是一样的 - 因为这就是C / C ++语言规范说数组的布局。
答案 1 :(得分:0)
编译C代码时,会得到一个二进制文件。没有太多细节(即我正在简化),这包括一些代码和不同部分的一些数据,.code
部分和.data
部分。它有助于考虑.data
部分中的内容。
char *words[] = {"hello", "cruel", "world"};
这会在.data
部分创建一些数据,您的编译器可能会以不同的方式执行此操作,但我使用的是{-1}}的单字节和指针的4字节:
char
左边的数字是数据部分中的十六进制地址。我已经将每个字符串对齐在一个8字节的边界上,你的编译器可能会采用不同的方式,这就是我选择这样做的方式。请注意,字符串以nil字节结尾,问号表示下一位数据开始之前的两个额外(未初始化)字节(由我的对齐决定引起)。
如果您的数组是全局数组,那么您也可以获得数组:
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.
在您的代码中,它不是全局的,它会在您输入函数时临时在堆栈上创建。这就是为什么你看到的地址与字符串数据的地址有如此大的不同。我会假装这个解释是全球性的,并使用上面的地址。
不要担心这里第一个指针的值实际上是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*);
为12(查看上面的数据),sizeof(words)
为4,因此计数为3。
现在,您需要了解C:中的指针运算:
编译器知道指向的东西的大小,并且加法以该大小的倍数工作。考虑sizeof(char*)
- 编译器知道单词指向指针,指针的大小为4,因此words + i
等于words
,0x00000018
等于{{ 1}}。
现在words + 1
与0x0000001C
相同 - 这意味着words[i]
的内容。当*(words + i)
(words + i)
为i = 1
并查看数据部分时,该地址的内容(编译器知道的是4字节指针)为(words + i)
。这是指向0x0000001C
字符串的指针。
您的其他实验是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
- 它已添加1,因为words2 + 1
为1. 0x00000011
的内容为sizeof(char)
。如果您想要0x00000011
字符的地址,则可以'o'
,'o'
。