此问题已经在这里有了答案:
我有此代码:
static void foo(char *string1, char *string2)
{
char *string1_copy= malloc(strlen(string1));
strcpy(string1_copy, haystack);
char *string2_copy = malloc(strlen(string2));
strcpy(string2_copy, needle);
}
我必须复制string1
和string2
才能修改其副本并保留原件。这会执行应做的工作,并且编译时不会出错,但是在我运行时:
valgrind --leak-check=full -v ./myProgram
我明白了:
==20595== ERROR SUMMARY: 3 errors from 3 contexts (suppressed: 0 from 0)
==20595==
==20595== 1 errors in context 1 of 3:
==20595== Invalid read of size 1
==20595== at 0x4C376F4: strstr (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==20595== by 0x108CED: grep (myProgram.c:87)
==20595== by 0x109023: main (myProgram.c:214)
==20595== Address 0x522e3b3 is 0 bytes after a block of size 3 alloc'd
==20595== at 0x4C2FB0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==20595== by 0x108CA5: grep (myProgram.c:77)
==20595== by 0x109023: main (myProgram.c:214)
==20595==
==20595==
==20595== 1 errors in context 2 of 3:
==20595== Invalid write of size 1
==20595== at 0x4C32E0D: strcpy (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==20595== by 0x108CBC: grep (myProgram.c:78)
==20595== by 0x109023: main (myProgram.c:214)
==20595== Address 0x522e3b3 is 0 bytes after a block of size 3 alloc'd
==20595== at 0x4C2FB0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==20595== by 0x108CA5: grep (myProgram.c:77)
==20595== by 0x109023: main (myProgram.c:214)
==20595==
==20595==
==20595== 1 errors in context 3 of 3:
==20595== Invalid write of size 1
==20595== at 0x4C32E0D: strcpy (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==20595== by 0x108C91: grep (myProgram.c:75)
==20595== by 0x109023: main (myProgram.c:214)
==20595== Address 0x522e362 is 0 bytes after a block of size 18 alloc'd
==20595== at 0x4C2FB0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==20595== by 0x108C7A: grep (myProgram.c:74)
==20595== by 0x109023: main (myProgram.c:214)
==20595==
==20595== ERROR SUMMARY: 3 errors from 3 contexts (suppressed: 0 from 0)
这正是我使用strcpy()
创建这2个副本以及使用strstr()
读取它们的地方。有什么办法可以避免这种情况,还是我不应该在这里使用strcpy()
?我传递的字符串的strlen(string)
大小不正确吗?
您的分配提出了经典的[[Off-By-One问题。 C中的字符串始终以nul-character终止。这就是string与character array的区别。要为源字符串src
的副本正确分配存储,必须分配strlen(src) + 1
字节。
foo
功能没有意义。在foo
中,您可以分配存储空间,例如char *string1_copy= malloc(strlen(string1));
,但无法提供函数返回后程序可以利用分配的内存的任何方式。 void
函数不返回任何消除确定副本成功/失败
的方法的值,也没有其他pointer-to-pointer参数来提供任何方法来更新原始指针地址。此外,在分配存储空间之后,该函数返回,并且您丢失了保存每个分配的起始地址的指针,从而创建了[[内存泄漏。[要复制两个字符串时,编写一个可以复制两个字符串的函数没有意义。该一次性功能几乎没有可重用性。取而代之的是简单地编写一个复制单个字符串的函数,提供一个有意义的返回值以允许确定成功/失败
,然后为您需要复制的每个字符串调用一次该函数。 该函数的重构不仅使您能够充分地[[验证每个分配,而且在需要复制字符串的任何时间都可以重用。实际上,POSIX提供了一个strdup()
函数来执行此操作,但是您可以轻松编写自己的函数以确保严格遵守C标准。这种功能的合理实现可以写为:
/* returns pointer to allocated copy of src, or NULL on failure */
char *dupstr (const char *src)
{
size_t len = strlen (src); /* get length of src */
char *dest = malloc (len + 1); /* allocate length + 1 bytes */
if (!dest) { /* validate EVERY allocation */
perror ("dupstr() malloc-dest");
return NULL;
}
return memcpy (dest, src, len + 1); /* copy src to dest, return ptr */
}
((note:
您还可以在if (!src)
上添加检查以确保传递的指针不是NULL
-留给您)]这是一个简单的函数,它获取原始字符串的长度(作为分配并在失败时提供错误并返回const char*
传递),然后分配len + 1
字节以提供足够的存储空间validating
NULL
。然后,该函数使用src
将dest
复制到目标字符串memcpy()
,并返回指向dest
的指针。(note:
无需使用strcpy()
。此时,您已经计算出src
的长度,也无需再次扫描string-of-string [strcpy()
)。复制并输出所有程序参数的简单实现可以是:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* returns pointer to allocated copy of src, or NULL on failure */
char *dupstr (const char *src)
{
size_t len = strlen (src); /* get length of src */
char *dest = malloc (len + 1); /* allocate length + 1 bytes */
if (!dest) { /* validate EVERY allocation */
perror ("dupstr() malloc-dest");
return NULL;
}
return memcpy (dest, src, len + 1); /* copy src to dest, return ptr */
}
int main (int argc, char **argv) {
char *copies[argc]; /* VLA of argc pointers to char */
for (int i = 0; i < argc; i++) { /* loop over each argument */
if ((copies[i] = dupstr (argv[i]))) { /* duplicate in copies[i] */
puts (copies[i]); /* output copy */
free (copies[i]); /* free copy */
}
}
}
示例使用/输出
$ ./bin/dupstr my dog has fleas and my cat has none - lucky cat ./bin/dupstr my dog has fleas and my cat has none - lucky cat
内存使用/错误检查
在您编写的任何可动态分配内存的代码中,对于任何已分配的内存块,您都有2个职责
:(1)始终保留指向起始地址的指针,因此,( 2)当不再需要它时,可以将其freed。务必使用内存错误检查程序,以确保您不会尝试访问内存或在分配的块的边界之外/之外写,尝试读取或基于未初始化的值进行条件跳转,最后,确认您释放了已分配的所有内存。
对于Linuxvalgrind
是正常选择。每个平台都有类似的内存检查器。它们都很容易使用,只需通过它运行程序即可。$ valgrind ./bin/dupstr my dog has fleas and my cat has none - lucky cat ==6014== Memcheck, a memory error detector ==6014== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al. ==6014== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info ==6014== Command: ./bin/dupstr my dog has fleas and my cat has none - lucky cat ==6014== ./bin/dupstr my dog has fleas and my cat has none - lucky cat ==6014== ==6014== HEAP SUMMARY: ==6014== in use at exit: 0 bytes in 0 blocks ==6014== total heap usage: 14 allocs, 14 frees, 1,086 bytes allocated ==6014== ==6014== All heap blocks were freed -- no leaks are possible ==6014== ==6014== For counts of detected and suppressed errors, rerun with: -v ==6014== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
始终确认已释放已分配的所有内存,并且没有内存错误。
仔细检查,如果还有其他问题,请告诉我。