了解指针数组语法

时间:2015-04-27 03:10:27

标签: c arrays pointers

我正在尝试编写一个简单的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)在指针的上下文中是相同的吗?)

有人请帮我澄清一下。感谢

2 个答案:

答案 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等于words0x00000018等于{{ 1}}。

现在words + 10x0000001C相同 - 这意味着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的指针,因此words2char - 它已添加1,因为words2 + 1为1. 0x00000011的内容为sizeof(char)。如果您想要0x00000011字符的地址,则可以'o''o'