casting char[][] to char** causes segfault?

后端 未结 3 596
忘掉有多难
忘掉有多难 2020-11-29 08:54

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

相关标签:
3条回答
  • 2020-11-29 09:16

    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."};
    
    0 讨论(0)
  • 2020-11-29 09:33

    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
    
    0 讨论(0)
  • 2020-11-29 09:39

    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.

    0 讨论(0)
提交回复
热议问题