LinuxQuestions.org
Did you know LQ has a Linux Hardware Compatibility List?
Go Back   LinuxQuestions.org > Blogs > rainbowsally
User Name
Password

Notices

Rate this Entry

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:
  • 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.
Anyone that has used strdup() or strcat() knows how convenient those are. But when it comes to memory allocation and reallocation for other kinds of data the lack of simple standard mechanisms for this often leads to duplicated code.

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;
}
BEGINNERS:

How would you write a function named (let's say)
Code:
stradd(char** s, const char* newstr);
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.
Posted in Uncategorized
Views 3915 Comments 1
« Prev     Main     Next »
Total Comments 1

Comments

  1. Old Comment
    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...
    You could use a template function to accept only pointer types:
    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 ntubski is offline
 

  



All times are GMT -5. The time now is 12:09 AM.

Main Menu
Advertisement

My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
identi.ca: @linuxquestions
Facebook: linuxquestions Google+: linuxquestions
Open Source Consulting | Domain Registration