3

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);
}
2
  • Aside: why the final +1 in int ramNeeded=scratchsize+(sizeof(allphones)*100)+(sizeof(allitems)*200)+1;? Its usefulness (so far undemonstrated) is unclear. Commented Apr 13 at 2:52
  • Since you allocate one big chunk at start you might as well use static storage duration. Calling realloc on this big chunk will also be inefficient. Commented Apr 13 at 9:41

2 Answers 2

2

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);

Sign up to request clarification or add additional context in comments.

2 Comments

Consider if scratchsize was not a multiple of the alignment needs of allphones. (allphones*)(ram+scratchsize) could result in UB. It needs to be something like (allphones*)(ram+ scratchsize + determine_pad(scratchsize, alignof(allphones))). Similar trouble for items.
the parantheses literally solved everything. thanks.
2

how do I fix my program below so I can only use the memory region assigned to me ?

One could post the correct calculation (using sizeof, watch for padding, ...), but there is a better way: don't calculate at all - let the compiler do it.

Consider creating one struct and avoid trying to do the pointer math (which was done wrong).

Tip: complex size math, type casting and complex pointer calculation hint that a better approach exists.

Tip: avoid naked magic numbers.

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

#define scratchsize 10000
#define PHONES_N 100
#define ITEMS_N 200
#define NAME_SZ 99
#define DETAIL_SZ 99

typedef struct {
  char code;
  char name[NAME_SZ];
} allphones;

typedef struct {
  char code;
  char detail[DETAIL_SZ];
} allitems;

int main() {
  struct {
    char scratchpad[scratchsize];
    allphones phones[PHONES_N];
    allitems items[ITEMS_N];
    char plus1;  // Unclear why OP wants an extra byte in the allocation.
  } *ram = calloc(1, sizeof *ram);

  if (!ram) {
    printf("Can't allocate RAM.\n");
    return EXIT_FAILURE;
  }

  char *scratchpad = ram->scratchpad;
  allphones *phones = ram->phones;
  allitems *items = ram->items;

  strcpy(scratchpad, "Junk");
  phones[0].code = '1';
  strcpy(phones[0].name, "John");
  phones[0].code = '1';
  strcpy(items[0].detail, "my item");

  free(ram);
  return EXIT_SUCCESS;
}

3 Comments

but wouldn't that use local memory (which one wouldn't have plenty of)? I'm trying to run all variables off dynamic memory
@MikeS: All the large arrays in this code are in dynamic memory. The only variables that aren't dynamically allocated are the pointers, and they'll likely be put in registers or optimized out.
@MikeS "but wouldn't that use local memory" --> No, other than local objects like in your example code: char *ram, char* scratchpad, allphones* phones, and allitems* items. Even these could be made non-local or removed. To be clear: struct { char scratchpad[scratchsize]; allphones phones[PHONES_N]; allitems items[ITEMS_N]; char plus1; } *ram is only a pointer in local memory.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.