繁体   English   中英

如何将char字符串从另一个函数复制到main?

[英]How do I copy a char string from another function to main?

我正在尝试使用strcpy将char从char类型的函数复制到main函数中的var中,但是程序总是给我一个随机值作为返回值,并且程序已完成。 代码中是否缺少某些语法错误,或者还有另一种方式可以完成此复制?

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

char text()
{
    char palavra1[]= "tata";
    return palavra1;
}

char main()
{
    char saida[50];
    strcpy(saida,text());   // this line is supposed to copy the string from text to the var saida
    printf("%s", saida);
    return 0;
}

在函数text()palavra1存储在test()的堆栈帧中的堆栈上,因此当您返回字符串并返回到main的堆栈帧时,编译器将不再保留palavra1占用的内存,并且可以并且将通过调用main中的其他函数来覆盖。 要解决此问题,您有两个选择,要么让text调用方提供一个将字符串存储到的指针,要么让text malloc()为该字符串存储一些内存并返回该内存,并依赖于调用函数在该字符串释放时释放它完成。 就个人而言,我建议使用第一种方法,因为这意味着您不必担心完成后free指针,但这实际上是您的选择。

此外,您的函数返回类型都是错误的。 text()应该返回一个char* ,并且您应该希望从编译器得到关于此的警告。 最后,在ISO标准中将main()指定为int main(void)int main(int argc, char** argv) 偏离这两个签名中的任何一个通常是不好的做法。

每个函数可以返回自己的类型,但是不能返回指向其函数堆栈内分配的内存的指针,因为该函数堆栈已释放,可以在函数返回时重用,并且不再可访问。 任何尝试访问其内存不再可访问的值的尝试都是Undefined Behavior

您有两个选择,或者(1)将saida作为参数传递给text (例如, char *text (char *s) { strcpy (s, "tata"); return s } (该函数可以在此时声明为void因为复制到s的字符无论如何仍可以在调用者中使用-并且在main()不需要strcpy ,或者(2)动态分配内存以在text()保存"tata"并返回指针分配给malloc, calloc, or realloc内存在函数返回时仍然有效,因为它是在堆上分配的,并且具有程序持续时间(或直到释放)。

注意:第三个选项是将palavra1声明为具有足以容纳"tata"大小的static字符数组,这也将导致它在函数返回后仍然存在。但是,通常应避免使用前两个选项来避免这种做法。)

您可以采用以下简单方式动态分配:

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

char *text(void)
{
    char *p = "tata",   /* delcare pointer to string literal "tata" */
        *palavra1 = malloc (strlen (p) + 1);    /* allocate storage */

    if (palavra1 != NULL)       /* validate malloc succeeded */
        strcpy (palavra1, p);   /* copy "tata" to palavra1 */

    return palavra1;
}

int main(void)
{
    char saida[50],
        *p = text();    /* must preserve pointer to beginning of memory */

    /* validate p not NULL and length < 50 */
    if (p != NULL && strlen (p) < sizeof saida) {
        strcpy (saida, p);          /* copy to saida */
        printf ("%s\n", saida);     /* output */
        free (p);                   /* free allocated memory */
    }

    return 0;
}

使用/输出示例

$ ./bin/allocinfn
tata

内存使用/错误检查

在您编写的任何可以动态分配内存的代码中,对于任何分配的内存块,您都有2个责任 :(1) 始终保留指向该内存块起始地址的指针,因此,(2)在没有内存块时可以将其释放需要更长的时间。

必须使用一个内存错误检查程序来确保您不尝试访问内存或不要在已分配的块的边界之外/之外进行写入,不要尝试以未初始化的值读取或基于条件跳转,最后确定您可以释放已分配的所有内存。

对于Linux, valgrind是通常的选择。 每个平台都有类似的内存检查器。 它们都很容易使用,只需通过它运行程序即可。

$ valgrind ./bin/allocinfn
==13720== Memcheck, a memory error detector
==13720== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==13720== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==13720== Command: ./bin/allocinfn
==13720==
tata
==13720==
==13720== HEAP SUMMARY:
==13720==     in use at exit: 0 bytes in 0 blocks
==13720==   total heap usage: 1 allocs, 1 frees, 5 bytes allocated
==13720==
==13720== All heap blocks were freed -- no leaks are possible
==13720==
==13720== For counts of detected and suppressed errors, rerun with: -v
==13720== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

始终确认已释放已分配的所有内存,并且没有内存错误。

另请注意: main的正确声明是int main (void)int main (int argc, char **argv) (您将看到使用等效的char *argv[]编写)。 注意: main是以下函数的函数type int并返回一个值。请参阅: C11标准§5.1.2.2.1程序启动p1(草稿n1570) 。另请参见: 请参见main()在C和C ++中应该返回什么?

仔细检查一下,如果您还有其他问题,请告诉我。

即使您将text()的返回类型固定为

char *text()
{
    char palavra1[]= "tata";
    return palavra1;
}

这仍然是错误的。 原因是palavra1text()的局部变量,并且只有在text()返回之前它才有效且可访问。 此后,指向palavra1的指针指向无效位置,使用它读取/写入值将导致不确定的行为。

从函数返回字符串有两种方法。

  1. 该函数使用malloc分配内存,并返回指向已分配内存的指针。 调用text()的函数将需要释放内存:

     char *text(void) { char *ptr, palavra1[] = "tata"; ptr = malloc(strlen(palavra1) + 1); if(ptr == NULL) return NULL; strcpy(ptr, palavra1); return ptr; } int main(void) { char saida[50]; char *res = text(); if(res == NULL) { fprintf(stderr, "failed to allocate memory\\n"); return 1; } strncpy(saida, res, sizeof saida); saida[sizeof(saida) - 1] = 0; // making sure to get a valid string puts(saida); free(res); return 0; } 
  2. 第二种选择是将指针传递给函数,然后让函数通过指针写入内容:

     int text(char *buffer, size_t length) { palavra1[] = "tata"; if(buffer == NULL) return 0; strncpy(buffer, palavra1, length); buffer[length - 1] = 0; // making sure to get a valid string return 1; } int main(void) { char saida[50]; text(saida, sizeof saida); puts(saida); return 0; } 
  3. 第三个选项(我知道我说过2)将在text() palavra1声明为static char[] = "tata"; 为了完整起见,我添加了第三个选项,但是我建议您不要使用它,因为它还有其他您不想处理的问题,例如,当需要调用函数时一种嵌套的方式。 因此最好忽略它,直到您真正了解并理解如何处理static变量。

在这两种情况下,我都使用strncpy ,因为有了它,您可以限制写入的字节数,从而防止缓冲区溢出。 但是,如果目标缓冲区不够大, strncpy可能不会写入'\\0' 0'-终止字节,这就是为什么必须通过在字符串的最后位置写入0来确保字符串为'\\0' 0'-终止的原因。目标缓冲区。

另外,声明main功能的正确方法是:

  • int main(void);
  • int main(int argc, char **argv);
  • int main(int argc, char *argv[]);

我以前从未见过char main() ,这对我来说是第一次。 那是不正确的,您应该更改它。

palavral具有自动存储持续时间-意味着函数结束时它将被销毁(更清楚地是,声明该函数的块结束了,但是这里的函数体是声明它的块-因此,当函数结束时,它的生命周期将结束,因为块结束)。 现在,您返回了指向局部变量的指针-精确地指向函数结束后将不存在的内存,并尝试通过在strcpy上使用它来访问它-这是未定义的行为

实际上标准说:

从第6.2.4节可以看到未定义的行为

在生命周期之外引用对象

这就是问题所在:-但有几种解决方案。 如果要返回一些指针,则可以动态分配内存并返回分配的块的地址,或者使数组pass静态或全局pass

关于动态内存分配的讨论:

您可以使用malloc/calloc/reallocstrdup (内部使用其中之一)创建字符串的重复副本,该副本的存储时间超出其声明的范围,并返回该字符串。 (一种选择是动态内存分配,并将其用于从函数返回)。(此代码显示在答案的后半部分)

讨论使内存静态或全局。

使其具有静态存储期限是另一种选择。 (要么使数组成为全局数组,要么通过显式使用static关键字使其成为静态数组。)

从第6.2.4p3静态存储持续时间

...它的生命周期是程序的整个执行过程,并且在程序启动之前,其存储值仅初始化一次。

最后一行是什么意思? 除非程序结束,否则(局部变量的)生存期不会结束,这意味着我们可以从函数中返回它。 (是的,这是一个解决方案,但您不会看到它-这不是做事的好方法)。

动态分配代码

const char*p = "data";
char *s = malloc(strlen(p)+1);
if(!s){
   perror("malloc");
   exit(EXIT_FAILURE);
}
memcpy(s,p,strlen(p)+1);
return s;

main()

 char *p = text();
 if(p!=NULL)
     strcpy(saida,p);
 free(p);

静态存储持续时间示例的代码:

在这种情况下,除了将指针变量设置为static之外,没有什么要显示的。

static char palavar1[]="tata";
...
return palavar;

无需使用所有那些困难的词-可以说,即使函数结束,现在此局部变量仍将保持活动状态。 之前讨论的问题现在不再是问题。 它能活多久? 直到程序结束。 因此,您可以安全地使用上述技术,放弃在生命周期之外访问对象的可能性。

其他解决方案

其他解决方案将简单地传递将被修改的缓冲区,并使用strcpymemcpy将字符串文字内容复制到该缓冲区中。 无需退货。 我在第一次编辑时没有讨论过这个解决方案,因为它是实现您想要做的事情的不同方法。

Standard指示main返回类型为int not char 理想情况下, int main(void)可以。

您可以将其设为静态(当然这是指针):

static char palavra1[]= "tata"; 

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM