#define ROWS 2
#define COLS 3
typedef int pArr[COLS];
void GetMatrix(pArr* M ,int rows , int cols )
{
for(int i = 0; i < rows; i++)
for(int j = 0; j < cols; j++)
scanf("%d", &(M[i][j]));
}
int main()
{
int M[ROWS][COLS] = {{100, 101, 102},{103, 104, 105}};
printf("before\n");
for(int i = 0; i < ROWS; i++){
for(int j = 0; j < COLS; j++)
printf("%d ", M[i][j]);
printf("\n");
}
printf("enter values\n");
GetMatrix(M, ROWS, COLS);
printf("after\n");
for(int i = 0; i < ROWS; i++){
for(int j = 0; j < COLS; j++)
printf("%d ", M[i][j]);
printf("\n");
}
}
Выход:
before
100 101 102
103 104 105
enter values
0 1 2 3 4 5
after
0 1 2
3 4 5
Внутри функции M
является указателем на массив из трех целых чисел. M[i]
- это i-й массив, а M[i][j]
- это j-й элемент в i-м массиве. &
оператор передает свой адрес в scanf, который с радостью хранит там прочитанное число.
Вот что происходит, когда вы заменяете scanf("%d", &(M[i][j]));
на scanf("%d",*(M+cols*i+j))
:
for(int i = 0; i < rows; i++)
for(int j = 0; j < cols; j++)
scanf("%d", *(M + cols * i + j));
чик ..
before
100 101 102
103 104 105
enter values
0 1 2 3 4 5
after
0 101 102
1 104 105
- На первой итерации внутреннего цикла i и j равны нулю, а
*(M+cols*i+j)
становится *M
. Поскольку M - указатель на массив, разыменование дает массив целых чисел, который превращается в указатель на целое число. Таким образом, первое число сохраняется в правильном месте.
- Во второй итерации внутреннего цикла j равно 1. Таким образом, оно равно
*(M + 1)
. Здесь указывается арифметика указателя. Поскольку M является указателем на массив из трех целых чисел, (M + 1)
является указателем на массив из трех целых чисел, который указывает на второй массив; разыменование дает массив, который распадается на указатель, а scanf записывает туда второе число.
- На третьей итерации j равен 2, а
(M + 2)
указывает на третий (несуществующий) массив, и scanf записывает введенный номер в эту ячейку памяти в стеке, тем самым разрушая стек.
- И так далее, и тому подобное. Он пытается записать каждое третье целое место, оставляя наши предварительно инициализированные значения (101, 102, 104, 105) нетронутыми.
Если вы хотите использовать указатели, вы можете привести M
в качестве указателя на int как в:
scanf("%d", (int*)M + cols * i + j);
После того, как вы произвели M как int*
, арифметика указателя добавляет 1 * sizeof(int)
к начальной позиции вместо 1 * sizeof(array of three ints)
, что дает желаемый результат.
Обновление - ответ на комментарий, который я получил в Facebook.
Рассмотрим фрагмент:
int a = 10;
scanf("%d", &a);
printf("%d", a);
Scanf сохраняет введенное значение по указанному адресу - поэтому мы передаем указатель на int в качестве аргумента. printf печатает переданное значение - поэтому мы передаем int в качестве аргумента. Та же разница применяется здесь. ((int*)M + cols * i + j)
имеет тип int*
, указатель на целое число. Чтобы напечатать целое число в этом месте, мы должны разыменовать его используя оператор *
. Таким образом это становится:
printf("%d\t",*((int*)M + cols * i + j));
Да, и, кстати, вы бы получили более быстрый ответ, если бы вы разместили здесь свои сомнения вместо моего FB. В SO много хороших людей, готовых помочь повесить трубку - и я проверяю FB только по выходным.