Приоритет, круглые скобки, указатели с итеративными функциями массива - PullRequest
0 голосов
/ 02 января 2019

Я учу себя немного C и наткнулся на упражнение, которое я хочу убедиться, что я полностью понимаю.В упражнении меня просят передать указатель на многомерный массив целых чисел и выполнить итерацию по массиву.Итак, я начал с функции печати, а затем перешел к одному из них, чтобы ввести массив для заполнения массива.Я перепробовал все виды вещей, но после нахождения небольшого количества кода на приведение типов и итерацию указателей у меня есть приведенная ниже программа, которая, кажется, работает, но я не совсем понимаю, что происходит.В моих комментариях у меня есть вопросы с пометкой 1-3, относящиеся к вопросам ниже кода.Я надеялся, что кто-то умнее меня сможет просветить меня.

//passing multi-Array to a simple print function
#include <stdio.h>

//simple print function prototype
void printMyArr(int (*pt)[4]);

int main()
{
    //declare and initialize a multi-array. I picked 4x4 and some arbitrary 
    //numbers for simplicity's sake

    int myArr[4][4] = { {12, 16, 19, 20}, 
                        {5, 99, 102, 200}, 
                        {12, 20, 25, 600},
                        {65, 66, 999, 1000} };

    //declare a pointer to an array of integers and an int for a loop counter
    int (*pt)[4], counter;

    //initialize the pointer
    pt = myArr;

    //for loop calling printMyArr function to iterate through arrays -- or this is what I understand it to mean
    for(counter=0; counter<4; counter++)
        printMyArr(pt++);   //<-------------Question 1
    return 0;
}

//function called to print array elements to the console
void printMyArr(int(*pt)[4])
{
    //declare a counter....and apparently another pointer
    int counter, *p;

    //initialize new pointer to old pointer and type cast array as int
    p = (int *)pt;          //<-------------Question 2

    //for loop to iterate through elements of array -- or this is what I understand it to mean
    for(counter=0; counter<4; counter++)
        printf("\n\n\n%d", *p++);  //<------Question 3
}

Вопрос 1: Здесь я передал указатель на функцию и продолжаю думать: «Что за цикл повторяется?».Правильно ли я считаю, что я увеличиваю указатель на каждый из первых элементов в каждом массиве (myArr[0][0], myArr[1][0], myArr[2][0], myArr[3][0])?Кроме того, я прав, предполагая, что синтаксис этой строки в сущности говорит: «Выполните функцию, передающую текущий указатель, и ТО, когда это будет сделано, увеличьте указатель.»?

Вопрос 2: Вот чтоменя больше всего смущает.После долгого копания я обнаружил, что этот бит работает правильно, и я понимаю, что так оно и есть, но почему?

Вопрос 3: Правильно ли я думаю, что я увеличиваю каждый элемент здесь?

Итак

1: передать указатель как назначено -> myArr[0][0], затем вывести значения в myArr[0][0], myArr[0][1], myArr[0][2] и myArr[0][3], затем увеличить указатель myArr[1][0]

2: передать указатель как назначено -> myArr[1][0], затем вывести значения в myArr[1][0], myArr[1][1], myArr[1][2] и myArr[1][3] с указателем приращения до myArr[2][0]

3: передать указатель в соответствии с назначением -> myArr[2][0], затем вывести значения в myArr[2][0], myArr[2][1], myArr[2][2] и myArr[2][3], увеличить указатель на myArr[3][0]

4: передать указатель в соответствии с назначением-> myArr[3][0] затем выведите значения в myArr[3][0], myArr[3][1], myArr[3][2] и myArr[3][3], увеличивая указатель до myArr[4][0], и если это так, на что указывает указатель, поскольку не должно бытьmyArr[4][0]?

Ответы [ 3 ]

0 голосов
/ 02 января 2019

Многофункциональный массив D хранится в памяти как непрерывный массив, поэтому, если у вас есть массив [2] [2], он выделит 4 непрерывных sizeof (int). Причина, по которой вам нужно определить массив [] [COL], состоит в том, чтобы сообщить компилятору, как должна выполняться арифметика указателей.

так что для Q1 - вы правы, каждый раз, когда вы увеличиваете, вы делаете это для (sizeof (int) * COL). поэтому каждый раз, когда вы двигаетесь на ряд.

Q2 - теперь для каждой строки, которую вы хотите вывести по одному значению int за раз, поэтому, когда у вас есть int * p-, это означает, что каждое приращение выполняется с помощью sizeof (int).

Q3 - приращение указателя, а не значения - это легко увидеть, если вы сделаете еще один отпечаток на своей основной.

Надеюсь, это поможет, удачи!

0 голосов
/ 03 января 2019

Просмотр в памяти массива 2D myArr будет выглядеть примерно так:

        myArr
         ---------------------
myArr[0] | 12 | 16 | 19 | 20 |
         ---------------------
myArr[1] | 5  | 99 |102 |200 |
         ---------------------
myArr[2] | 12 | 20 | 25 |600 |
         ---------------------
myArr[3] | 65 | 66 |999 |1000|
         ---------------------

Question 1: Here, I've passed the pointer to the function and I keep thinking, "What is this loop iterating over?". Am I correct in thinking that I am incrementing the pointer to each of the first elements in each array (myArr[0][0], myArr[1][0], myArr[2][0], myArr[3][0])? Also, am I correct in assuming that the syntax of this line is in essence saying: "Execute the function passing the current pointer and THEN when it's done, increment the pointer."?

Ответ на вопрос 1:

int (*pt)[4]

pt - указатель на массив целых чисел 4. Это утверждение

pt = myArr;

делает pt указывающим на первый массив 1D массива myArr 2D. Это похоже на:

pt = &myArr[0];

Воздействие обоих утверждений будет таким:

pt--------|
         \|/
         ---------------------
myArr[0] | 12 | 16 | 19 | 20 |
         ---------------------

Когда вы увеличиваете указатель, он постепенно увеличивается с размером объекта , на который указатель может указывать . Здесь

printMyArr(pt++);

сначала значение указателя pt, переданное в printMyArr(), а затем pt будет увеличено, и поскольку его тип равен int (*)[4], это означает, что он может указывать на объект, который представляет собой массив целых чисел 4, поэтому после увеличения pt будет указывать на адрес (то есть address pointing to + size of array of 4 int), т.е. на myArr[1] адрес.

Обратите внимание, что это:

for(counter=0; counter<4; counter++)
        printMyArr(pt++);

такой же, как этот:

for(counter=0; counter<4; counter++)
        printMyArr(&myArr[counter]);
                   ^^^^^^^^^^^^^^^

Question 2: This is what has me the most confused. After quite a bit of digging I found this bit to make it run right and I realize this is how it works, but why?

Ответ на вопрос 2:

Здесь

p = (int *)pt;   // pt is pointer to an array of `4` integer

если вы удалите приведение (int *), вы обнаружите, что компилятор выдает предупреждение об этом утверждении. Причина в том, что p и pt не являются совместимыми типами указателей. Тип p равен int *, тогда как тип pt равен int (*)[4]. Это несовместимое присвоение указателя работает, потому что адрес массива и адрес первого элемента массива численно совпадают, хотя их тип отличается . Вместо этого вы должны сделать

p = &(*pt)[0];

Из стандартов C # 6.5.2.1

Определение оператора нижнего индекса [] состоит в том, что E1 [E2] идентичен (* ((E1) + (E2)))

по этому правилу

&(*pt)[0] --> &(*((*pt)+(0))) --> ((*p)+(0)) --> *p

The operator & is used to get the address and the operator * is used for dereferencing. These operators cancel the effect of each other when used one after another.

Следовательно, это утверждение

p = &(*pt)[0];

совпадает с этим утверждением

p = *pt;

Question 3: Am I correct thinking that I am incrementing each element here?

Ответ на вопрос 3:

Да.
p - указатель, указывающий на первый элемент массива 4 integer, и его тип int *, то есть он может указывать на объект, который является int. Когда вы делаете *p++, это сначала разыменовывает p и получает значение по адресу, на который он указывает, тогда p будет увеличиваться и указывать на следующий элемент массива.

0 голосов
/ 02 января 2019

Вопрос 1: Здесь я передал указатель на функцию и продолжаю думать: «Что за цикл повторяется?». Правильно ли я считаю, что увеличиваю указатель на каждый из первых элементов в каждом массиве (myArr [0] [0], myArr [1] [0], myArr [2] [0], myArr [3] [ 0])?

больше или меньше. pt начинается с адреса myArr. Вы знаете, что в массиве 4 вещи, так что вы выполняете цикл 4 раза, увеличивая pt после обращения к нему (читайте ниже) каждый раз, чтобы перейти к printMyArr каждому из 4 элементов «верхнего уровня», «внешнего» массива. Затем printMyArr перебирает 4 элемента внутреннего массива, чтобы показать каждое число.

Кроме того, я прав, предполагая, что синтаксис этой строки по сути говорит: «Выполните функцию, передающую текущий указатель, и ТО, когда это будет сделано, увеличьте указатель».?

Функционально, да. Технически порядок операций выглядит следующим образом:

1) получить значение pt 2) приращение pt 3) вызвать функцию, передав предыдущее значение pt с шага 1

pt увеличивается как часть вызова pt++, но pt++ возвращает старое значение. В качестве аргумента для функции, pt++ должен быть оценен перед функцией, которую он передал для выполнения. Но время выглядит так же, как и вычисление непосредственно после запуска функции для вашего случая.

Давайте возьмем вопросы 2 и 3 вместе, потому что они оба являются частью ответа.

Вопрос 2: Это то, что меня больше всего смущает. После долгого копания я нашел этот бит, чтобы заставить его работать правильно, и я понимаю, что так оно и есть, но почему?

Вопрос 3: Правильно ли я считаю, что увеличиваю каждый элемент здесь?

p = (int *)pt;          
for(counter=0; counter<4; counter++)
    printf("\n\n\n%d", *p++);  

p хранит адрес целого числа. Вы устанавливаете его на адрес первого элемента внутреннего массива (так сказать, адрес самого внутреннего массива). Затем, зная, что у вас есть 4 элемента в массиве, вы сможете выполнить цикл 4 раза, выполнив аналогичную операцию постинкрементного указателя для p, которую вы делали с pt для внешнего массива.

Первая итерация, p совпадает с pt, адресом первого из 4 целочисленных значений в памяти. *p, разыменовывая указатель, получает первое целое число. *p++, из-за порядка операций (++ имеет наивысший приоритет и разыменование * ниже), возвращает целое число на p, оставляя p, указывая на следующий целочисленный адрес для следующего цикла.

Как правило, значений приведения в C следует избегать при любой возможности . Вам просто нужно сделать разыменование, чтобы указать p на набор целых чисел. pt содержит в памяти адрес одного из 4 адресов (внешний массив) непрерывных наборов из 4 целых чисел (внутренний массив). Значение в *pt является адресом «текущего» набора из 4 смежных целых чисел в памяти. Так что вы можете просто сказать,

int* p=*pt;

Который не может произвольно приводить тип и очень прост и понятен. (Спасибо @WhozCraig)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...