Go Back   CodingForums.com > :: Computing & Sciences > Computer Programming

Before you post, read our: Rules & Posting Guidelines

Reply
 
Thread Tools Rate Thread
Enjoy an ad free experience by logging in. Not a member yet? Register.
Old 06-17-2011, 07:49 PM   PM User | #1
bobleny
Regular Coder

 
bobleny's Avatar
 
Join Date: May 2007
Posts: 256
Thanks: 3
Thanked 11 Times in 11 Posts
bobleny is on a distinguished road
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...
Get Firefox!
bobleny is offline   Reply With Quote
Old 06-17-2011, 08:35 PM   PM User | #2
oracleguy
Rockstar Coder


 
Join Date: Jun 2002
Location: USA
Posts: 9,043
Thanks: 1
Thanked 322 Times in 318 Posts
oracleguy is a jewel in the roughoracleguy is a jewel in the roughoracleguy is a jewel in the rough
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.
__________________
OracleGuy

Last edited by oracleguy; 06-17-2011 at 08:37 PM..
oracleguy is offline   Reply With Quote
Old 06-18-2011, 01:38 AM   PM User | #3
bobleny
Regular Coder

 
bobleny's Avatar
 
Join Date: May 2007
Posts: 256
Thanks: 3
Thanked 11 Times in 11 Posts
bobleny is on a distinguished road
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:
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.
__________________
--www.firemelt.net--
* No good deed goes unpunished.
* Cheer up, the worst has yet to come...
Get Firefox!

Last edited by bobleny; 06-18-2011 at 01:41 AM..
bobleny is offline   Reply With Quote
Reply

Bookmarks

Jump To Top of Thread


Thread Tools
Rate This Thread
Rate This Thread:

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT +1. The time now is 10:26 PM.


Advertisement
Log in to turn off these ads.