2015-02-24 2 views
3

Мы можем передать массив 2d как один указатель, а также двойной указатель. Но во втором случае выход не так, как ожидалось. Итак, что не так во втором коде?При прохождении 2-мерного массива в зависимости от того, какая разница между одним указателем и двойным указателем?

Метод 1:

#include <stdio.h> 
void print(int *arr, int m, int n) 
{ 
    int i, j; 
    for (i = 0; i < m; i++) 
     for (j = 0; j < n; j++) 
     printf("%d ", *((arr+i*n) + j)); 
} 

int main() 
{ 
    int arr[][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}; 
    int m = 3, n = 3; 
    print((int *)arr, m, n); 
    return 0; 
} 

Выход:

1 2 3 4 5 6 7 8 9 

Способ 2:

#include <stdio.h> 
void print(int *arr[], int m, int n) 
{ 
    int i, j; 
    for (i = 0; i < m; i++) 
     for (j = 0; j < n; j++) 
     printf("%d ", *((arr+i*n) + j)); 
} 

int main() 
{ 
    int arr[][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}; 
    int m = 3; 
    int n = 3; 
    print((int **)arr, m, n); 
    return 0; 
} 

Выходной:

1 3 5 7 9 3 0 -1990071075 0 
+1

«Мы можем передать массив 2d как один указатель, а также двойной указатель» - вы можете передать его в виде четырехкратного указателя на «char». Это не значит, что это хорошая идея или определенное поведение для использования (а это не так, и его нет). То, что hard-cast to 'int **' и неспособность кода компилироваться без него, должны намекать на то, что вы так злитесь. «Int **» является указателем на указатель на 'int'. * * * * Преобразование' arr' в указатель в качестве выражения является 'int (*) [3]'. Эти типы * не эквивалентны *. Массивы не являются указателями, независимо от того, к чему вы, возможно, верили. – WhozCraig

+1

Правило большого пальца в C состоит в том, чтобы избежать 2D-массивов. Просто используйте 1D-массив и получите доступ к нему как 'a [i * width + j]' ... –

+0

Но @BasileStarynkevitch в моем проекте мне нужно передать 2D-массив для работы, поэтому я не могу избежать 2D-массива –

ответ

5

Первый из них не определено поведение: Accesing a 2D array using a single pointer.

Второй просто неправильно, вы не можете передать 2D массив (arr[][3]) на массив указателей int (*arr[]), посмотрите на Correct way of passing 2 dimensional array into a function:

void print(int *arr[], int m, int n) 

Должно быть

void print(int arr[][3], int n) /* You don't need the last dimesion */ 

или

void print(int (*arr)[3], int n) /* A pointer to an array of integers */ 

Но таким образом столбец в arr [] [3] должен быть определен глобально. Разве нет любое другое обходное решение?

Под C99 вы можете использовать (массив переменной длины) VLA в:

void print(int rows, int cols, int arr[rows][cols]) 
+2

Исправьте меня, если я ошибаюсь, но, как я его вижу, он рассматривает матрицу как линеаризованный массив (вычисление индексов) без фактической индексации, что я считаю вполне законным (случай frist). – HighPredator

+0

Но таким образом столбец в arr [] [3] должен быть определен глобально. Разве нет другого решения? –

+2

@PankajMahato, под C99 вы можете использовать [VLA's] (http://en.wikipedia.org/wiki/Variable-length_array) –

1

Alter Mann является правильным, но главная проблема в методе 2 этот код:

*((arr+i*n) + j) 

С arr теперь тип int *arr[], размер элемента sizeof(int *), а не sizeof(int) как в первом случае. Поэтому, когда f.e. arr = 0, то arr + 1 соответствует 0 + sizeof(int*) вместо 0 + sizeof(int) как в первом случае. Она будет работать хорошо, если arr отлили в (int*) как:

*(((int *)arr+i*n) + j) 

TL; DR вы прыгая через массив по размеру указателя, а не целый размера.

Мое личное предложение использовать указатели, но доступ к нему как массив:

int *arr[]; 
return arr[i][j]; 

Это работает каждый раз, в отличие от арифметики указателей, которые могут укусить вас с размером шага, как он сделал с вашим делом ,

+0

Просто для того, чтобы сделать вещи более ясными: это UB, потому что результат типа указателя (из примера OP) после арифметики указателя отличается от int *, поэтому требуется бросок? – HighPredator

+0

Это не UB, это просто чтение вне пределов. В X86_64 программа считывает память на позициях '[0, 8, 16, 24, 32 ...]' вместо '[0, 4, 8, ...]', потому что размер шага отличается. Чтобы проиллюстрировать это, попробуйте напечатать: '(int *) NULL + 1' и' (int) 0 + 1' и посмотреть, как он отличается. –

+0

Нет ничего плохого в '* ((arr + i * n) + j)' в методе 1 (кроме этого сложнее читать, чем 'arr [i * n + j]'. Вы говорите о методе 2 или о том, что ? –

 Смежные вопросы

  • Нет связанных вопросов^_^