将char 转换为char **导致段错误?
好吧我的C有点生疏,但我想我会在C中制作我的下一个(小)项目,所以我可以对它进行修改,不到20行,我已经有了一个seg错误。
这是我的完整代码:
#define ROWS 4 #define COLS 4 char main_map[ROWS][COLS+1]={ "a.bb", "ac", "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:警告:从不兼容的指针类型传递'print_map'的参数1 rushhour.c:13:7:注意:预期'char **'但参数类型为'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" | +---------------+
虽然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 +---------------+ | | vv none of your process memory SEGFAULT
当你做这个声明时:
char main_map[ROWS][COLS+1]={ "a.bb", "ac", "adc.", ".dc."};
您创建一个char-array数组。 char-array只是一个字符块,而数组数组只是一个数组块 – 所以总的来说, main_map
只是一堆字符。 它看起来像这样:
| 'a' | '.' | 'b' | 'b' | 0 | 'a' | '.' | 'c' | '.' | 0 | ... | 'd' | 'c' | '.' | 0 |
当你将main_map
传递给print_map()
,它正在评估main_map
作为指向数组第一个元素的指针 – 所以这个指针指向那个内存块的开头。 您强制编译器将其转换为char **
类型。
当你在函数中评估map[0]
(例如,对于循环的第一次迭代),它会获取map
指向的char *
值。 不幸的是,正如你从ASCII-art中看到的那样, map
并没有指向char *
– 它指向一堆普通的char
。 那里根本没有char *
值。 此时,您将加载其中一些char
值(4或8,或其他一些数字,具体取决于您的平台上char *
大小),并尝试将其解释为char *
值。
当puts()
然后尝试遵循伪造的char *
值时,就会出现分段错误。
查看我的代码,我意识到列的数量是不变的,但它实际上并不重要,因为它只是一个字符串。 所以我改了它,所以main_map是一个字符串数组(呃,char指针)。 这使得我可以使用**
来传递它:
char *main_map[ROWS]={ "a.bb", "ac", "adc.", ".dc."};