arrayscmultidimensional-arraystructmalloc

storing all c stuct arrays in only one single memory segment obtained with malloc/calloc


Suppose I have a database in memory that I want to modify. Let's say the tables are phone information and items. The phone information consists of a name up to 99 characters and a character code. The items consist of an item description up to 99 characters and a character code. I made it that way so each struct entry is exactly 100 bytes.

My objective is to allocate only ONE segment of ram large enough to hold absolutely everything. This means only one calloc (or one malloc statement). That way, on program exit, I only need to call one free statement.

My program has no problem storing the scratchpag value, but when I try to change something inside a struct, I receive a segmentation fault.

The only fix I can think of which doesn't always work, especially when I do alot of declarations with numbers in brackets is by allocating structs locally like this:

allphones phones[100];
allitems items[100];

But how do I fix my program below so I can only use the memory region assigned to me (by malloc/calloc) to store all values in their proper formats without running into a segmentation fault?

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

#define scratchsize 10000
typedef struct {char code;char name[99];}allphones;
typedef struct {char code;char detail[99];}allitems;

int main(){
  int ramNeeded=scratchsize+(sizeof(allphones)*100)+(sizeof(allitems)*200)+1;
  char* ram=calloc(ramNeeded,1);
    if(!ram){
      printf("Cant allocate RAM\n");
      exit(-1);
    }
  //scratchpad is at start of ram from byte 0 - 9999
  char* scratchpad=ram; 
  //phones directory should go from byte 10,000 to byte 19,999
  allphones* phones=(allphones*)ram+scratchsize; 
  //items directory should go from byte 20,000 to 39,999
  allitems* items=(allitems*)ram+scratchsize+(sizeof(allphones)*100);
  strcpy(scratchpad,"Junk");
  phones[0].code='1'; //Segmentation fault here. why?
  strcpy(phones[0].name,"John"); 
  phones[0].code='1';
  strcpy(items[0].detail,"my item");
  free(ram);
  exit(0);
}

Solution

  • Your problem is the way you're doing pointer arithmetic. (allphones*)ram+scratchsize returns the address of scratchsize * sizeof(allphones) (you have a similar issue when calculating items a few lines later). The solution is to do the arithmetic then cast the final result.

    allphones* phones=(allphones*)(ram+scratchsize);
    allitems* items=(allitems*)(ram+scratchsize+(sizeof(allphones)*100));
    

    Crank up your compiler warnings since gcc complains about bad offsets. It wouldn't help you find the problem necessarily, but would show you that a problem existed.

    $ gcc scratch.c -O0 -ggdb3 -Wall -fsanitize=address -o scratch
    scratch.c: In function ‘main’:
    scratch.c:24:3: warning: ‘__builtin_memcpy’ writing 5 bytes into a region of size 0 overflows the destination [-Wstringop-overflow=]
       24 |   strcpy(phones[0].name,"John");
          |   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    scratch.c:11:13: note: at offset 1000001 into destination object of size 40001 allocated by ‘calloc’
       11 |   char* ram=calloc(ramNeeded,1);
          |             ^~~~~~~~~~~~~~~~~~~
    scratch.c:26:3: warning: ‘__builtin_memcpy’ writing 8 bytes into a region of size 0 overflows the destination [-Wstringop-overflow=]
       26 |   strcpy(items[0].detail,"my item");
          |   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    scratch.c:11:13: note: at offset 2000001 into destination object of size 40001 allocated by ‘calloc’
       11 |   char* ram=calloc(ramNeeded,1);