指针指向位置

问题描述 投票:0回答:4

作为我们在编程语言学院培训的一部分,我们也学习了C.在测试期间,我们遇到了程序输出的问题:

#include <stdio.h>
#include <string.h>

int main(){
    char str[] = "hmmmm..";
    const char * const ptr1[] = {"to be","or not to be","that is the question"};
    char *ptr2 = "that is the qusetion";

    (&ptr2)[3] = str;

    strcpy(str,"(Hamlet)");
    for (int i = 0; i < sizeof(ptr1)/sizeof(*ptr1); ++i){
        printf("%s ", ptr1[i]);
    }
    printf("\n");
    return 0;
}

后来,在检查了答案之后,很明显细胞(&ptr2)[3]与&ptr1 [2]中的记忆细胞相同,所以程序的输出是:to be or not to be (Hamlet)

我的问题是,是否有可能只通过笔记本中的书面代码,而不检查任何编译器,知道某个指针(或一般所有变量)跟随或先于内存中的其他变量?

注意,我不是指数组变量,因此数组中的所有元素必须按顺序排列。

c arrays pointers memory-management
4个回答
8
投票

在这个声明中:

(&ptr2)[3] = str;

ptr2被定义为char *ptr2内部的main。根据此定义,编译器负责为ptr2提供存储。允许编译器使用它想要的任何存储 - 它可能在ptr1之前,它可能在ptr1之后,它可能是接近的,它可能很远。

然后&ptr2ptr2的地址。这是允许的,但我们不知道该地址与ptr1或其他任何地方的关系,因为允许编译器使用它想要的任何存储。

由于ptr2char *&ptr2指向char *,也被称为char **

然后(&ptr2)[3]试图引用位于char *&ptr2数组的元素3。但是在C的计算模型中没有数组。那里只有一个char *。当没有数组的元素3时,当您尝试引用数组的3的元素时,行为不是由C标准定义的。

因此,这段代码就是一个糟糕的例子。看来测试作者误解了C,这段代码没有说明预期的内容。


2
投票
char *ptr2 = some initializer;

(&ptr2)[3] = str;

当您评估&ptr2时,您将获得存储指向该初始化程序的指针的内存地址。

当您执行(&ptr2)[3]=something时,您尝试在ptr2(字符串的地址)的位置进一步写入3*sizeof(void*)位置。这是无效的,几乎可以肯定它完成了分段错误。


2
投票

不,这是不可能的,也不能做出这样的假设。

通过在变量空间外写入,此代码调用未定义的行为,它基本上是“非法的”,并且当您运行它时会发生任何事情。 C语言规范没有说明可以利用某种特定顺序在堆栈上分配的变量,但它确实说访问随机存储器是未定义的行为。

基本上这段代码非常糟糕,不应该被使用,在教学环境中更是如此。这让我很难过,人们如何误解C并仍然教给别人。 :/


-1
投票

程序通常使用以下结构加载到内存中:堆栈,Mmap文件,堆,BSS(未初始化的静态变量),数据段(初始化静态变量)和文本(编译代码)

你可以在这里了解更多:https://manybutfinite.com/post/anatomy-of-a-program-in-memory/

根据您声明变量的方式,它将转到之前说过的地方之一。

编译器将根据编译时的意愿安排BSS和数据段变量,因此通常没有机会。堆旧变量(操作系统将获得更适合分配空间的内存块)

在堆栈(这是一个LIFO结构)中,变量被放在一起,所以如果你有:

int a = 5;
int b = 10;

你可以说a和b将一个接一个地放置。所以,在这种情况下,你可以告诉。

还有另一个例外,那就是如果变量是一个结构或一个数组,它们总是像我之前所说的那样放置,每个都跟在最后一个之后。

在你的代码中,ptr1是一个字符数组的数组,因此它将遵循我所说的异常。

实际上,请执行以下练习:

#include <stdio.h>
#include <string.h>

int main(){
    const char * const ptr1[] = {"to be","or not to be","that is the question"};
    for (int i = 0; i < 3; i++) {
            for (int j = 0; j < strlen(ptr1[i]); j++) 
            printf("%p -> %c\n", &ptr1[i][j], ptr1[i][j]);
        printf("\n");
    }

}

你会看到内存地址及其内容!

祝你今天愉快。

© www.soinside.com 2019 - 2024. All rights reserved.