0

Trying to allocate 2d array in shared memory. Execution returns segmentation fault during assignments (I think). Should I do the assignments in another way? This is my code:

...
#define KEYSM 46378 
#define X 10
#define Y 10

int main(){
 int i, j;
 int shm_id;
 int **addressArray;

 if((shm_id = shmget(KEYSM, sizeof(int[X][Y]), IPC_CREAT | 0666)) == -1){
   perror("shmget");
   exit(-1);
 }

 if((addressArray = (int **)shmat(shm_id, NULL, 0)) == (int **)-1){
   perror("shmat");
   exit(-1);
 }

 for(i = 0; i < X; i++){
   for(j = 0; j < Y; j++){
      if(i % 2 != 0)
         addressArray[i][j] = -1;
      else
         addressArray[i][j] = 0;
   }
 }
...
}
hawk
  • 28
  • 5
  • 2
    The code has several problems before it even compiles: The missing `)` in the `shmat` line, `i` and `j` undeclared and a `,` instead of `;` in the second `for`. Then you have 2 logic problems: the `if` in the loops is useless, you are overwritting the value anyhow and `int **addressArray` is not an 2d array `int (*addressArray)[Y]` can be used for that. – mch Dec 16 '20 at 10:08
  • Thanks for your reply. I did some errors writing the code here. I did not want to copy my entire code, so I decided to re-write it here, without check on a compiler if it's worked. So thanks for getting over my typos. – hawk Dec 16 '20 at 10:15
  • After fixing the typos is it related to: https://stackoverflow.com/questions/36890624/malloc-a-2d-array-in-c It does not matter if the memory block comes from `malloc` or `shmat` – mch Dec 16 '20 at 10:21
  • TLDR: `int **addressArray;` is ***not*** a "2-d array", no matter what you've been told. See [**Correctly allocating multi-dimensional arrays**](https://stackoverflow.com/questions/42094465/correctly-allocating-multi-dimensional-arrays) for a good explanation. – Andrew Henle Dec 16 '20 at 11:22
  • And using two lines like `addressArray = shmat(shm_id, NULL, 0); if ( addressArray == -1 ) ...` ***prevents*** creating bugs like the one in your original code. Cramming the assignment into the `if` statement is a bug-prone bad idea - and you've seen why. – Andrew Henle Dec 16 '20 at 11:24

2 Answers2

0

A segfault is a runtime error, but your code shouldn't even compile, look a this statement (paranthesis missing):

if ( ( addressArray = ( int** ) shmat ( shm_id, NULL, 0 ) == ( int** ) -1 )

And what if that assignment failed? Does perror terminate the process? No. But you still access the array below. Most likely the source of the seg fault.

As mch pointed out:

// i is not declared
for(i = 0; i < X; i++){
    // j is not declared, after j < Y, use semicolon instead of comma
    for(j = 0; j < Y, j++){
        // if the result is not zero you're doing an assignment
        if(i % 2 != 0)
            addressArray[i][j] = -1;
        // but immediately after you assign a zero, 
        // previous statement -> useless
        // 'else' is missing here
        addressArray[i][j] = 0;
    }
}
Erdal Küçük
  • 4,810
  • 1
  • 6
  • 11
  • Umm yeah actually the bug is caused by operator precedence, == over =. The OP likely didn't understand the compiler error and smacked in some harmful casts to hide it. – Lundin Dec 16 '20 at 09:59
  • You're right, I added a paranthesis, and an exit call, but still returns seg fault. – hawk Dec 16 '20 at 10:04
0

Your problem is in a – (your ?) – misunderstanding of the difference of a true 2D array, and a pointer→pointer→value indirection.

When you define a int **a this is interpreted as a pointer to another pointer, that then reference an int. Assigning a pointer obtained from an allocation function like malloc or shmat to such a double pointer, then as far as the semantics of C go, it expects this allocation of memory contain further pointers. So if you do a double dereference, and there's not a valid pointer there, it will fall flat on its face.

This misunderstanding is furthered by the fact, that in C you can validly write int a[X][Y]; and dereference it with a[i][j]. The key insight to understand this is, that the first "half", i.e. that with a array defined like that, a[i]… decays into a int* pointer, that points toward the 0th element in the i "column". The other half …[j] then dereferences this implicitly "appearing" pointer.

Multidimensional arrays in C are deceiving and I strongly discourage using them like that. Also you'll have a hard time to properly implement specific padding and row alignments with them, without jumping some really annoying arithmetic.

It's easier by far, to just write down the calculations explicitly, with the added benefit of having precise control of padding and alignment.

Suppose we want to create a 2D array of int, that shall be aligned to the sizes of long long

size_t const array_height = ...;
size_t const array_width = ...;
size_t const alignment = sizeof(long long);
size_t const row_size   = array_width * sizeof(int);
size_t const row_stride =
        alignment * ((row_size + alignment-1) / alignment);
size_t const array_size = array_height * row_stride;

int const shm_id = shmget(KEYSM, array_size, IPC_CREAT | 0666);
if( 0 > shm_id ){
    perror("shmget");
    exit(-1);
}

int *const array = shmat(shm_id, NULL, 0);
if( (void*)-1 == array ){
    perror("shmat");
    exit(-1);
}

for(size_t j = 0; j < array_height; ++j){
    int *const row = array + j * row_stride;
    for(size_t i = 0; i < array_width;  ++i){
        row[i] = (i % 2) ? -1 : 0;
    }
}
datenwolf
  • 159,371
  • 13
  • 185
  • 298