0

请看下面的代码:

char* test ( )
{
    char word[20];
    printf ("Type a word: ");
    scanf ("%s", word);
    return word;
}

void main()
{
    printf("%s",test());
}

当函数返回时,变量word被销毁并打印一些垃圾值。但是当我更换

char word[20];

经过char *word;

它打印正确的值。据我说,指针变量应该像字符数组一样被销毁,输出应该是一些垃圾值。谁能解释一下歧义?

4

4 回答 4

5

未定义的行为就是这样 - 未定义。有时它似乎会起作用,但这只是巧合。在这种情况下,未初始化的指针可能恰好指向有效的可写内存,而该内存没有用于其他任何内容,因此它成功地写入和读取了该值。这显然不是你应该指望的。

于 2012-06-24T15:04:23.320 回答
2

无论哪种方式,您都有未定义的行为,但纯粹从“这里发生了什么”的角度来看,两者之间仍然存在一些差异。

当你使用一个数组时,它保存的数据被分配在堆栈上。当函数返回时,该内存将不再是堆栈的一部分,并且几乎肯定在调用过程中被覆盖printf

当您使用指针时,您的数据将被写入指针碰巧指向的任何随机位置。尽管写入存在未定义的行为,但简单的统计数据表明,如果您(例如)拥有约 40 亿个位置的 32 位地址空间,则命中将在新的几条指令中被覆盖的地址空间的机会相当低。

你显然不应该做任何一个,你得到的结果也并不特别令人惊讶。

于 2012-06-24T15:10:01.260 回答
0

因为char数组是在函数中定义和声明的,所以它是一个局部变量,函数返回后就不再存在了。如果您使用 char 指针并为其分配内存,那么它将保留,您所需要的只是指针(又名数字)。

int main(int argc, char* argv[]) {
    printf("%s", test());
    return 0;
}

char* test(void) {
    char* str = (char*)malloc(20 * sizeof(char));
    scanf("%19s", str);
    return str;
}

请注意我如何使用 %19s 而不是 %s。如果用户输入 20 个或更多字符,您当前的函数很容易导致缓冲区溢出。

于 2012-06-24T15:10:55.453 回答
0

在程序执行期间,它首先会main在进程内存的堆栈段中为函数创建激活记录。在该main激活记录中,它将为该函数(main)的局部变量分配内存,并为内部目的分配更多内存。在您的程序main中没有任何局部变量,因此它不会为main激活记录中的局部变量分配任何内存。

然后在执行调用函数的语句时test,它会为调用函数()再创建一个激活记录,并为局部变量test分配20字节word

一旦控件退出函数test,为该函数创建的激活记录将从该堆栈中弹出。然后它将继续执行printf被调用函数的剩余语句()main。这里 printf 试图打印test已经从堆栈中弹出的函数局部变量中的字符。所以这种行为是未定义的,有时它可能会打印正确的字符串,否则它会打印一些垃圾字符串。

所以在这种情况下,只有动态内存才会出现。借助动态内存,我们可以控制变量的生命周期(或范围)。所以使用如下的动态内存。

char *word = NULL:
word = (char *) malloc(sizeof(char) * 20);

注意:注意对 malloc 返回值的 NULL 检查,并且不要忘记printf在 main 函数之后释放分配的内存。

于 2012-06-24T18:34:08.050 回答