c - 了解指针数组语法

标签 c arrays pointers

我正在尝试编写一个简单的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/

相关文章:

c - 为什么取消引用 C 中的引用?在 * 旁边使用 &

c++ - 通过套接字发送 BYTE*

c - putchar() 函数 : ambiguous output

c - 在这种情况下,通过指针或通过值传递结构有什么区别?

android - Eclipse 索引器没有找到使用 android ndk 的函数

c - 使用 char 到 int 数据类型

c - 除了 pthread_create 在 linux 上创建 linux 线程的方法

c - 区分嵌入式 NUL 和 NUL 终止符

javascript - 迭代嵌套关联数组

c# - DefaultIfEmpty 不起作用