释放代码
您的释放代码很有趣——不是完全错误,但也不是完全正确:
static void freeDuplicateArgs(char **copy) {
int i = 0;
while (copy[i]) {
if (copy[i] != NULL){
free(copy[i]);
}
i++;
}
if (copy != NULL){
free(copy);
}
}
您copy != NULL
在释放它之前进行了测试,但是您一直在使用它,因此那里的测试是多余的。此外,您可以做到free(NULL)
不会遇到问题。
然后我们看一下循环:
while (copy[i] != NULL)
{
if (copy[i] != NULL)
循环条件和 if 条件是相同的——同样,你可以free(NULL)
.
您的 while 循环具有写出的 for 循环结构。你会做得更好:
static void freeDuplicateArgs(char **copy)
{
if (copy != NULL)
{
for (int i = 0; copy[i] != NULL; i++)
free(copy[i]);
free(copy);
}
}
防止崩溃的测试copy != NULL
,并且仅在已知它为非空时才释放它是一个(非常非常)次要的优化。
我怀疑这是否是问题的直接原因(稍后我会更加努力地查看其余代码),但这是需要考虑的事情。
分配代码
你有:
static char **duplicateArgs(int argc, char **argv) {
if (argc <= 1) {
printf("%s\n", "No strings to duplicate.");
exit(1);
}
首先,可以在没有用户参数的情况下调用程序,但它仍然具有argv[0]
、程序名称和 NULL 指针。您的代码可能应该简单地通过返回来处理负值(char **)NULL
。对于 0 个参数,您返回一个指向空指针的指针。对于一个或多个论点,还有工作要做。
考虑这一行:
duplicate[i] = (char*)malloc(stringLength * sizeof(char) + 1);
您可能会考虑两种情况:sizeof(char) == 1
和sizeof(char) > 1
(是的,等等——等等!)。如果sizeof(char) == 1
,那么你不需要乘以它。如果sizeof(char) > 1
,您没有分配足够的空间;表达式应该是(stringLength + 1) * sizeof(char)
。因此,编写以下两行之一:
duplicate[i] = (char*)malloc((stringLength + 1) * sizeof(char));
duplicate[i] = (char*)malloc(stringLength + 1);
我注意到 C 标准sizeof(char) == 1
根据定义说,所以实际结果是相同的,但如果你正在处理数组int
或其他东西,sizeof(type)
在正确的位置获得乘法可能是至关重要的。
ISO/IEC 9899:2011 §6.5.3.4sizeof
和_Alignof
运算符
¶4 当sizeof
应用于类型为 , 或 , 的操作数char
(unsigned char
或其
signed char
限定版本)时,结果为 1。
Mac OS X 10.7.5 上的 Valgrind
将您的代码几乎与问题中所写的一样:
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static char **duplicateArgs(int argc, char **argv)
{
if (argc <= 1) {
printf("%s\n", "No strings to duplicate.");
exit(1);
}
char **duplicate = (char**)malloc((argc + 1) * sizeof(char**));
int i = 0;
int j = 0;
int stringLength;
while (argv[i]) {
stringLength = strlen(argv[i]);
duplicate[i] = (char*)malloc(stringLength * sizeof(char) + 1);
for (j = 0; j < stringLength; j++) {
duplicate[i][j] = toupper(argv[i][j]);
}
duplicate[i][j]= '\0';
i++;
}
duplicate[i] = NULL;
return duplicate;
}
static void freeDuplicateArgs(char **copy)
{
int i = 0;
while (copy[i]) {
if (copy[i] != NULL){
free(copy[i]);
}
i++;
}
if (copy != NULL){
free(copy);
}
}
int main(int argc, char **argv)
{
char **copy = duplicateArgs(argc, argv);
char **p = copy;
argv++;
p++;
while(*argv) {
printf("%s %s\n", *argv++, *p++);
}
freeDuplicateArgs(copy);
return 0;
}
(区别在于int i = 0;
这个工作代码而不是int i = 1;
问题代码。)
当在 下运行时valgrind
,这会得到一份干净的健康清单:
$ gcc -O3 -g -std=c99 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition mlk.c -o mlk
$ valgrind ./mlk nuts oh hazelnuts
==582== Memcheck, a memory error detector
==582== Copyright (C) 2002-2011, and GNU GPL'd, by Julian Seward et al.
==582== Using Valgrind-3.7.0 and LibVEX; rerun with -h for copyright info
==582== Command: ./mlk nuts oh hazelnuts
==582==
nuts NUTS
oh OH
hazelnuts HAZELNUTS
==582==
==582== HEAP SUMMARY:
==582== in use at exit: 18,500 bytes in 33 blocks
==582== total heap usage: 38 allocs, 5 frees, 18,564 bytes allocated
==582==
==582== LEAK SUMMARY:
==582== definitely lost: 0 bytes in 0 blocks
==582== indirectly lost: 0 bytes in 0 blocks
==582== possibly lost: 0 bytes in 0 blocks
==582== still reachable: 18,500 bytes in 33 blocks
==582== suppressed: 0 bytes in 0 blocks
==582== Rerun with --leak-check=full to see details of leaked memory
==582==
==582== For counts of detected and suppressed errors, rerun with: -v
==582== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 1 from 1)
$
如您所见,没有报告内存错误。这台机器仍在使用的 33 个块是“正常的”;它们由 Mac OS XC 运行时库分配。我还没有valgrind
Mac OS X 10.8 系统可以与之比较。我建议你尝试在下面运行一个非常简单的程序valgrind
,看看它告诉你什么:
int main(void) { return 0; }
这将是您系统的基线。如果有泄漏,它们不是由你的代码引起的,你需要学习如何记录抑制,这样你的程序就不会因为 O/S 的违规而受到惩罚。
如果你运行上面的简单程序并发现它不会产生泄漏——或者泄漏更简单、更小——然后尝试一些稍微复杂一些的程序:
#include <stdlib.h>
int main(void)
{
void *vp = malloc(32);
free(vp);
return(0);
}
这样可以确保将malloc()
其拖入并使用,但显然不会泄漏(并且它也可以malloc()
正确处理故障!)。
valgrind
支持 Mac OS X 10.8
Valgrind网站说:
2012 年 9 月 18 日:valgrind-3.8.1,适用于 X86/Linux、AMD64/Linux、ARM/Linux、PPC32/Linux、PPC64/Linux、S390X/Linux、MIPS/Linux、ARM/Android(2.3.x 及更高版本), X86/Android(4.0 和更高版本)、X86/Darwin 和 AMD64/Darwin(Mac OS X 10.6 和 10.7,对 10.8 的支持有限)可用。
您所看到的可能是对 Mac OS X 10.8 的有限支持的一个方面。
我知道我需要valgrind
为自己重建(3.7.0 不是最新的)。