使用C中的指针来打印字符串

时间:2015-09-28 23:30:31

标签: c pointers memory allocation

我目前正在学习如何在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的调用使用了无效指针。任何人都可以花时间解释我在想错的方式吗?

编辑:感谢您的所有答案,我正在阅读所有答案。

7 个答案:

答案 0 :(得分:3)

函数str[20]中声明的数组aString()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);

答案 1 :(得分:1)

有一个很好的解释堆栈和堆是In this link

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

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

如果您希望在函数返回后能够使用该数组,则需要将内存放在堆上。函数返回后没有清理。

或者传递一个地方将数组存储到函数中。

我在下面列出了一个堆分配示例:

#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);
}

答案 2 :(得分:1)

STR [20];存在于堆栈中,从aString返回后引用它是无效的。

0

答案 3 :(得分:1)

你错误地思考了两个概念。

  1. 返回函数本地的对象是个坏主意。

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

    因为从str返回后释放aString的内存,所以函数返回的指针可能不再指向有效内存。

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

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

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

    /*
     * 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.
    
  3. 首先要做的是解决复制问题:

    // 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;
    

    但是,您仍然返回函数本地对象的地址,这意味着strncpy将尝试复制astring返回的不存在的字符串。这是解决问题的第二次尝试:

    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之前未返回空指针。大多数示例代码为了简洁省略了它。有些系统总是返回一个非空指针,但无论如何最好是安全的。始终检查您的退货价值。 ; - )

答案 4 :(得分:0)

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

答案 5 :(得分:0)

老实说,我不确定你的代码是如何在没有警告的情况下编译的,但是当我编写代码并尝试编译时,我会得到一个返回本地地址的警告。使用-Wall标志检查所有可能的警告。

基本上你所做的是创建一个char数组,你的str[20],并尝试返回并在其声明的范围之外使用它。

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

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

答案 6 :(得分:0)

[警告!太多解释]以下是您的代码中逐行发生的事情:

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

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

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

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

return str;返回str,它是字符数组的第一个元素的地址。这只是堆栈内存的地址,它包含一个局部变量,该变量在函数外不再存在。所以现在回到主要的

ptr = aString();现在意味着ptr指向没有任何内容的地址

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

这会更有意义,因为它会将第一个字符H放在ptr中,因为它是返回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);
}