我正在书上练习,将句子中的单词改为猪拉丁语。代码在窗口7中工作正常,但是当我在mac中编译时,错误就出来了。
经过一些测试,错误来自那里。我不明白这个问题的原因。我正在为所有指针使用动态内存,我还添加了空指针的检查。
while (walker != NULL && *walker != NULL){
free(**walker);
free(*walker);
free(walker);
walker++;
}
完整源代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#define inputSize 81
void getSentence(char sentence [], int size);
int countWord(char sentence[]);
char ***parseSentence(char sentence[], int *count);
char *translate(char *world);
char *translateSentence(char ***words, int count);
int main(void){
/* Local definition*/
char sentence[inputSize];
int wordsCnt;
char ***head;
char *result;
getSentence(sentence, inputSize);
head = parseSentence(sentence, &wordsCnt);
result = translateSentence(head, wordsCnt);
printf("\nFinish the translation: \n");
printf("%s", result);
return 0;
}
void getSentence(char sentence [81], int size){
char *input = (char *)malloc(size);
int length;
printf("Input the sentence to big latin : ");
fflush(stdout);
fgets(input, size, stdin);
// do not copy the return character at inedx of length - 1
// add back delimater
length = strlen(input);
strncpy(sentence, input, length-1);
sentence[length-1]='\0';
free(input);
}
int countWord(char sentence[]){
int count=0;
/*Copy string for counting */
int length = strlen(sentence);
char *temp = (char *)malloc(length+1);
strcpy(temp, sentence);
/* Counting */
char *pToken = strtok(temp, " ");
char *last = NULL;
assert(pToken == temp);
while (pToken){
count++;
pToken = strtok(NULL, " ");
}
free(temp);
return count;
}
char ***parseSentence(char sentence[], int *count){
// parse the sentence into string tokens
// save string tokens as a array
// and assign the first one element to the head
char *pToken;
char ***words;
char *pW;
int noWords = countWord(sentence);
*count = noWords;
/* Initiaze array */
int i;
words = (char ***)calloc(noWords+1, sizeof(char **));
for (i = 0; i< noWords; i++){
words[i] = (char **)malloc(sizeof(char *));
}
/* Parse string */
// first element
pToken = strtok(sentence, " ");
if (pToken){
pW = (char *)malloc(strlen(pToken)+1);
strcpy(pW, pToken);
**words = pW;
/***words = pToken;*/
// other elements
for (i=1; i<noWords; i++){
pToken = strtok(NULL, " ");
pW = (char *)malloc(strlen(pToken)+1);
strcpy(pW, pToken);
**(words + i) = pW;
/***(words + i) = pToken;*/
}
}
/* Loop control */
words[noWords] = NULL;
return words;
}
/* Translate a world into big latin */
char *translate(char *word){
int length = strlen(word);
char *bigLatin = (char *)malloc(length+3);
/* translate the word into pig latin */
static char *vowel = "AEIOUaeiou";
char *matchLetter;
matchLetter = strchr(vowel, *word);
// consonant
if (matchLetter == NULL){
// copy the letter except the head
// length = lenght of string without delimiter
// cat the head and add ay
// this will copy the delimater,
strncpy(bigLatin, word+1, length);
strncat(bigLatin, word, 1);
strcat(bigLatin, "ay");
}
// vowel
else {
// just append "ay"
strcpy(bigLatin, word);
strcat(bigLatin, "ay");
}
return bigLatin;
}
char *translateSentence(char ***words, int count){
char *bigLatinSentence;
int length = 0;
char *bigLatinWord;
/* calculate the sum of the length of the words */
char ***walker = words;
while (*walker){
length += strlen(**walker);
walker++;
}
/* allocate space for return string */
// one space between 2 words
// numbers of space required =
// length of words
// + (no. of words * of a spaces (1) -1 )
// + delimater
// + (no. of words * ay (2) )
int lengthOfResult = length + count + (count * 2);
bigLatinSentence = (char *)malloc(lengthOfResult);
// trick to initialize the first memory
strcpy(bigLatinSentence, "");
/* Translate each word */
int i;
char *w;
for (i=0; i<count; i++){
w = translate(**(words + i));
strcat(bigLatinSentence, w);
strcat(bigLatinSentence, " ");
assert(w != **(words + i));
free(w);
}
/* free memory of big latin words */
walker = words;
while (walker != NULL && *walker != NULL){
free(**walker);
free(*walker);
free(walker);
walker++;
}
return bigLatinSentence;
}
答案 0 :(得分:4)
您的代码不必要地复杂化,因为您已经设置了以下内容:
n
:字数words
:指向已按顺序保存n+1
char **
个值的已分配内存words[i]
(0 <= i && i < n
):指向分配的内存,可以按顺序保存一个char *
words[n]
:NULL
words[i][0]
:指向一个单词的已分配内存(如前所述,0&lt; = i&lt; n)由于每个words[i]
指向按顺序排序,因此对于某些有效整数j有words[i][j]
...但j
的允许值始终为0,因为那里那里只有一个char *
malloc()。因此,您可以完全消除此级别的间接,并且只有char **words
。
walker
相同的words
开头,因此它首先尝试释放words[0][0]
(这很好并有效),然后尝试释放words[0]
(这很好)并且工作),然后尝试释放words
(这很好并且有效,但意味着您无法再访问words[i]
的任何其他i
- 即“存储泄漏” )。然后它递增walker
,使其或多或少等同于&words[1]
;但是words
已经free()
d。
我没有在这里使用walker
,而是使用带有整数i
的循环:
for (i = 0; words[i] != NULL; i++) {
free(words[i][0]);
free(words[i]);
}
free(words);
我还建议在malloc()
和calloc()
返回值上删除所有演员表。如果在执行此操作后收到编译器警告,则通常表示以下两种情况之一:
#include <stdlib.h>
或后者有时会起作用,但却是苦难的一个秘诀:好的C代码是糟糕的C ++代码,好的C ++代码不是C代码。 : - )
编辑:PS:我错过了@David RF捕获的一个lengthOfResult
。
答案 1 :(得分:3)
int lengthOfResult = length + count + (count * 2);
必须是
int lengthOfResult = length + count + (count * 2) + 1; /* + 1 for final '\0' */
while (walker != NULL && *walker != NULL){
free(**walker);
free(*walker);
/* free(walker); Don't do this, you still need walker */
walker++;
}
free(words); /* Now */
你有泄漏:
int main(void)
{
...
free(result); /* You have to free the return of translateSentence() */
return 0;
}
答案 2 :(得分:1)
在此代码中:
while (walker != NULL && *walker != NULL){
free(**walker);
free(*walker);
free(walker);
walker++;
}
在释放**walker
之前,您需要检查\0
是否为空。
另外 - 当你计算你需要返回字符串的内存长度时,你只需一个字节,因为你复制每个单词PLUS A SPACE(包括最后一个单词后的空格)加上终止bigLatinSentence
。换句话说,当您将结果复制到{{1}}时,您将覆盖一些不属于您的内存。有时你会侥幸逃脱,有时候你却没有......
答案 3 :(得分:1)
现在我明白了,我感到愚蠢。
我在gdb下运行时注意到的是,第二次运行的东西在线路上循环失败
自由(步行者);
现在为什么会如此。这是我没有立刻看到它的愚蠢。当你第一次运行那一行时,第二次运行的整个char ***指针数组(在第一次运行时也称为walker),当你运行该行时,你试图释放已经释放记忆。
所以它应该是:
while (walker != NULL && *walker != NULL){
free(**walker);
free(*walker);
walker++;
}
free(words);
修改强>
我还想注意你不必在C中使用void *进行强制转换。
所以当你调用malloc时,你不需要那里的(char *)。