Почему это генерирует отказ сегментации?

#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]);
}
7
задан N 1.1 31 March 2011 в 05:28
поделиться

6 ответов

Предположим, вы объявляете: int arr[ 10 ][ 20 ] ;
Какой тип у arr?
Вы можете подумать, что это 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 ints, contiguous)
             [0][0] [0][1]
arr + 1 --> int int int ... int (20 ints, contiguous)
             [1][0] [1][1][1]

10
ответ дан 6 December 2019 в 05:42
поделиться

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

4
ответ дан 6 December 2019 в 05:42
поделиться

Вот как выглядит 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 байта). Это неправильно.

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

7
ответ дан 6 December 2019 в 05:42
поделиться

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

6
ответ дан 6 December 2019 в 05:42
поделиться

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

#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]);
}
5
ответ дан 6 December 2019 в 05:42
поделиться

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

1
ответ дан 6 December 2019 в 05:42
поделиться
Другие вопросы по тегам:

Похожие вопросы: