Почему это вызывает ошибку сегментации? - PullRequest
7 голосов
/ 15 марта 2010
#include<stdio.h>
void foo(int **arr) {
    arr[1][1]++;
}

main() {
    int arr[20][20];
    printf("%d\n",arr[1][1]);
    foo((int**)arr);
    printf("%d\n",arr[1][1]);
}

Ответы [ 6 ]

10 голосов
/ 15 марта 2010

Предположим, вы заявляете: int arr [10] [20];
Какой тип обр?
Вы можете подумать, что это int **, но это неверно.

На самом деле он имеет тип int (*)[20], когда распадается (например, когда вы передаете его функции);
Затухание массива применяется только один раз.

Подробности здесь


Теперь рассмотрим следующее,

#include<stdio.h>
#include<stdlib.h>
void foo(int arr[][20]) {
  arr[1][1]++;
}
main() {
  int (*arr)[20];
  arr = malloc(sizeof(int (*)[]) * 2); //2 rows & malloc will do implicit cast.

  printf("%d\n",arr[1][1]);
  foo(arr);
  printf("%d\n",arr[1][1]);
}

Вывод:

$ gcc fdsf.c && ./a.out
0
1


arr и arr + 1 указывают на массив из 20 целых чисел.

arr + 0 -> int int int ... int (20 дюймов, смежные)
[0] [0] [0] [1]
arr + 1 -> int int int ... int (20 дюймов, смежные)
[1] [0] [1] [1]

7 голосов
/ 15 марта 2010

Вот как int[2][2] выглядит в памяти:

int[2] int[2]

То есть, за массивом сразу же следует другой массив.

Вот как int[2] выглядит в памяти:

int int

То есть, за int сразу же следует другое int.

Итак, вот как выглядит int[2][2] в памяти:

int int int int
     ^       ^
     |       |___ this is arr[1][1]
     |
     |____ this is p[1], assuming sizeof(int*) == sizeof(int)

Если вы приведете arr к int**, я назову результат p. Тогда это указывает на ту же память. Когда вы делаете p[1][1], вы не получаете arr[1][1]. Вместо этого программа читает значение на p[1], корректирует его на величину типа int и разыменовывает его . Если во втором int содержится, скажем, значение «21», то вы только что попытались разыменовать указатель «25» (если int равно 4 байта). Это не правильно.

Массивы - это не то же самое, что указатели, а двумерные массивы определенно не то же самое, что указатели на указатели.

6 голосов
/ 15 марта 2010

Поскольку foo ожидает указатель на указатель на int, а вы передаете ему указатель на массив из 20 int. Применение этого не изменит тот факт, что это не правильный тип.

5 голосов
/ 15 марта 2010

Если вы измените его следующим образом, вы получите ожидаемый результат:

#include<stdio.h>
void foo(int arr[][20]) {
    arr[1][1]++;
}

int
main() {
    int arr[20][20];
    arr[1][1] = 1;
    printf("%d\n",arr[1][1]);
    foo(arr);
    printf("%d\n",arr[1][1]);
}
4 голосов
/ 15 марта 2010

foo нужно знать размер массива (ну, по крайней мере, второе измерение массива, первое не нужно), иначе он не может выполнить необходимую арифметику указателей для [1][1].

1 голос
/ 15 марта 2010

Проблема в том, что int arr[20][20] для 2d массива означает, что этот массив хранится как 1d массив, а строки хранятся одна за другой. когда вы выполняете индексирование до int **arr, вы фактически берете 2-й элемент из первой строки массива, затем разыменовываете его и получаете первый элемент там.

...