【问题标题】:Using pointers in C to print string使用 C 中的指针打印字符串
【发布时间】:2015-12-26 07:22:29
【问题描述】:

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

#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 的调用使用了一个无效的指针。谁能花时间解释一下我在哪里思考问题?

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

【问题讨论】:

  • free 只能用于malloc(和朋友)分配的空间。 ptr = aString(); 表示将 ptr 指向与 malloc 块不同的位置。这并不意味着复制字符。
  • str[] 是堆栈上的局部变量。当它“超出范围”时,即当函数退出时,对变量的任何访问都是未定义的行为,并可能导致段错误事件。我的建议:在 main() 中声明 str[] 数组并将指向 str[] 的指针传递给 aString() 函数

标签: c pointers memory allocation


【解决方案1】:

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

【讨论】:

    【解决方案2】:

    函数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);
    

    【讨论】:

    • 谢谢!我不知道为什么我没有想到。
    • C 没有堆栈概念,而只有storage duration
    • @Olaf 存储时间如果你想学究气
    【解决方案3】:

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

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

    当函数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);
    }
    

    【讨论】:

    • 您对strncpy 的使用不正确。无论如何都应该避免使用此功能,因为正确使用它很复杂。 strcpy 会简单得多。
    • @M.M 我已经因为没有使用strncpy 太多次而被烧毁了,因为坏数据不会出现空终止。我更喜欢它而不是 strcpy,即使它更难使用。
    • 同样的问题仍然存在,strncpy 将覆盖所有你刚刚 memset 如果源字符串是 20 或更多的东西,你最终会得到一个非空终止的字符串。
    • 我认为这说明了我的观点......即使你最终得到了正确的代码,后来的人也很容易在不经意间把它搞砸,没有意识到为什么代码完全正确的微妙之处照原样。 (或者有人阅读这个答案,然后根据它编写自己的代码)。我个人会使用snprintf,它的出错空间要小得多。顺便说一句,你需要增加你的 memset 数量!
    • 如果您已更改为 snprintf,则不再需要 memset。 (有些人会说calloc 比 malloc 和 memset 好)。
    【解决方案4】:

    str[20];存在于栈中,从aString返回后引用无效。

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

    【讨论】:

      【解决方案5】:

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

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

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

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

      【讨论】:

      • 是的,但这使得“Hello World”的使用无关紧要,它应该被替换为字符串。然后我可以在 aString(char *astring) 中调用 strcpy(str, string),然后在 main 中使用 char *ptr = aString("Hello World!")。
      • 这真的取决于你想要完成什么。看起来(从接受的答案)你只是想弄清楚如何创建一个可以在它声明的范围之外使用的字符串。很高兴您找到了答案!
      【解决方案6】:

      您对两个概念的思考不正确。

      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.
        

      首先要做的是补救复制情况:

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

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

      【讨论】:

        【解决方案7】:

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

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

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

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

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

        返回字符串; str 被返回,它是字符数组的第一个元素的地址。这只是一个堆栈内存的地址,它保存了一个一旦退出函数就不再存在的局部变量。所以现在回到主要的

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

        【讨论】:

          猜你喜欢
          • 2023-03-15
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-06-08
          • 2013-03-09
          • 1970-01-01
          • 1970-01-01
          • 2021-11-07
          相关资源
          最近更新 更多