1

I am trying to malloc a 1 d array of pointers following a 1d array of struct type entry.

While making each pointer points to the correspondent element of the 1d array, I hit the problem.

The pointers stores at the beginning of the memory and each pointer points to the address of each entry.

Below is a simple illustration:

--------------------------
ptr1|ptr2|ptr3|ptr4|ptr5
--------------------------
entry1
--------------------------
entry2
--------------------------
entry3
--------------------------
entry4
--------------------------
entry5
--------------------------


each pointer in the array of pointers points to entry 1- 5 accordingly.

Here is the code,

#include <stdio.h>

#define MAX_LAST_NAME_SIZE 16
#define TABLE_SIZE 131072  //2^17

typedef struct __PHONE_BOOK_DETAIL {   //not really matter in this question
    char firstName[16];
    char city[16];
    char zip[5];

} __PHONE_BOOK_DETAIL;

typedef struct __PHONE_BOOK_ENTRY {    // the size is 16 + 8 +8 =32byte
    char lastName[MAX_LAST_NAME_SIZE];

    __PHONE_BOOK_DETAIL *detail;

    struct __PHONE_BOOK_ENTRY *pNext;

} entry;

int main()
{
    int i;

    entry  **pHead;
    entry  *e; 
    pHead = (entry **) malloc(sizeof(entry *)*TABLE_SIZE + sizeof(entry)* TABLE_SIZE);

    for(i=0;i<TABLE_SIZE;i++){
        pHead[i]=  (pHead+ TABLE_SIZE) + sizeof(entry)*i;
        printf("i=%d , phead[i]=%p &phead[i]=%p, sizeof (entry)=%d sizeof(e)=%d \n",i, pHead[i],&pHead[i],sizeof(entry),sizeof(e));
        //pHead[i]->pNext=NULL;
    }
    return 0;
}

output:

i=0 , phead[i]=6b597010 &phead[i]=6b497010, sizeof (entry)=32 sizeof(e)=8
i=1 , phead[i]=6b597110 &phead[i]=6b497018, sizeof (entry)=32 sizeof(e)=8
i=2 , phead[i]=6b597210 &phead[i]=6b497020, sizeof (entry)=32 sizeof(e)=8
i=3 , phead[i]=6b597310 &phead[i]=6b497028, sizeof (entry)=32 sizeof(e)=8
i=4 , phead[i]=6b597410 &phead[i]=6b497030, sizeof (entry)=32 sizeof(e)=8
i=5 , phead[i]=6b597510 &phead[i]=6b497038, sizeof (entry)=32 sizeof(e)=8
i=6 , phead[i]=6b597610 &phead[i]=6b497040, sizeof (entry)=32 sizeof(e)=8
i=7 , phead[i]=6b597710 &phead[i]=6b497048, sizeof (entry)=32 sizeof(e)=8
i=8 , phead[i]=6b597810 &phead[i]=6b497050, sizeof (entry)=32 sizeof(e)=8
i=9 , phead[i]=6b597910 &phead[i]=6b497058, sizeof (entry)=32 sizeof(e)=8
                      ....

The program crashes when i = 16369. It seems to be memory access violation.

My CPU is 64 bit, so the pointer size is 8 bytes.

However, while I am expecting the size of entry is 32byte, you can see the increment of phead[i] is 256. Can anyone explain that?

Thanks!

mch
  • 9,424
  • 2
  • 28
  • 42
Craig Yang
  • 330
  • 3
  • 13
  • what is this?? sizeof(entry *)*TABLE_SIZE – Tsakiroglou Fotis Apr 12 '18 at 08:56
  • 3
    Your malloc code is nonsense. You probably want a 2D array instead. See [Correctly allocating multi-dimensional arrays](https://stackoverflow.com/questions/42094465/correctly-allocating-multi-dimensional-arrays). – Lundin Apr 12 '18 at 09:03
  • @Lundin one disadvantage of the practice shown in the link is memory fragment. That's why I use this method. – Craig Yang Apr 12 '18 at 09:18
  • 1
  • 2
    This is very dirty code, relying on sensible alignment, but you can set `e = (entry*)(pHead + TABLE_SIZE);` followed by `pHead[i] = &e[i];`. – Ian Abbott Apr 12 '18 at 09:25
  • @IanAbbott a very practical suggestion. it works! Thanks! :) – Craig Yang Apr 12 '18 at 09:27
  • You forgot to `#include `, this may be the cause of major troubles on 64 bit platforms where `int` is 32 bit. Didn't you get any compiler warnings? And please make clear what you're trying to achieve. – Jabberwocky Apr 12 '18 at 09:28
  • 1
    *one disadvantage of the practice shown* non-working code and undefined behaviour are both far greater disadvantages than a tiny bit of fragmentation. – n. m. could be an AI Apr 12 '18 at 09:31
  • @n.m. Thanks! for the suggestion regarding the fragmentation. but would you please be more specific regarding the 32*8=256? after following Ian Abbott's suggestion, it works. But I dont know why – Craig Yang Apr 12 '18 at 09:33
  • 2
    The original code had problems with pointer arithmetic. Remember that addition of an integer to a pointer is scaled by the size of the dereferrenced pointer type. – Ian Abbott Apr 12 '18 at 09:35
  • 3
    You are mixing size/offset in elements with size/offset in bytes. You need to read the chapter about pointer arithmetic again. – n. m. could be an AI Apr 12 '18 at 09:40
  • 1
    badly written spaghetti code. Bin it and rethink it. – 0___________ Apr 12 '18 at 09:52
  • Hi All, after reading the hint given by n.m. and Ian Abbott, I suddenly realize where the problem is Thanks! Also, thanks for the suggestion regarding separating the two allocations (an array of pointers and a data array). Putting all it together and messing everything around is definitely should be avoided. – Craig Yang Apr 12 '18 at 09:57
  • 1
    @CraigYang Yes, that's why you should read the _answer_ to the linked question, which shows how to do it proper while avoiding fragmentation. – Lundin Apr 12 '18 at 09:57

1 Answers1

2

I am expecting the size of entry is 32byte, you can see the increment of phead[i] is 256.
Can anyone explain that?

Unexpected bad pointer math.

//                                 + this adds by the size of *pHead 
// pHead[i] = (pHead + TABLE_SIZE) + (sizeof(entry) * i);

With compiler warnings fully enabled, OP may have received the below, hinting at the problem.

warning: assignment from incompatible pointer type [-Wincompatible-pointer-types] 

Tip: Save time, enable all compiler warnings.


There is also an alignment/size problem in that the 2nd half entry[] array may not align as needed. @Ian Abbott

A simple approach to avoid issues is to create a struct of the desired memory and allocate to that.

int main(void) {
  //  This is the memory layout that OP seeks
  typedef struct {
    entry *pHead[TABLE_SIZE];
    // Compiler will determine if padding needed between the 2 members.
    // OP's code reasonably assumed zero padding - yet best not to assume it.
    entry e[TABLE_SIZE];
  } all;

  all *a = malloc(sizeof *a);  // Simple!
  if (a == NULL) {
    return (EXIT_FAILURE);
  }

  entry **pHead = a->pHead;
  entry *e = a->e;

  for (size_t i = 0; i < TABLE_SIZE; i++) {
    pHead[i] = &e[i];
    printf(   // format corrected
        "i=%zu, phead[i]=%p &phead[i]=%p, sizeof (entry)=%zu sizeof(e)=%zu \n",
        i, (void *) pHead[i], (void *) &pHead[i], sizeof entry, sizeof e);
  }
  free(pHead); // Note pHead and `a` point to the same memory.
}

Output

i=0, phead[i]=0x600061fc8 &phead[i]=0x600061f90, sizeof (entry)=32 sizeof(e)=8 
i=1, phead[i]=0x600061fe8 &phead[i]=0x600061f98, sizeof (entry)=32 sizeof(e)=8 
i=2, phead[i]=0x600062008 &phead[i]=0x600061fa0, sizeof (entry)=32 sizeof(e)=8
...
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
  • Thanks for that! this post answers my question why the increment is 200 byte. Although I understand how to code it correctly after reading previous comments, I am still thinking why the wrong code behaves like this. This post is simple and clear! – Craig Yang Apr 12 '18 at 14:30
  • 1
    @CraigYang `(pHead + TABLE_SIZE) + (sizeof(entry) * i)` --> is like `((char *)pHead + sizeof(*pHead)*TABLE_SIZE) + (sizeof(*pHead)*sizeof(entry) * i)` - that is _pointer math_. – chux - Reinstate Monica Apr 12 '18 at 15:28