How to avoid unnecessary cast and extraneous variable involving ptr to ptr to ptr

I have the following code that totally works 100% fine, no errors, compile or runtime. But it's damn ugly because I have to cast and use an extraneous variable when I'm sure there's a way to do without either.

structMSGB ***init_bstack(int Blk_Size,int Blks_N)
  {
  structMSGB **Mp=calloc(Blk_Size,Blks_N);
  void *M=(void*)Mp+sizeof(structMSGB*)+sizeof(structMSGB*)*Blks_N;

  structMSGB ***startStack=(structMSGB***)Mp++;

  for(int i=0;i<Blks_N;i++)
    {
    *Mp=M+(Blk_Size-sizeof(structMSGB*))*i-(i==1)*sizeof(structMSGB*);
    (*Mp)->blk_size=Blk_Size-sizeof(structMSGB)-sizeof(structMSGB*)-(i==0)*sizeof(structMSGB*);
    Mp++;
    }

  *startStack=(structMSGB **)Mp;
  return startStack;
  }

Specifically, it's the startStack variable that is pissing me off. I feel there should be a way of doing without it altogether. The return value is the address of a ptr to a ptr to a struct, i.e. It needs to return a ptr to a ptr to a ptr to a struct.

The result returned is the starting address of a memory block that is Blk_Size bytes in size and is composed of the following in order:

**ptr variable

table of ptrs of Blk_N length

sequential blocks of size Blk_Size - sizeof(ptr) except for the first block which is sizeof(ptr) smaller.

It's done this way to ensure that the entire memory allocation is used, no more and no less.

Answers


Your code invokes undefined behavior when it performs arithmetic on an expression of type void *. Some compilers will treat that as if void * were char *, and if your code in fact works then that's what's happening, but it's still wrong. And probably unnecessary, to boot.

Allow me to introduce you to pointer arithmetic. Given a pointer p of type some_type * and an integer value x, the expression p + x is equivalent to (some_type *) (((char *) p) + (x * sizeof(some_type)). By no coincidence whatever, that's also equivalent to &p[x]. That is, pointer arithmetic is defined in terms of the size of the pointed-to object.

The code you present performs a lot of casting and arithmetic with explicit object sizes that could be eliminated by relying on ordinary pointer arithmetic. For example, this ...

void *M = (void*) Mp + sizeof(structMSGB*) + sizeof(structMSGB*) * Blks_N;

... would be better written

structMSGB **M = Mp + 1 + Blks_N;

Similar applies elsewhere in your code.

More generally, good code rarely requires sizeof other than for memory allocation, and requires very few casts. Any time you find yourself writing a cast, you should ask yourself why, and be sure you have a good answer.

Update:

As for getting rid of variable startStack, it looks like you could do so at the cost of some additional arithmetic. You initialize it to the original value of variable Mp. You then increment Mp at total of Blks_N + 1 times. At the only points where you use startStack, then, its value is equal to Mp - (Blks_N + 1). You could use that expression instead of a variable. I certainly would not make such a change, though.


This is the greatly improved version that solves my problem (with help from @John Bollinger):

void *init_bstack(int Blk_Size,int Blks_N)
  {
  structMSGB **Mp=calloc(Blk_Size,Blks_N);
  Mp[0]=Mp[1]=(void*)&Mp[Blks_N+1];
  Mp[1]->blk_size=Blk_Size-sizeof(structMSGB)-sizeof(structMSGB*)sizeof(structMSGB*);
  for(int i=1;i<Blks_N;i++)
    {
    Mp[i+1]=Blk_Size+(void*)Mp[i]-8-(i==1)*8;
    Mp[i+1]->blk_size=Blk_Size-sizeof(structMSGB)-sizeof(structMSGB*);
    }
  return Mp;
  }

I use the return value thusly:

structMSGB ***MBp=init_bstack(4096,10);

I can then use *MBp to allocate chunks of memory using:

structMSGB *xb=*(--*Mp); 

And when I'm done I can return the chunk with:

*((*Mp)++)=xb;

MBp also contains a value I can later use to free the memory - free(MBp)

I think MBp needs to be a *** type as it contains the address of the calloc'd block, the first 8 bytes of which contains a ptr into a table of ptrs. This address is passed to allocate and free functions, so that the ptr at this address can be incremented or decremented accordingly, and also provide the chunk of memory requested.

The question now becomes, can the code be improved further? I'm casting with a void * when I really don't like to cast but in this case, I don't see any alternative. e.g. If I replace *Mp=(void*)(Mp+Blks_N+1); with *Mp=(Mp+Blks_N+1);, it works but gcc throws up a " assignment from incompatible pointer type" warning. Is there a better alternative to using (void*)?


Need Your Help

Phonegap File Download to device - Which device path to take?

android ios windows-phone-7 cordova

i was researching on how to download a file to the native filesystem via a URL with phonegap. I then read about this method:

Using Json with android app

java android asp.net sql json

I want to use json in order to import info from my ms-sql database to my android app.