Ok my C is a bit rusty but I figured I'd make my next(small) 开发者_StackOverflow中文版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.
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
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 function (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.
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."};
精彩评论