我目前正在学习如何在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的调用使用了无效指针。任何人都可以花时间解释我在想错的方式吗?
编辑:感谢您的所有答案,我正在阅读所有答案。
答案 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)
你错误地思考了两个概念。
返回函数本地的对象是个坏主意。
char *aString(void)
{
char str[20];
strcpy(str, "Hello World!");
return str;
}
因为从str
返回后释放aString
的内存,所以函数返回的指针可能不再指向有效内存。
从指针复制不会自动复制它所拥有的任何值。
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);
此外,您确实应该添加一个检查,以确保malloc
在尝试使用ptr
之前未返回空指针。大多数示例代码为了简洁省略了它。有些系统总是返回一个非空指针,但无论如何最好是安全的。始终检查您的退货价值。 ; - )
答案 4 :(得分:0)
在aString上,str在堆栈上分配,并在退出时由stack unwinding自动释放。
答案 5 :(得分:0)
老实说,我不确定你的代码是如何在没有警告的情况下编译的,但是当我编写代码并尝试编译时,我会得到一个返回本地地址的警告。使用-Wall
标志检查所有可能的警告。
基本上你所做的是创建一个char数组,你的str[20]
,并尝试返回并在其声明的范围之外使用它。
相反,您可以让aString()
直接将char *string
和strcpy
类型的参数带入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);
}