【问题标题】:Best way to modify a string within a function?在函数中修改字符串的最佳方法?
【发布时间】:2022-01-15 15:47:52
【问题描述】:

所以我在下面有一个示例程序(更大程序的一部分),我需要将一个指向字符串的指针(char 的双指针)传递给函数,并在函数内修改字符串。实现这一目标的最佳方法是什么?

#include <string.h>
#include <stdio.h>
int incr(char **ptr)
{
   char ar[104];
   scanf("%s\n",ar);
   *ptr = ar;
   // this prints the string correctly
   printf("%s\n",*ptr);
   return 0;
}

int main(void)
{
   char *d;
   // pass the string (char array) to function
   // expecting the input from scanf to be stored
   // in this pointer (modified by the function)
   incr(&d);
   printf("%s\n",d);
   return 0;
}

valgrind 的输出:

$ gcc test.c -o terst
$ valgrind --tool=memcheck --leak-check=yes --show-reachable=yes ./terst
==1346438== Memcheck, a memory error detector
==1346438== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==1346438== Using Valgrind-3.16.0 and LibVEX; rerun with -h for copyright info
==1346438== Command: ./terst
==1346438==
Sampletexttodisplay
Sampletexttodisplay
==1346438== Conditional jump or move depends on uninitialised value(s)
==1346438==    at 0x4C38329: strlen (vg_replace_strmem.c:459)
==1346438==    by 0x4EB48D5: puts (in /usr/lib64/libc-2.28.so)
==1346438==    by 0x400658: main (in prog/terst)
==1346438==
==1346438== Conditional jump or move depends on uninitialised value(s)
==1346438==    at 0x4C38338: strlen (vg_replace_strmem.c:459)
==1346438==    by 0x4EB48D5: puts (in /usr/lib64/libc-2.28.so)
==1346438==    by 0x400658: main (in prog/terst)
==1346438==
==1346438== Conditional jump or move depends on uninitialised value(s)
==1346438==    at 0x4EBE86D: _IO_file_xsputn@@GLIBC_2.2.5 (in /usr/lib64/libc-2.28.so)
==1346438==    by 0x4EB4992: puts (in /usr/lib64/libc-2.28.so)
==1346438==    by 0x400658: main (in prog/terst)
==1346438==
==1346438== Conditional jump or move depends on uninitialised value(s)
==1346438==    at 0x4EBE87F: _IO_file_xsputn@@GLIBC_2.2.5 (in /usr/lib64/libc-2.28.so)
==1346438==    by 0x4EB4992: puts (in /usr/lib64/libc-2.28.so)
==1346438==    by 0x400658: main (in prog/terst)
==1346438==
==1346438== Syscall param write(buf) points to uninitialised byte(s)
==1346438==    at 0x4F2F648: write (in /usr/lib64/libc-2.28.so)
==1346438==    by 0x4EBE1FC: _IO_file_write@@GLIBC_2.2.5 (in /usr/lib64/libc-2.28.so)
==1346438==    by 0x4EBD56E: new_do_write (in /usr/lib64/libc-2.28.so)
==1346438==    by 0x4EBF2B8: _IO_do_write@@GLIBC_2.2.5 (in /usr/lib64/libc-2.28.so)
==1346438==    by 0x4EBF692: _IO_file_overflow@@GLIBC_2.2.5 (in /usr/lib64/libc-2.28.so)
==1346438==    by 0x4EB4A61: puts (in /usr/lib64/libc-2.28.so)
==1346438==    by 0x400658: main (in prog/terst)
==1346438==  Address 0x5207490 is 16 bytes inside a block of size 1,024 alloc'd
==1346438==    at 0x4C34F0B: malloc (vg_replace_malloc.c:307)
==1346438==    by 0x4EB260F: _IO_file_doallocate (in /usr/lib64/libc-2.28.so)
==1346438==    by 0x4EC04BF: _IO_doallocbuf (in /usr/lib64/libc-2.28.so)
==1346438==    by 0x4EBF727: _IO_file_overflow@@GLIBC_2.2.5 (in /usr/lib64/libc-2.28.so)
==1346438==    by 0x4EBE8CE: _IO_file_xsputn@@GLIBC_2.2.5 (in /usr/lib64/libc-2.28.so)
==1346438==    by 0x4EB4992: puts (in /usr/lib64/libc-2.28.so)
==1346438==    by 0x400631: incr (in prog/terst)
==1346438==    by 0x40064C: main (prog/terst)
==1346438==
)▒▒lay
==1346438==
==1346438== HEAP SUMMARY:
==1346438==     in use at exit: 0 bytes in 0 blocks
==1346438==   total heap usage: 2 allocs, 2 frees, 2,048 bytes allocated
==1346438==
==1346438== All heap blocks were freed -- no leaks are possible
==1346438==
==1346438== Use --track-origins=yes to see where uninitialised values come from
==1346438== For lists of detected and suppressed errors, rerun with: -s
==1346438== ERROR SUMMARY: 40 errors from 5 contexts (suppressed: 0 from 0)
$

如您所见,main 中的 printf 不会打印预期的输出“Sampletexttodisplay”(它只是输出一堆垃圾),而 incr 函数中的 printf 会打印。所以发生了一些事情,原来的指针被修改了,但没有修改到所需的字符串。是否有快速解决此问题的方法,或者是否有一些更首选的方法来修改函数中的字符串?感谢您的帮助。

【问题讨论】:

  • 您将指针设置为指向函数退出后被销毁的局部变量。您需要使用 malloc 之类的东西来分配一些内存来指向。
  • “完成此任务的最佳方法”这句话听起来像是您在问一个基于主观/意见的问题,而且没有提供足够的细节。你在寻找什么样的答案?接受它的条件是什么?
  • 把一些事情搞清楚。指针不是数组。数组不是指针。字符串是数组,而不是指针。

标签: c function pointers c-strings


【解决方案1】:

d已经是指针了,可以直接使用。但首先,您需要使用malloc() 为其分配一些内存。

另外,scanf("%s\n",d) 不应该被使用,末尾的换行符会使scanf() 填充永远等待输入。最后输入的换行符在使用scanf() 时会自动删除。而是使用scanf("%s",d)

工作代码:

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

int incr(char *ptr)
{
   scanf("%s",ptr);
   printf("%s\n",ptr);
   return 0;
}

int main(void)
{
   char *d = malloc(sizeof(char) * 104);
   // pass the string (char array) to function
   // expecting the input from scanf to be stored
   // in this pointer (modified by the function)
   incr(d);
   printf("%s\n",d);
   free(d);
   return 0;
}

【讨论】:

    【解决方案2】:

    正如 cmets 中的 @RetiredNinja 所建议的,问题在于数组 ar 的寿命。在调用incr 期间,此数组在堆栈上分配。在incr 返回后,指向此内存位置的指针可能有效,但可能无效

    如果内存是动态分配的,它的生命周期不受函数调用的约束,它会一直保持有效,直到被free释放。

    还建议您在 scanf 中指定字段宽度。

    int incr(char **ptr)
    {
       char *ar = malloc(104); // char is 1 byte, so this allocates 104 chars
    
       scanf("%103s", ar);
       *ptr = ar;
    
       printf("%s\n", *ptr);
       return 0;
    }
    

    或者,不需要这个,我们可以使用scanf将文本直接读入ptr

    int incr(char **ptr)
    {
       scanf("%103s", *ptr);
    
       printf("%s\n", *ptr);
       return 0;
    }
    

    这有两个好处。它避免了这个函数动态分配内存,我们可能会忘记。这也意味着最初分配给ptr 的任何内存都不会泄漏。

    但是,它剥夺了我们在更改 ptr 之前扫描文本并对其进行验证的机会。

    【讨论】:

      【解决方案3】:

      您成功地“修改了函数中的字符串”。好吧,实际上不是修改,更像是通过打印创建。您可以在函数中输出它,而您存储字符串的数组仍然有效且可访问,即直到函数执行结束。

      之后,不再允许访问现在不存在的局部变量。如果你不这样做,一切都会好起来的——除了一个稍微奇怪的 API,它允许保留一个指向函数之外被禁止的东西的指针。以这种方式指向指针构造的指针与从函数返回禁止指针非常相似。

      然后你确实访问了被禁止的、不再存在的字符串。这当然失败了。

      您可以将指针的参数(在我看来不是指向指针的指针,这是不需要的)传递给字符串或至少传递给合法内存;这必须是您的接口定义的一部分“亲爱的用户,您提交的指针必须指向有效且足够大的合法内存用于字符串。”

      或者您可以坚持使用指向指针接口的指针,并定义之后的指针将指向合适的东西。为此,您必须向我们提供一种创建此类合法记忆的合法方式,例如malloc()。而且您的接口规范必须包括“亲爱的用户,指针到指针参数引用的指针随后将指向合法内存。如果您调用此函数,您有责任在使用结束时正确释放它。 "

      【讨论】:

        【解决方案4】:

        有几点: 从你的评论看来,你

        您不是在尝试修改指针,而是在尝试修改它所指向的内容,因此您可以只传递原始指针。

        正如 Retired Ninja 在他的评论中指出的那样,您的代码失败的地方在于,当您离开函数时,您的原始字符数组会被破坏,因为它是一个局部变量。为避免这种情况,您应该在 main 中声明它。

        我在下面写了我相信你打算做的事情。在这里,我们可以看到正在发生以下情况:

        1. 您使用 char ar[104] 为您的数组创建空间。这里 ar 指向你的数组
        2. 您将 ar 传递给您的函数 incr,在该函数中创建它的本地副本并将其存储为 ptr。但既然我们不是试图改变指针,而是改变它指向的东西,那没关系。
        3. 数据被写入 ptr 指向的位置,与我们原来的 ar 指针指向的位置相同。
        #include <string.h>
        #include <stdio.h>
        
        int incr(char* ptr)
        {
            scanf("%s\n",ptr);
            printf("%s\n",ptr);
            return 0;
        }
        int main(void)
        {
            char ar[104];
            incr(ar);
            printf("%s\n",d);
            return 0;
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2010-10-31
          • 2014-10-15
          • 2016-11-22
          • 1970-01-01
          • 1970-01-01
          • 2013-12-27
          • 1970-01-01
          • 2021-03-18
          相关资源
          最近更新 更多