Simple C: Filling in the blanks - A simple memory allocator/reallocator.
Posted 01-23-2012 at 07:32 AM by rainbowsally
No memdup()? No memcat()? No prob.
Today's Features:
Here's a couple of simple C/C++ routines you can add to your snippets library if you are tired of complicated memory allocation and reallocation schemes.
[Don't have gcc/g++ yet? The gnu compiler and its dependencies is about 67 megs of the best download time ever spent. I'm talking to newbies and to you assembler guys too. ;-) The libc docs is also very handy. Bookmark the "Function and Macro" index (an alphabetical listing) to get to the goodies fast.]
There are only two short routines of importance below. The main() function is just the tester.
BEGINNERS:
How would you write a function named (let's say)
that would do both reallocation of the string's base pointer and grow the memory for the expanded string?
Hint: All you'd need to pass as parameters is a pointer to the string, ala "&<string>" and the string to add. Lengths can be calculated using strlen(). (Note: The ampersand in "&<sting>" means "AddressOf".)
Gotchas: The initial string must be "char*" not "const char*" or it will segfault. Strings like 'this' (type: const char*, aka "string constant") are read-only. Strings like 'strdup("this")' (type: char* aka "string variable") are not.
:-)
ASM CODERS:
The tail code can be eliminated by setting the code up to do calls at the end of the code block. Use the -O2 optimization flag.
Type 'tofile objdump -d main -M intel-mnemonic' (remove "-M ..." if you prefer AT&T syntax) and find _memcat in the dump.
[tofile is a simple 'more'-ish alternative that views the dump with a kde-based editor. See the link near the top of the previous blog entry if you want it.]
You can further reduce overhead using the '--omit-frame-pointer' flag, which eliminates pushing and popping the frame pointer in each subroutine. You lose debug info but you gain what you gain in hand-written asm. GCL uses this flag, BTW, to expose the machine stack for pushing an popping lists.
Give gcc a chance. I think you'll be very impressed.
Today's Features:
- Simple C/C++ program and new memdup() and memcat() functions.
- C macros (spanning lines with backslash) to create 'lenient' aliases.
- Type casting in C/C++ to override sometimes overly strict syntax checks.
Here's a couple of simple C/C++ routines you can add to your snippets library if you are tired of complicated memory allocation and reallocation schemes.
[Don't have gcc/g++ yet? The gnu compiler and its dependencies is about 67 megs of the best download time ever spent. I'm talking to newbies and to you assembler guys too. ;-) The libc docs is also very handy. Bookmark the "Function and Macro" index (an alphabetical listing) to get to the goodies fast.]
There are only two short routines of importance below. The main() function is just the tester.
Code:
// main.c /* * Simple memory allocation, reallocation using built-in * libc calls. Based on strdup() and strcat() concepts. */ // use gnu99 so we can declare loop index inside for(...) block in tester // COMPILE: gcc -std=gnu99 main.c -o main #include <stdio.h> #include <string.h> #include <malloc.h> void dbg(){} // Try kdbg if you haven't yet. It's dynamite. // Add -g3 to the compile line if you want to enable it. ///////////////////////////////////////////////////////////////////////// // The calling function is responsible for free-ing the memory after use. // use xmalloc() and xrealloc() if you need safety over simplicity and speed. void _memcat(void** dest, int* destsize, const void* src, int srcsize) { int dsz = *destsize; *destsize += srcsize; *dest = realloc(*dest, dsz + srcsize); memcpy((char*)*dest + dsz, src, srcsize); // the call at the end eliminates an unnecessary call/return pair. // Asm coders, see note at the bottom. } void _memdup(void** dest, int* destsize, const void* src, int srcsize) { *dest = malloc(srcsize); *destsize = srcsize; memcpy(*dest, src, srcsize); } // NOTE: // Since C++ syntax is so strict we can create aliases that will cast the // input params to the type needed by the functions. This is a little risky // because even 'longs' can be case to char**, and crazy stuff like that so // consider deleting these and renaming the above without the underscore prefix // to renenable the more comprehensive error checks. But this CAN be done // and may make breaking the ice a little more palletable for folks coming // from other programming languages that aren't so strict. #define memcat(dest, destsize, src, srcsize) \ _memcat((void**)dest, destsize, (const void*)src, srcsize) #define memdup(dest, destsize, src, srcsize) \ _memdup((void**)dest, destsize, (const void*)src, srcsize) /////////////////////////////////////////////////////////////////////// // Example: int main(int argc, char** argv) { dbg(); const char* a = "this is a"; const char* b = "this is b"; // the memory descriptor is a base pointer and a length variable. // they could have been in a single object but we'll keep them // separate here so the parameter lists make clear what's going on, // especially for those new to C/C++. void* pa; int palen; int paptr; // a temp for test // here we go... duplicate string a plus its terminator using memory // blocks instead of strings, per se. memdup(&pa, &palen, a, strlen(a) + 1); printf("memdup()\n0x%0X: %s\n", pa, pa); // you can lie about the buffer length to truncate the block as far // as the descriptor is concerned. Don't truncate to < 0 though and // don't increase the length or the end of the block may be in // non-existent memory. palen--; // remove terminator (the lie) // add a five-byte string to the memory block memcat(&pa, &palen, " and ", 5); // add string b and it's null char terminator. memcat(&pa, &palen, b, strlen(b) + 1); // Let's have a look... printf("memcat()\n0x%0X: %s\n", pa, pa); free(pa); return 0; }
How would you write a function named (let's say)
Code:
stradd(char** s, const char* newstr);
Hint: All you'd need to pass as parameters is a pointer to the string, ala "&<string>" and the string to add. Lengths can be calculated using strlen(). (Note: The ampersand in "&<sting>" means "AddressOf".)
Gotchas: The initial string must be "char*" not "const char*" or it will segfault. Strings like 'this' (type: const char*, aka "string constant") are read-only. Strings like 'strdup("this")' (type: char* aka "string variable") are not.
:-)
ASM CODERS:
The tail code can be eliminated by setting the code up to do calls at the end of the code block. Use the -O2 optimization flag.
Type 'tofile objdump -d main -M intel-mnemonic' (remove "-M ..." if you prefer AT&T syntax) and find _memcat in the dump.
[tofile is a simple 'more'-ish alternative that views the dump with a kde-based editor. See the link near the top of the previous blog entry if you want it.]
You can further reduce overhead using the '--omit-frame-pointer' flag, which eliminates pushing and popping the frame pointer in each subroutine. You lose debug info but you gain what you gain in hand-written asm. GCL uses this flag, BTW, to expose the machine stack for pushing an popping lists.
Give gcc a chance. I think you'll be very impressed.
Total Comments 1
Comments
-
Code:
// NOTE: // Since C++ syntax is so strict we can create aliases that will cast the // input params to the type needed by the functions. This is a little risky // because even 'longs' can be case to char**, and crazy stuff like that...
Code:#ifdef __cplusplus template<typename T, typename U> inline void memcat(T** dest, int* destsize, const U* src, int srcsize) { _memcat((void**)dest, destsize, (const void*)src, srcsize) } #else #define memcat(dest, destsize, src, srcsize) _memcat(dest, destsize, src, srcsize) #endif
Posted 02-04-2012 at 09:18 PM by ntubski