char *pname = "Eric";
pname = "Alexander"; //No problemo
No, that's a BIG problemo. You got lucky, and the program didn't crash, but you should not use pointers this way. When you point a pointer at a string literal like that, don't attempt to modify it or even repoint it. It's const, for all practical purposes. This is so important to understand.
One last thing, and that's about calling malloc and other memory allocation functions. Believe it or not, they can lead to A LOT of memory fragmentation. This is not a problem for small programs, but when you start playing with more memory, you will one day get a big byte in the *** when malloc returns NULL! So what's my point, you say? At some point you should consider large scale allocation and management schema. C++ is perfect for the job because of it's many useful constructs. So how about a simple example?
For a given program, guestimate how much total memory you'll need. Allocate that much ( but perhaps a bit more, right?) straight from a special class object.
Code:
class Memory {
unsigned char *heap;
unsigned char *current;
double heapsize;
double remaining;
bool bailout;
public:
Memory( double initial_size, bool yes_or_no = true )
:heapsize(initial_size), remaining( initial_size ),
current(heap), bailout( yes_or_no )
{
heap = (unsigned char*)malloc(sizeof(unsigned char));
if( heap == NULL )
{
if(bailout)
{
cerr << "Not Enough Memory!!!";
abort();
} //...otherwise...
heap = NULL;
heapsize = remaining = 0;
}
current = heap;
memset(heap, 0, heapsize);
}
void * malloc( int amount )
{
if( amount > remaining + 1)
{
if( remaining == 0 ) return NULL;
amount = remaining;
}
amount++; //...to advance to the next block...
void * chunk = (void*)current;
current += amount;
remaining -= amount;
return chunk;
}
char * cMalloc( int amount )
{
return (char*) Memory::malloc( amount );
}
int * iMalloc( int amount )
{
return (int*) Memory::malloc( amount * sizoef(int) );
}
~Memory()
{
if( heap != NULL )
{
free(heap);
heap = NULL;
}
}
};
In main, the calling would be familar:
Code:
Memory mem( 1000000 ); //...get a MB...
int main()
{
char * str = mem.cMalloc(100);
int * array = mem.iMalloc(200);
Employee *emps = (Employee*)mem.malloc( sizeof(Employee) * 10 );
//...an array of 10 Employees...
return 0;
}
//...destuctor is automatically called, hey, that's garbage collection!
So why go to all the trouble? Simple. First, we can ensure we have enough "startup memory" before going to all the trouble of setting up the program, only to crash 15 minutes later because the system says "no", for instance. But most importantly, we can very easily extend this class to do defragmentation too (automatically, of course, perhaps in our own "free()" method). Third, we have an automatic garbage collector, so we don't have to free up the memory when the program is done ( or crashes for some other reason, etc).