Ошибка сегментации при запуске программы C (putchar() и указатели на многомерный массив)

Я пытаюсь запустить следующий код на C:

#include "ex1_1.h"

void path(char **adj_mat, int u, int v)
{
        printf("test\n");
        char temp = *adj_mat[1];
        putchar(temp);
}

int main()
{
    int u = 5;
    int v = 5;
    char mat[5][5]={
    {'0', '1', '1', '1', '0'},
    {'0', '0', '0', '0', '1'},
    {'0', '0', '0', '0', '0'},
    {'0', '0', '0', '0', '0'},
    {'0', '0', '0', '0', '0'}
    };
    char** adj_mat = (char**)&mat;
    printf("Mtest\n");
    path(adj_mat, u, v);
    return 1;
}

и я получаю «Ошибка сегментации».

Я не знаю почему, я предполагаю, что это связано с тем, как я использую putchar() с указателями на многомерные массивы.

Спасибо.


person judith    schedule 28.04.2012    source источник
comment
char** adj_mat = (char**)&mat; бесполезно. mat уже является символом **.   -  person    schedule 28.04.2012
comment
Нет, это не так. массивы не являются указателями.   -  person asaelr    schedule 28.04.2012
comment
Но ведут себя так. Да, я должен был добавить const.   -  person    schedule 28.04.2012
comment
@ H2CO3: указатели на указатели не ведут себя как указатели на массивы!   -  person Anthales    schedule 28.04.2012
comment
@H2CO3 только в очень специфических условиях.   -  person asaelr    schedule 28.04.2012
comment
@Анталес прав. Извините, просто проглядел. Да, здесь есть 2 уровня косвенности.   -  person    schedule 28.04.2012


Ответы (3)


char[5][5] не является char **.

Объявите свою функцию path как одну из этих (они эквивалентны):

void path(char (*adj_mat)[5], int u, int v)
void path(char adj_mat[5][5], int u, int v)

и использовать его как

path(mat, u, v);

Обновление: Теперь о причине segfault.

mat — это 5-массив из 5-массивов char, который будет «распадаться» в указатель на 5-массивы из char при использовании в качестве выражения. Это означает, что mat можно использовать так, как будто это указатель на 5-массив. Обычно n-массив типа T будет распадаться на указатель типа T (обратите внимание, что T не распадается!).

Обратите внимание, что вы используете mat в качестве выражения, когда передаете его своей функции pass(). В C невозможно передать массив как есть в функцию, но он может только «получать» указатели.

Итак, в чем разница между указателем на указатель char и указателем на 5-массив char? Их тип. И их размеры.

Предположим, у вас есть указатель на указатель на char — назовем его p, и у нас есть указатель на 5-массив q. Короче: char **p и char (*q)[5].

Когда вы пишете p[i], это будет эквивалентно *(p+i). Здесь у вас есть арифметика указателя. p+i имеет значение (которое является адресом) p плюс i*sizeof(char *), потому что p указывает на char *.

Когда вы посмотрите на q, q+i будет иметь значение q плюс i*sizeof(char[5]).

Они почти всегда будут разными!


Обновить (реальный ответ сейчас): в вашем случае все еще хуже, поскольку вы «заставляете» char (*q)[5] «быть» char p** (посредством недопустимого приведения типов), поэтому в вашем вызове

char temp = *adj_mat[1];

adj_mat[1] посмотрит на 2-ю строку вашей матрицы и попытается интерпретировать ее значение как указатель (но это массив из 5!), поэтому, когда вы затем разыменуете через *adj_mat[1], вы приземлитесь где-нибудь в нирване - segfault!

person Anthales    schedule 28.04.2012
comment
Теперь я получаю: ex1_1.c: В функции «main»: ex1_1.c:26: предупреждение: передается аргумент 1 «пути» из несовместимого типа указателя - person judith; 28.04.2012
comment
Как вы объявили path и как вы его называете? Если бы вы сделали, как я сказал, у вас не должно быть проблем. - person Anthales; 28.04.2012
comment
@ user128877: Итак... ты запустил его? - person Anthales; 29.04.2012

Преобразование (char**)&mat имеет неопределенное поведение. &mat равно char(*)[5][5] (словами указатель на массив из 5 массивов по 5 символов), и вы не можете преобразовать его в указатель на указатель на char.

Я бы написал path как (при условии, что C99):

void path(char *arr,int u,int v) {
 char (*adj_mat)[v]=arr;
 ...
}
person asaelr    schedule 28.04.2012
comment
Небольшой комментарий: &mat — это char[5][5], так как addressof массива возвращает саму ссылку на массив (поскольку вы не можете передать массив по значению, только по ссылке). Насколько я знаю, &mat должен быть таким же, как и мат. - person ; 28.04.2012
comment
@ H2CO3, насколько я знаю, вы ошибаетесь. значение &mat такое же, как mat, но типы разные. &mat — это указатель на массив, и вы можете разыменовать его, как любой указатель. (рассмотрите mat[0] против &mat[0] - у них одинаковое отношение) - person asaelr; 28.04.2012
comment
вы правы в вопросе о типах, но их значение одинаково. - person ; 28.04.2012

Изменить строку

char temp = *adj_mat[1];

to

char temp = adj_mat[1];

Проблема в том, что в

*adj_mat[1];

вы пытаетесь разыменовать указатель на недопустимый адрес, а именно код ASCII «1» (я полагаю).

person Sven Hager    schedule 28.04.2012
comment
Неа. adj_mat — это МАССИВ МАССИВОВ. adj_mat[1] по-прежнему char *, а не char. - person ; 28.04.2012