Memory Management and Strings in C

C has an extremely flexible – some would say, dangerous – ability to allocate, use, and deallocate memory. Memory can be allocated statically, dynamically, or automatically in C. Static memory allocation occurs when variables are declared with the static keyword, and have scope set to the file; static memory also occurs when variables are declared outside the scope of any function, these variables may be used globally via the extern keyword. Dynamic memory allocation is the result of calling malloc() or another related function to get memory from the heap, and persists until free() is called. Automatic memory allocation occurs when a variable is declared within a function.

One of that nice things about static variables is that we can declare them within a function, and yet they persist beyond the lifetime of a function.

int useStaticVar(int iNum = 1);

int main()
{

	int i = 1;
	int iSum = 0;
	while (i++ < 10) {
		useStaticVar(i);
	}
	
	printf("Value is %d\n", useStaticVar());

    return 0;
}


int useStaticVar(int iNum) {
	static int iProduct = 1;
	iProduct *= iNum;
	return iProduct;
}

Remember, automatic variables are allocated on the stack. They are allocated and deallocated for us when the program enters and leaves their scopes.

Both automatic and static variables share the same problem – their size must be declared, and therefore known, at compile time. Dynamic memory allocation allows memory to be allocated by the program while it is running as needed. The malloc() function enables us to allocate variable amounts of memory that can be utilized via a pointer that is returned by the function.

#include <stdlib.h>
#include <string.h>

char * allocateString(int iLength);

int main()
{
	char * ourString = allocateString(256);
	strcpy_s(ourString, 256, "With sufficient thrust, pigs fly just fine.");

	printf("%s\n", ourString);

    return 0;
}

char * allocateString(int iLength) {
	char *str = NULL;
	if (iLength > 0) {
		str = (char*)malloc(iLength);
	}
	return str;
}

Note that to modify a pointer that is a parameter in a function, we must pass it as a pointer to a pointer, using **.

#include <stdlib.h>
#include <string.h>

#define STRING_LENGTH 256

void appendString(char ** szTarget, int iTotalLength, char *szSource);

int main()
{
	char *szString = (char *)malloc(STRING_LENGTH);
	strcpy_s(szString, STRING_LENGTH, "A strange game.The only winning move is not to play.");
	appendString(&szString, STRING_LENGTH, " All your base are belong to us.");

	printf("%s\n", szString);

    return 0;
}

void appendString(char ** szTarget, int iTotalLength, char *szSource) {
	int i = 0;
	int j = 0;
	//with single indirection, the pointer arithmetic would be
	//*(szTarget+i)
	//with double indirection, we have to deallocate once
	//just to get to where we would be with single indirection
	while (*(*szTarget+i) != '\0') { i++; }

	iTotalLength--;
	while ((i < iTotalLength) && (*(szSource + j) != '\0')) {
		*(*szTarget + i) = *(szSource + j);
		i++;
		j++;
	}

	*(*szTarget + i) = '\0';
}

Converting an integer to a string is fun, but there are three things to bear in mind. First, the characters that make up strings themselves have an integer value, which is set by the ASCII standard. To get the ASCII equivalent of an integer between 0-9, we need to add the integer value of the character 0 to the integer. Second, while strings in English are read left to right, numbers are read right to left. This means that the easiest thing to do for us will be to write the string equivalent of the integer backwards.

Finally, we should keep in mind that the value of any digit in a number is the digit itself multiplied by 10 for each place it is left of the starting position.  So, for example, the ‘9’ in 90210 itself is 9 * 10 * 10 * 10 * 10, or 90,000. The ‘2’, therefore, is 2 * 10 * 10, or 200.

#include <stdlib.h>
#include <string.h>

#define STRING_LENGTH 256

char * intToString(int iNum);

int main()
{
	char *s = intToString(8675309);

	printf("\n%s\n", s);

    return 0;
}

char * intToString(int iNum) {

	int iNumCopy = iNum;
	int iLength = 0;
	
	//numbers are read right from left, not left to right 
	//as words are
	do {
		iLength++;
		printf("iNumCopy = %d\n", iNumCopy);
		iNumCopy /= 10;
	} while (iNumCopy != 0);

	printf("Number has %d places\n", iLength);

	//one extra char needed 
	char *rString = (char *)malloc(iLength + 1);

	//terminate string
	*(rString + iLength) = '\0';

	while (--iLength >= 0) {
		printf("%d\n", iNum % 10);
		*(rString + iLength) = (iNum % 10) + '0';
		iNum /= 10;
	}

	return rString;

}

Please note that the example above does not, for reasons of simplicity, handle negative numbers. However, it would be easy enough to add that capability.

Incidentally, reversing a number is even easier. We just have to remember that multiplication and division are the opposites of each other.

#include <stdlib.h>

int reverseNumber(int iNum);

int main()
{
	int i = 73;
	int j = reverseNumber(i);

	int x = 501;
	int y = reverseNumber(x);

	printf("%d \t %d\n", i, j);
	printf("%d \t %d\n", x, y);

    return 0;
}

int reverseNumber(int iNum) {
	int iReturnNum = 0;
	while (iNum != 0) {
		//shift iReturnNum left one position
		//except when at the first position
		//as 0 * 10 = 0
		iReturnNum *= 10;
		iReturnNum += iNum % 10;
		//dividing by 10
		//removes the rightmost digit 
		//from an integer
		iNum /= 10;
	}

	return iReturnNum;
}
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s