I haven’t used C in a long time and apparently I’ve forgotten more than I thought. While attempting to use malloc() to allocate a string, I keep getting the old data for that string, including it’s old, longer length when the requested space is shorter. The circumstance do include the pointer to the string being free()’d and set to NULL. Here is a sample run of what I see in my terminal:
yes, quit, or other (<-message from program)
oooo (<-user input; this will be put to upper case and token'd)
------uIT LENGTH:4 (<-debug message showing length of userInputToken)
preC--tmp: (<-contents of tmp variable)
pstC--tmp:OOOO (<-contents of temp variable)
bad input (<-program response)
yes, quit, or other
yes
------uIT LENGTH:3
preC--tmp:OOOO (<-: tmp = malloc(sizeof(char)*(strlen(userInputToken)-1)); )
pstC--tmp:YESO (<-: strncpy(tmp,userInputToken,strlen(userInputToken)-1); )
bad input
yes, quit, or other
yes
------uIT LENGTH:3
preC--tmp:YESO
pstC--tmp:YESO
bad input
yes, quit, or other
quit
------uIT LENGTH:4
preC--tmp:YESO
pstC--tmp:QUIT (<-: Successful quit because I only did 4 chars; if 5 were used, this would have failed)
As you can see, strlen(userInputToken) gets the correct length and it is used to get the correct number of characters copied – but either free() or malloc() doesn’t seem to care about it. I can’t figure out what’s going on here! Is this a punishment for leaving C for Python?
What’s more, the tmp variable should be cleared regardless of free() because it is limited by its scope. Here is the code where everything goes down:
In main.c:
void run() {
outputFlagContainer *outputFlags = malloc(sizeof(outputFlagContainer));
while(true) {
puts("yes, quit, or other");
outputFlags = getUserInput(outputFlags);
if (outputFlags->YES) {
puts("It was a yes!");
} else if (outputFlags->QUIT) {
break;
} else {
puts("bad input");
}
}
free(outputFlags);
}
In messsageParserPieces.h:
outputFlagContainer *getUserInput(outputFlagContainer *outputFlags) {
outputFlags = resetOutputFlags(outputFlags);
char *userInput = NULL;
char user_input[MAX_INPUT];
char *userInputToken = NULL;
char *tmp = NULL;
char *finalCharacterCheck = NULL;
// Tokens to search for:
char QUIT[] = "QUIT";
char YES[] = "YES";
userInput = fgets(user_input, MAX_INPUT-1, stdin);
int i = 0;
while(userInput[i]) {
userInput[i] = toupper(userInput[i]);
i++;
}
userInputToken = strtok(userInput, " ");
if (userInputToken) {
finalCharacterCheck = strchr(userInputToken, '\n');
if (finalCharacterCheck) {
int MEOW = strlen(userInputToken)-1; // DEBUG LINE
printf("\n------uIT LENGTH:%d\n", MEOW); // DEBUG LINE
// The problem appears to happen here and under the circumstances that
// userInput is (for example) 4 characters and then after getUserInput()
// is called again, userInput is 3 characters long.
tmp = malloc(sizeof(char)*(strlen(userInputToken)-1));
if (tmp == NULL) {
exit(1);
}
printf("\npreC--tmp:%s\n", tmp); // This shows that the malloc DOES NOT use the given length.
strncpy(tmp,userInputToken,strlen(userInputToken)-1);
printf("\npstC--tmp:%s\n", tmp); // Copies in the correct number of characters.
userInputToken = tmp;
free(tmp);
tmp = NULL;
}
}
while (userInputToken != NULL) { // NULL = NO (more) tokens.
if (0 == strcmp(userInputToken, YES)) {
outputFlags->YES = true;
} else if (0 == strcmp(userInputToken, QUIT)) {
outputFlags->QUIT = true;
}
userInputToken = strtok(NULL, " ");
if (userInputToken) {
finalCharacterCheck = strchr(userInputToken, '\n');
if (finalCharacterCheck) {
tmp = malloc(sizeof(char)*(strlen(userInputToken)-1));
if (tmp == NULL) {
exit(1);
}
strncpy(tmp,userInputToken,strlen(userInputToken)-1);
userInputToken = tmp;
free(tmp);
tmp = NULL;
}
}
}
return outputFlags;
}
I’m assuming this is some kind of obvious error, but I’ve tried googling it for about 2 hours tonight. I can’t think of how to search this that doesn’t bring up a malloc() tutorial – and I have looked at a couple already.
Any insight at all would be greatly appreciated!
This snippet shows that you expect
tmpto be initialised with something. This is not true. You must initialise your memory after allocating it. That’s what you do withstrncpy.There’s also a problem because you are not allocating enough bytes to hold the string, therefore you cannot display it with a plain
%sformat specifier. You are allocatingstrlen(userInputToken)-1bytes and copying that same number. That means there’s no room for a null character, andstrncpywill consequently not terminate your string. You should always add one more byte, and if the NULL character will not be copied bystrncpythen you must set it yourself:So, just to be clear, you have three issues:
strncpybecause it did not encounter a string terminator within the allowed number of bytes).I just spotted something else in your
while (userInputToken != NULL)loop… You always to a string compare usinguserInputTokenat the beginning of the loop, but inside the loop (and also in the part above the loop) you do this:That means
userInputTokenis a dangling pointer. It points to memory that has been freed, and you must NOT use it. You will have to rethink your approach, and allow it to live until it’s no longer needed.