Quote:
Originally Posted by oracleguy
Also looking at your code I noticed that if you were to hit enter immediately at the prompt your function would return a string that most likely will not be null terminated.
|
Actually, it was returning a NULL terminated string, but because I didn't explicitly instruct it to, it may have just been dumb luck. I decided to memset the initial allocation of the string so I don't need to worry about it.
Quote:
Originally Posted by oracleguy
You should look into using realloc instead of malloc in your loop. It would reduce the amount of code you need and make the function faster.
|
True, but the thing that bothers me about realloc is if it fails, it returns a NULL pointer. What happens to the information that was initially malloced? I would hope realloc frees the memory at that location before it returns NULL (or not, read bellow). Even if it does free the memory, I still no longer have access to the memory. Although, I did just think of a way around this, while still keeping the data...
This I believe is a better version:
Code:
#include <stdio.h>
#include <stdlib.h>
char* GetUserInput(void);
void CopyString(char *, char *);
void MemSet(void *, char, size_t);
int main()
{
char *pString = NULL;
pString = GetUserInput();
printf("\n\n\n%s\n", pString);
free(pString);
return 0;
}
//////////////////////////////////////////////////////////////////////
// Function: GetUserInput
// Inputs: None
// Returns: An character pointer to a block of allocated memory
// containing the user input
// Description: Gets the user input by reading stdin.
//////////////////////////////////////////////////////////////////////
char* GetUserInput(void)
{
// The value by which to grow the string
short mallocSize = (sizeof(char) * 10);
int charCount = 0; // Counts the number of characters in string
int sizeOfString = mallocSize; // Current byte size of the string
// The string who's pointer is returned
char *pString = (char *) malloc(sizeOfString);
// Temporally stores the string to prevent data lose during malloc
char *pTempAddress = NULL;
char tempChar = '\0'; // Temporally stores the input character
if(pString == NULL)
{
return NULL;
}
MemSet(pString, '\0', sizeOfString);
// Get a character from stdin until the return key is pressed
while((tempChar = getchar()) != '\n')
{
// If tempChar is less than space or greater than tilde
if((tempChar < 0x20) || (tempChar > 0x7E))
{
return pString;
}
// If there is not enough memory to store another character
if((sizeof(char) * charCount) >= sizeOfString)
{
// Increment the amount of memory
sizeOfString += mallocSize;
pTempAddress = (char *) realloc(pString, sizeOfString);
// If the memory hasn't been reallocated
if(pTempAddress == NULL)
{
pTempAddress = NULL;
break;
}
// If the reallocated memory is not in the same place as
// the old memory
if(pString != pTempAddress)
{
free(pString);
}
// Reset the old pointer
pString = pTempAddress;
// Clear the new memory to prevent contamination
MemSet((pString + (sizeOfString - mallocSize )), '\0', mallocSize);
}
pString[charCount] = tempChar;
charCount++;
}
return pString;
}
// End GetUserInput
//////////////////////////////////////////////////////////////////////
// Function: CopyString
// Inputs-
// pSource: A pointer to the string to be copied
// pDestination: A preallocated pointer to the location
// in which pSource is to be copied
// Returns: None
// Description: Copies inputed source to inputed destination
//////////////////////////////////////////////////////////////////////
void CopyString(char *pSource, char *pDestination)
{
long index = 0;
// If the source and destination do NOT exist
if((pSource == NULL) || (pDestination == NULL))
{
return;
}
// For each charater in source
for(index = 0; (pSource[index] != '\n') && (pSource[index] != '\0'); index++)
{
// Copy the chacter at index of source to the character at
// index of destination
pDestination[index] = pSource[index];
}
}
// End CopyString
//////////////////////////////////////////////////////////////////////
// Function: MemSet
// Inputs-
// pPtr: A pointer to the start of a memory block
// value: The character used to replace the information
// in the memory block
// bytes: The number of bytes to replace with value
// Returns: None
// Description: Replaces the data in a memory block from pPtr to
// (pPtr + bytes) with value.
//////////////////////////////////////////////////////////////////////
void MemSet(void *pPtr, char value, size_t bytes)
{
unsigned int counter = 0; // Loop counter
char *pAddress = (char*)pPtr; // Char pointer to memory block
// If the address doesn't point to anything
if(pAddress == NULL)
{
return;
}
// If value is less than space or greater than tilde, and if value
// is not NULL and not new line
if(((value < 0x20) || (value > 0x7E)) && ((value != '\0') && (value != '\n')))
{
return;
}
// For each address to be set as value
for(counter = 0; counter < bytes; counter++)
{
*pAddress = value;
pAddress++;
}
}
// End MemSet
With this method, I can only hope realloc doesn't free the memory before returning NULL.
The reason for doing it the first way was to prevent data lose. Lets say the user types in a thousand characters. Lets also say that at the thousand character mark, pString needs more memory to store additional characters. However, for some reason it was unable to allocate the additional space. With the first method, the first one thousand characters are still in memory and can still be used by the rest of the program. With the second method, I'm not sure what will happen to the first one thousand characters. That depends on whether or not realloc frees pstring before returning NULL.
How do I find out exactly what realloc does?
According to
C++ reference:
Quote:
|
If the function failed to allocate the requested block of memory, a NULL pointer is returned, and the memory block pointed to by argument ptr is left unchanged.
|
So that is good news, but I will have to keep that in mind when I use realloc in the future. Should it fail, I need to ensure I have the ability to free the original memory.