views:

183

answers:

3

Ok my C is a bit rusty but I figured I'd make my next(small) project in C so I could polish back up on it and less than 20 lines in I already have a seg fault.

This is my complete code:

#define ROWS 4
#define COLS 4

char main_map[ROWS][COLS+1]={
  "a.bb",
  "a.c.",
  "adc.",
  ".dc."};

 void print_map(char** map){
  int i;
  for(i=0;i<ROWS;i++){
    puts(map[i]); //segfault here
  }
 }



int main(){
  print_map(main_map); //if I comment out this line it will work.
  puts(main_map[3]);
  return 0;
}

I am completely confused as to how this is causing a segfault. What is happening when casting from [][] to **!? That is the only warning I get.

rushhour.c:23:3: warning: passing argument 1 of ‘print_map’ from incompatible pointer type
rushhour.c:13:7: note: expected ‘char **’ but argument is of type ‘char (*)[5]’

Are [][] and ** really not compatible pointer types? They seem like they are just syntax to me.

+17  A: 

A char[ROWS][COLS+1] cannot be cast into a char**. The input argument of print_map should be

void print_map(char map[][COLS+1])

or

void print_map(char (*map)[COLS+1])

The difference being that a char** means to point to something that can be dereferenced like this:

   (char**)map
       |
       v
  +--------+--------+------+--------+-- ...
  | 0x1200 | 0x1238 | NULL | 0x1200 |
  +----|---+----|---+--|---+----|---+-- ...
       v        |      =        |
    +-------+   |               |
    | "foo" | <-----------------'
    +-------+   |
                v
             +---------------+
             | "hello world" |
             +---------------+

While a char(*)[n] is a points to a continuous memory region like this

   (char(*)[5])map
       |
       v
  +-----------+---------+---------+-------------+-- ...
  | "foo\0\0" | "hello" | " worl" | "d\0\0\0\0" |
  +-----------+---------+---------+-------------+-- ...

If you treat a (char(*)[5]) as a (char**) you get garbage:

   (char**)map
       |
       v
  +-----------+---------+---------+-------------+-- ...
  | "foo\0\0" | "hello" | " worl" | "d\0\0\0\0" |
  +-----------+---------+---------+-------------+-- ...
      force cast (char[5]) into (char*):
  +----------+------------+------------+------------+-- ...
  | 0x6f6f66 | 0x6c686500 | 0x77206f6c | 0x646c726f |
  +----|-----+---------|--+------|-----+------|-----+-- ...
       v               |         |            |
    +---------------+  |         |            v
    | "hsd®yœâñ~22" |  |         |       launch a missile
    +---------------+  |         |
                       v         v
               none of your process memory
                        SEGFAULT
KennyTM
That's kinda ugly though :/ or maybe I'm just spoiled by other languages..
Earlz
Thanks for updating your question with a explanation as to why :) Now I'm confused for which answer is better though
Earlz
+1 for the cute ascii-art and the pointer to array.
tristopia
+1  A: 

Looking at my code I realized that the amount of columns is constant, but it doesn't actually matter cause it's just a string. So I changed it so main_map is an array of strings(er, char pointers). This makes it so I can just use ** for passing it around also:

char *main_map[ROWS]={
  "a.bb",
  "a.c.",
  "adc.",
  ".dc."};
Earlz
+2  A: 

When you do this declaration:

char main_map[ROWS][COLS+1]={
  "a.bb",
  "a.c.",
  "adc.",
  ".dc."};

You create an array-of-arrays-of-char. An array-of-char is just a block of chars, and an array-of-arrays is just a block of arrays - so overall, main_map is just a whole heap of chars. It looks like this:

| 'a' | '.' | 'b' | 'b' | 0 | 'a' | '.' | 'c' | '.' | 0 | ... | 'd' | 'c' | '.' | 0 |

When you pass main_map to print_map(), it is evaluating main_map as a pointer to the first element of the array - so this pointer is pointing at the start of that block of memory. You force the compiler to convert this to type char **.

When you evaluate map[0] within the fuction (eg. for the first iteration of the loop), it fetches the char * value pointed to by map. Unfortunately, as you can see from the ASCII-art, map doesn't point to a char * - it points to a bunch of plain char s. There's no char * values there at all. At this point, you load some of those char values (4, or 8, or some other number depending on how big char * is on your platform) and try to interpret those as a char * value.

When puts() then tries to follow that bogus char * value, you get your segmentation fault.

caf
This was what I was wanting. Was an explanation not just a quick fix. Thanks for that :)
Earlz