函数在 C 语言中返回字符串和 frintf() 行为

Function return a string and frintf() behavior in C language

提问人:Hai Pham 提问时间:9/12/2023 最后编辑:Hai Pham 更新时间:9/13/2023 访问量:75

问:

我正在学习如何在 C 中返回字符串。它有 2 种方式:使用静态字符数组或使用 malloc。我对源代码进行了一些实验,例如:

#include <stdio.h>
#include <stdlib.h>

char *repeat(char r)
{
    int x;
    // char *string = malloc(32 * sizeof *string);

    static char string[32];
    for(x=0;x<32;x++)
        string[x] = r;

    return string;
}

int main()
{
    char *a = repeat('a');
    printf("%s\n",a);
    printf("%s\n",repeat('a'));

    return(0);
}

我知道此代码将具有未定义的行为,因为字符串末尾没有“\0”字符。如果我使用静态变量方式,则在调用的 2 次 printf() 函数中输出相同:

aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa☺
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa☺

但是当我使用 malloc 方式时,结果是:

aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaasers\man∩╒║2ü8
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

根据我的理解,f() 函数会返回分配给字符串的地址,并且由于字符串不是以 '/0' 结尾的,所以 printf() 函数会打印一些垃圾字符,就像 in 语句一样。但是在声明中,没有打印垃圾字符,我无法确定导致这种行为的原因。printf("%s\n",a)printf("%s\n",repeat('a'))

有人可以帮我解释一下吗?

编辑:我只是好奇 f() 函数和 printf() 函数返回的指针值是如何工作的,从而导致两个打印结果的差异。我多次尝试在 malloc() 情况下运行程序,并且始终使用语句的结果始终没有任何垃圾字符。 printf("%s\n",repeat('a'))

c 返回值 函数调用

评论

2赞 i486 9/12/2023
您需要在字符串末尾使用零字节。在行前添加以下内容:return string;string[sizeof string - 1] = 0;
1赞 Fe2O3 9/12/2023
它被称为 UB 是有原因的......如果您实际上在两个调用之间调用,您将获得不同的(仍然是 UB)动态结果(可能与静态版本相同)...UB还是不是UB,这就是问题......free()repeat()
1赞 Fe2O3 9/12/2023
在所有示例中,最终找到并停止输出未初始化的字节。请注意,您两次调用时没有调用任何 ...在这种情况下,有两个不相关的动态内存区域(其字节中具有不同的未初始化值)被传递给 。在静态版本中,两次访问了相同的内存块......printf()'\0'malloc()free()printf()
0赞 BoP 9/12/2023
UB 的一个可能结果是,它将继续输出,直到它遇到内存中的某个随机零字节。如果没有,则崩溃。printf
1赞 DevSolar 9/12/2023
您可能想看看 The Definitive C Book Guide and List。大多数互联网课程都是...比方说,质量有问题。

答:

2赞 Sourav Ghosh 9/12/2023 #1

一旦代码调用了未定义的行为,任何(次数)执行的特定结果都不能以任何方式证明是合理的。从语言一致性的角度来看,它只是未定义。

溶液:不要编写违反语言定律的代码并调用未定义的行为。

评论

0赞 Hai Pham 9/12/2023
谢谢你的回答,但我不试图找到解决方案。我只是好奇 f() 函数和 printf() 函数返回的指针值是如何工作的,从而导致两个打印结果的差异。
2赞 Bodo 9/12/2023 #2

您的代码包含

    // char *string = malloc(32 * sizeof *string);

    static char string[32];

使用静态变量时,函数的返回值将始终是此静态数组变量所在的内存中的相同地址。数组最后一个元素之后的内存内容可能属于不同的静态或全局变量。因此,如果您不修改位于那里的任何内容,则很可能在每次调用函数时都获得相同的内存内容。

您的使用方式将在每次调用函数时分配新的内存,而您的代码不会占用内存,因此您每次都会在不同的地址获得内存。在这种情况下,您可能会获得不同的内存内容。mallocfree

可以将版本更改为仅分配一次内存。在这种情况下,您可能会在每次调用函数时看到相同的结果。malloc

例:

    static char *string = NULL;
    if(!string) string = malloc(32 * sizeof *string);

是否看到垃圾字符取决于在数组末尾之后恰好找到 () 字节的位置。NUL'\0'

如果使用且不使用任何内存分配内存,则分配的阵列大小之后的内存内容可能包含所有 0,因为在打开系统后,未使用的内存可能会初始化为 0。如果内存,则可能会看到内存分配函数之前存储在预先分配的内存中的部分数据或内存分配函数使用的内部数据。但是,您也可以看到以前运行过的程序留下的垃圾数据。mallocfreefree

所有这些都取决于实现,您可能会在重复调用程序时看到不同的行为。这就是为什么我写了“可能”、“可能”、“可能“可能”。即使它看起来工作正常,这也是未定义的行为。

评论

0赞 Hai Pham 9/12/2023
哦,谢谢你的绝对精彩的回答。但是我多次尝试在 malloc() 情况下运行程序,并且始终使用 printf(“%s\n”,repeat('a')) 语句的结果始终没有任何垃圾字符。你能解释一下这个案例吗?
1赞 Jabberwocky 9/12/2023
@HaiPham“未定义的行为”包括“显然工作正常”。