Хорошо мой C немного ржав, но я полагал, что сделаю свой следующий (маленький) проект в C, таким образом, я мог полировать, создают резервную копию на нем и меньше чем 20 строк в, у меня уже есть отказ seg.
Это - мой полный код:
#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;
}
Я полностью смущен относительно того, как это вызывает segfault. От чего происходит при кастинге [][]
кому: **
!? Это - единственное предупреждение, что я добираюсь.
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]’
[][]
и **
действительно не совместимые типы указателей? Они кажутся, что они - просто синтаксис мне.
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" |
+---------------+
Хотя a 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
Глядя на свой код, я понял, что количество столбцов постоянно, но на самом деле это не имеет значения, потому что это просто строка. Поэтому я изменил его так, что main_map - это массив строк (э-э, указатели на символы). Благодаря этому я могу просто использовать **
для его передачи:
char *main_map[ROWS]={
"a.bb",
"a.c.",
"adc.",
".dc."};
Когда вы делаете это объявление:
char main_map[ROWS][COLS+1]={
"a.bb",
"a.c.",
"adc.",
".dc."};
Вы создаете массив массивов символов. Array-of-char - это просто блок символов, а array-of-array - это просто блок массивов, так что в целом main_map
- это просто целая куча символов. Это выглядит так:
| 'a' | '.' | 'b' | 'b' | 0 | 'a' | '.' | 'c' | '.' | 0 | ... | 'd' | 'c' | '.' | 0 |
Когда вы передаете main_map
в print_map ()
, он оценивает main_map
как указатель на первый элемент массива - поэтому этот указатель указывает на начало этого блока памяти. Вы заставляете компилятор преобразовать это в тип char **
.
Когда вы оцениваете карту [0]
внутри функции (например, для первой итерации цикла), она выбирает значение char *
, на которое указывает карта
. К сожалению, как вы можете видеть из ASCII-арта, map
не указывает на char *
- он указывает на набор простых char
] с. Здесь вообще нет значений char *
. На этом этапе вы загружаете некоторые из этих значений char
(4 или 8 или какое-то другое число в зависимости от размера char *
на вашей платформе) и пытаетесь интерпретировать их как значение char *
.
Когда put ()
затем пытается следовать за этим фиктивным значением char *
, вы получаете ошибку сегментации.