在 C 中使用指针打印字符串

Using pointers in C to print string

我目前正在学习如何在 C 中使用指针和内存分配,并编写一个简单的代码片段来尝试使用指针打印字符串。我现在拥有的是:

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

char *aString(void)
{
    char str[20];
    strcpy(str, "Hello World!");
    return str;
}

int main(int argc, char **argv)
{
    char *ptr = malloc(20);
    ptr = aString();
    printf("%s\n", ptr);
    free(ptr);
    exit(0);
}

它只打印一个空行并告诉我对 free 的调用使用了一个无效指针。谁能花时间解释一下我在哪里思考问题的方式不对?

编辑:谢谢大家的回答,我正在通读。

在 String 上,str 在堆栈上分配,并在退出时由 stack unwinding 自动释放。

函数aString()中声明的数组str[20]allocated in the program stack。当程序退出 aString() 时,这个数组被弹出内存,不再可访问,从而使 ptr 指向一个无效指针。

另一方面,malloc()函数从堆中分配内存,可以在aString():

中使用
char *aString() {
    char *str = malloc(20);
    strcpy(str, "Hello World!");
    return str;
}

然后在你的 main():

char *ptr = aString();
printf("%s\n", ptr);
free(ptr);

关于什么是栈和堆有很好的解释In this link

您的代码正在 aString 的本地堆栈上创建数组 str[20]

当函数 aString 调用 return 时,它使用的堆栈被清除。 这包括您随后尝试在主函数中使用的数组 str[20]

如果您希望在函数返回后能够使用该数组,您需要将内存放在堆上。在函数 returns.

之后没有清理

或者传递一个存储数组的地方给函数。

我在下面包含了一个堆分配示例:

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

#define MAX_SIZE 20
char *aString(void)
{
    /**
     * create an array of 20 characters on the heap.
     * This memory is not guaranteed to be all 0.
     * You may want to memset the memory to 0, or use calloc.
     */
    char* str = malloc( MAX_SIZE );

    /* copy a max of 20 characters into the array you just created */
    /* snprintf guarantees a null terminator, which is important. */
    snprintf( str, MAX_SIZE, "Hello World!" );

    return str;
}

int main(int argc, char **argv)
{
    char *ptr;
    ptr = aString();
    printf("%s\n", ptr);

    free(ptr); /* clear the heap memory, this is not needed for stack */
    exit(0);
}

海峡[20];存在于堆栈中,并且在您从 aString return 之后引用它是无效的。

char *aString(void)
{
    char *str;
    str = malloc(20);
    strcpy(str, "Hello World!");
    return str;
}

int main(int argc, char **argv)
{
    char *ptr = aString();
    printf("%s\n", ptr);
    free(ptr);
    exit(0);
}

老实说,我不确定您的代码是如何在没有警告的情况下编译的,但是当我编写代码并尝试编译时,我收到了 return-local-address 警告。使用 -Wall 标志检查所有可能的警告。

基本上你所做的是创建一个字符数组,你的str[20],并试图return并在它声明的范围之外使用它。

相反,您可以让 aString()char *stringstrcpy 类型的参数直接带入 string

void aString(char *string) {
    strcpy(string, "Hello World!");
}

你对两个概念的思考是错误的。

  1. 返回函数的局部对象不是一个好主意。

    char *aString(void)
    {
        char str[20];
        strcpy(str, "Hello World!");
        return str;
    }
    

    因为 str 的内存在从 aString 执行 return 后被释放,函数 return 的指针可能不再指向有效内存。

  2. 从指针复制不会自动复制它持有的任何值。

    char *ptr = malloc(20);
    ptr = aString();
    

    忽略 aString return 指向不再存在的对象的指针的问题,您实际上是在这样做:

    /*
     * NOT valid C syntax
     */
    ptr = AllocateMemory(ByteCount=20)
    // ptr now points to address 0x10 where at least 20 bytes are available.
    
    ptr = aString()
    // ptr now points to address 0x1010. The 20 bytes allocated above can
    // no longer be freed using ptr.
    

首先要做的是纠正复制情况:

// Copy a maximum of 20 bytes --
// the number of bytes allocated for the object that "ptr" points to.
strncpy(ptr, aString(), 20);
// If there were 20 bytes copied, and the last one was not the null character,
// "ptr" is not null-terminated. As a result, the string is forcibly truncated here.
// While ordinarily bad design, this is not meant to be robust.
ptr[19] = 0;

但是,您仍在 return 函数局部对象的地址,这意味着 strncpy 将尝试复制 return 由 [=19] 编辑的不存在的字符串=].这是解决问题的第二次尝试:

char *aString (char *s, size_t n) {
    strncpy(s, "Hello World!", n);
    s[n - 1] = 0;
    return s;
}

...

char *ptr = malloc(20);
aString(ptr, 20);
printf("%s\n", ptr);

此外,您确实应该添加检查以确保 malloc 在尝试使用 ptr 之前根本没有 return 空指针。大多数示例代码为简洁起见省略了它。有些系统总是return一个非空指针,但无论如何还是安全点好。始终检查您的 return 值。 ;-)

[警告!太多的解释]这是你的代码逐行发生的事情:

char *ptr = malloc(20);分配了 20 字节大小的内存,ptr 指向它。

ptr = aString();你说 ptr 不再指向创建的 20 字节内存,而是指向这个叫做 'aString()' 的东西 [这使你的第一行无用]。所以现在让我们看看 aString() 必须带来什么,因为 ptr 即将指向它。

字符海峡[20];创建了一个大小为 20 的字符数组,str 是指向它的指针(最好记住数组的名称只是指向第一个元素的指针)。

strcpy(str, "Hello World!"); str指向大约12个字符的数组。

return海峡; str 是 returned,它是字符数组第一个元素的地址。这只是一个堆栈内存的地址,其中包含一个局部变量,该变量一旦退出函数就不再存在。所以现在回到主

ptr = aString();现在意味着 ptr 指向什么都没有的地址

*ptr = *aString(); // put the content pointed by aString() to ptr 20byte memory.

这会更有意义,因为它将把第一个字符 H 放在 ptr 中,因为那是 return str;带来。但是如果你在 aString 函数中分配内存,那么你就有了你想要的。

char *aString(void)
{
    char *str = malloc(20);
    strcpy(str, "Hello World!");
    return str;
}
int main(int argc, char **argv)
{
    char *ptr;
    ptr = aString();
    printf("%s\n", ptr);
    free(ptr);
    exit(0);
}