我正在尝试使用strcpy将char类型函数中的char复制到main函数中的var,但程序总是给我一个随机值作为return并且程序结束。是否有一些语法错误我在代码中缺少或者可能是另一种方法来执行此复制?
#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;
}
答案 0 :(得分:3)
在函数text()
中,palavra1
存储在test()
堆栈帧中的堆栈中,因此当您返回字符串并返回main
的堆栈时帧,palavra1
占用的内存不再由编译器保存,可以通过调用main中的其他函数来覆盖。要解决此问题,您有两个选项,要么调用者text
提供指向存储字符串的指针,要么text
malloc()
为字符串提供一些内存并返回该字符串,依赖在调用函数上完成后释放它。就个人而言,我建议使用第一种方法,因为这意味着当你完成时你不必担心free
指针,但它确实是你的选择。
此外,您的函数返回类型都是错误的。 text()
应返回char*
,您应该从编译器中收到有关该问题的警告。最后,ISO标准中指定的main()
为int main(void)
或int main(int argc, char** argv)
。偏离这两个签名中的任何一个都是不好的做法。
答案 1 :(得分:2)
每个函数都可以返回自己的类型,但是它不能返回指向其函数堆栈中分配的内存的指针,因为函数堆栈被释放以便在函数返回时重用,并且不再可访问。任何访问其内存不再可访问的值的尝试都是未定义的行为。
您有两个选项,(1)将saida
作为参数传递给text
,(例如char *text (char *s) { strcpy (s, "tata"); return s }
(此时函数可以声明为void
,因为复制到s
的字符无论如何都将在调用者中可用 - 并且strcpy
中不需要main()
,或者(2)动态分配内存来保存{{} 1}}在"tata"
中并返回一个指向新内存块的指针。用text()
分配的内存在函数返回时幸存,因为它在堆上分配并具有程序持续时间(或直到它被释放)
(注意:第三个选项是将malloc, calloc, or realloc
声明为palavra1
字符数组,其大小足以容纳static
- 这也会导致它为了生存功能返回。但是,通常应该避免这种做法,而不是前两个选项)
您可以通过以下简单方式动态分配:
"tata"
示例使用/输出
#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;
}
内存使用/错误检查
在你编写的动态分配内存的任何代码中,你有2个职责关于任何分配的内存块:(1)总是保留一个指向起始地址的指针内存块,(2)当不再需要时,它可以释放。
必须使用内存错误检查程序,以确保您不会尝试访问内存或写入超出/超出已分配块的范围,尝试读取或基于未初始化值的条件跳转,最后,确认您释放了所有已分配的内存。
对于Linux $ ./bin/allocinfn
tata
是正常的选择。每个平台都有类似的记忆检查器。它们都很简单易用,只需通过它运行程序即可。
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
的函数,它会返回一个值。请参阅:C11 Standard §5.1.2.2.1 Program startup p1 (draft n1570)。另请参阅:See What should main() return in C and C++?)
仔细看看,如果您有其他问题,请告诉我。
答案 2 :(得分:1)
即使您将text()
的返回类型修复为
char *text()
{
char palavra1[]= "tata";
return palavra1;
}
这仍然是错误的。原因是palavra1
是局部变量
在text()
中,它仅在text()
返回之前有效且可访问。在那之后
指向palavra1
的指针指向无效位置,使用它来读/写
值将导致未定义的行为。
从函数返回字符串有两种方法。
该函数使用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;
}
第二个选项是传递指向函数的指针并让函数 通过指针编写内容:
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;
}
第三个选项(我知道我说过2)将在palavra1
中声明text()
为static char[] = "tata";
。为了完整起见,我已经添加了第三个
选项,但我建议您不要使用它,因为它有其他问题
您不想处理的问题,例如当您的职能需要时
以嵌套的方式调用。所以最好忽略它,直到你真正了解和
了解如何处理static
变量。
在这两种情况下,我都使用了strncpy
,因为有了它,你可以限制它的数量
写入的字节可以防止缓冲区溢出。但是,strncpy
可能不会
如果目标缓冲区不够大,则写入'\0'
- 终止字节,
这就是拥有以确保字符串为'\0'
的原因 - 终止于
在目标缓冲区的最后一个位置写入0。
此外,声明main
函数的正确方法是:
int main(void);
int main(int argc, char **argv);
int main(int argc, char *argv[]);
我之前从未见过char main()
,这对我来说是第一次。那&#39; S
不正确,你应该改变它。
答案 3 :(得分:0)
palavral
具有自动存储持续时间 - 意味着当函数结束时它将被销毁(更明确地说它被声明的块结束但是这里函数体是声明它的块 - 所以当函数结束它的生命将结束,因为块结束)。现在你返回了指向局部变量的指针 - 精确指向一旦函数结束并且试图通过在strcpy
上使用它来访问它时将不存在的内存 - 这是undefined behavior。
事实上,标准说: -
从§6.2.4您可以看到未定义的行为
对象在其生命周期之外被引用
这就是问题所在: - 但有几种解决方案。如果要返回某个指针,则可以动态分配内存并返回已分配块的地址,或使数组pass
为静态或全局。
您可以使用malloc/calloc/realloc
或strdup
(内部使用其中一个)创建字符串的副本,其存储持续时间超出其声明范围并返回。 (一种选择是动态内存分配,并使用它从函数返回)。(代码的代码显示在答案的后面部分)
使其具有静态存储持续时间是另一种选择。 (通过显式使用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;
不使用所有那些难以理解的单词 - 可以说现在这个局部变量即使在函数结束时也会保持活跃状态。现在讨论的上一个问题不会成为问题。它会活多久?直到该计划结束。所以你可以安全地使用上述技术丢弃的可能性 在它的生命周期之外访问对象。
其他解决方案将简单地传递将被修改的缓冲区,并使用strcpy
或memcpy
将字符串文字内容复制到其中。没有必要返回一些东西。我在第一次编辑时没有讨论过这个解决方案,因为这是实现你想要做的事情的一种不同的方法。
标准指示将main
的返回类型设为int
而不是char
。理想情况下,int main(void)
会这样做。
答案 4 :(得分:0)
你可以使它静止(这当然是指针):
B