приведение char [] [] к char ** вызывает segfault? - PullRequest
8 голосов
/ 24 мая 2010

Хорошо, мой C немного заржавел, но я решил, что сделаю свой следующий (маленький) проект на C, чтобы я мог отшлифовать его, и менее чем в 20 строках у меня уже есть ошибка сегмента.

Это мой полный код:

#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;
}

Я полностью сбит с толку относительно того, как это вызывает ошибку. Что происходит при касте от [][] до ** !? Это единственное предупреждение, которое я получаю.

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]’

Являются ли [][] и ** действительно несовместимыми типами указателей? Они кажутся мне просто синтаксисом.

Ответы [ 3 ]

36 голосов
/ 24 мая 2010

A char[ROWS][COLS+1] нельзя преобразовать в char**. Входной аргумент print_map должен быть

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

или

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

Разница в том, что char** означает указывать на то, что может быть разыменовано, как это:

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

В то время как char(*)[n] - это точка на непрерывную область памяти, подобную этой

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

Если вы рассматриваете (char(*)[5]) как (char**), вы получаете мусор:

   (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
3 голосов
/ 24 мая 2010

Когда вы делаете эту декларацию:

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

Вы создаете массив массивов символов. Массив char - это просто блок символов, а массив массивов - просто блок массивов, поэтому в целом main_map - это целая куча символов. Это выглядит так:

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

Когда вы передаете main_map в print_map(), он оценивает main_map как указатель на первый элемент массива - поэтому этот указатель указывает на начало этого блока памяти. Вы заставляете компилятор преобразовывать это в тип char **.

Когда вы оцениваете map[0] внутри функции (например, для первой итерации цикла), он выбирает значение char *, на которое указывает map. К сожалению, как вы можете видеть из ASCII-искусства, map не указывает на char * - он указывает на группу простых char s. Там нет char * значений там вообще. В этот момент вы загружаете некоторые из этих char значений (4, 8 или другое число в зависимости от того, насколько велико char * на вашей платформе) и пытаетесь интерпретировать их как char * значение.

Когда puts() затем пытается следовать этому поддельному char * значению, вы получаете ошибку сегментации.

1 голос
/ 24 мая 2010

Глядя на мой код, я понял, что количество столбцов постоянно, но на самом деле это не имеет значения, потому что это просто строка. Поэтому я изменил его так, чтобы main_map представлял собой массив строк (например, указатели на символы). Это делает его таким, чтобы я мог просто использовать ** для его передачи:

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