Hello and welcome to our community! Is this your first visit?
Register
Enjoy an ad free experience by logging in. Not a member yet? Register.
Results 1 to 3 of 3
  1. #1
    Regular Coder bobleny's Avatar
    Join Date
    May 2007
    Posts
    258
    Thanks
    3
    Thanked 11 Times in 11 Posts

    C: What do you think of my fancy user input function?

    One of the things that has bothered me in regards to C and command line input is you have to try and predict what the user is going to type and how much they are going to type.

    So, I made this:
    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 *pTempString = NULL;
    	char tempChar = '\0'; // Temporally stores the input character
    
    	if(pString == NULL)
    	{
    		return NULL;
    	}
    
    	// 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;
    			// Allocate enough memory to store the new string
    			pTempString = (char *) malloc(sizeOfString);
    
    			// If the memory hasn't been allocated
    			if(pTempString == NULL)
    			{
    				break;
    			}
    
    			// Clear the new memory block to prevent contamination
    			MemSet(pTempString, '\0', sizeOfString);
    			//memset(pTempString, 0, sizeOfString);
    
    			// Copy the string to the new memory block
    			CopyString(pString, pTempString);
    
    			// Clear the old memory block to prevent contamination
    			MemSet(pString, '\0', (sizeOfString - mallocSize));
    			//memset(pString, 0, (sizeOfString - mallocSize));
    
    			free(pString);// Free the old memory block
    			// Set the old pointer to the new memory block
    			pString = pTempString;
    			pTempString = NULL; // Destroy the temporary pointer
    		}
    
    		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
    So, theoretically, it doesn't matter what or how much the user types, GetUserInput will return a pointer to a memory block, large enough to handle the user input, containing a string of valid ascii characters.

    It also shouldn't blow up if the user runs out of memory. It will simply return with an partial string.

    I've noticed in my testing though, my console limits me to about 4095 characters.

    So what do you think? Is there a way to make it better or more efficient?
    --www.firemelt.net--
    * No good deed goes unpunished.
    * Cheer up, the worst has yet to come...

  • #2
    Rockstar Coder
    Join Date
    Jun 2002
    Location
    USA
    Posts
    9,074
    Thanks
    1
    Thanked 328 Times in 324 Posts
    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.

    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.
    Last edited by oracleguy; 06-17-2011 at 09:37 PM.
    OracleGuy

  • #3
    Regular Coder bobleny's Avatar
    Join Date
    May 2007
    Posts
    258
    Thanks
    3
    Thanked 11 Times in 11 Posts
    Quote Originally Posted by oracleguy View Post
    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 View Post
    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:
    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.
    Last edited by bobleny; 06-18-2011 at 02:41 AM.
    --www.firemelt.net--
    * No good deed goes unpunished.
    * Cheer up, the worst has yet to come...


  •  

    Posting Permissions

    • You may not post new threads
    • You may not post replies
    • You may not post attachments
    • You may not edit your posts
    •