Подтвердить что ты не робот

Вычитающие указатели

Мне было предложено описать, что делают эти строки кода для присвоения колледжа

int main() {
    int t1[] = {0,0,1,1,1}, t2[] = {0,0,1,1,1};
    int *p1 = t1, *p2 = t2;

    while (!*p1++ || !*p2++);
    cout << (p1-t1) << endl;
    cout << (p2-t2) << endl;
}

Взять его, 2 массива типа int создаются и заполняются значениями, 2 указателя создаются и указываются на каждый массив, тогда у меня возникают проблемы.

while (!*p1++ || !*p2++);

Мне говорят, что в то время как 0 переместить позицию *p1 в одно место или в то время как 0 переместить позицию *p2 на одно место, я действительно не уверен в этом предположении?

cout << (p1-t1) << endl;

Итак, тогда мы переходим на cout, теперь я беру это, я вычитаю позицию p1 из положения t1, где p1 было позиционировано while и t1 указывает на первую позицию в массиве. снова я мог быть совершенно не прав, я только узнаю о указателях, поэтому, пожалуйста, помните об этом, если я ошибаюсь в своих предположениях.

4b9b3361

Ответ 1

Цикл while на самом деле довольно ужасный. Я никогда не видел такой код в реальной жизни, и объявлял бы любого программиста, делающего это в реальной жизни безумным. Мы должны пройти этот шаг за шагом:

while (condition);

У нас есть инструкция while с пустым оператором (только ";" - это пустой оператор). Условие оценивается, и если оно истинно, выполняется оператор (который ничего не делает, потому что это пустой оператор), и мы начинаем все заново. Другими словами, условие оценивается повторно до тех пор, пока оно не станет ложным.

condition1 || condition2

Это оператор "или". Оценивается первое условие. Если это так, то второе условие не оценивается, а результат - "истина". Если оно ложно, тогда выполняется второе условие, и результат будет соответственно "истинным" или "ложным".

while (condition1 || condition2);

Это оценивает первое условие. Если это правда, мы начинаем все сначала. Если оно ложно, мы оцениваем второе условие. Если это так, мы начинаем все сначала. Если оба они ложны, мы выходим из цикла. Обратите внимание, что второе условие оценивается только в том случае, если первый - false. Теперь посмотрим на условия:

!*p1++
!*p2++

Это то же самое, что и * (p1 ++) == 0 и * (p2 ++) == 0. Каждое условие увеличивает p1 или p2 после того, как оно было оценено, независимо от результата. Каждое условие истинно, если * p1 или * p2 равно нулю и false в противном случае. Теперь мы проверяем, что происходит на каждой итерации:

p1 = &t1 [0], p2 = &t2 [0]
*p1++ == 0 is true, *p2++ == 0 is never evaluated, p1 = &t1 [1], p2 = &t2 [0].
*p1++ == 0 is true, *p2++ == 0 is never evaluated, p1 = &t1 [2], p2 = &t2 [0].
*p1++ == 0 is false, *p2++ == 0 is true, p1 = &t1 [3], p2 = &t2 [1].
*p1++ == 0 is false, *p2++ == 0 is true, p1 = &t1 [4], p2 = &t2 [2].
*p1++ == 0 is false, *p2++ == 0 is false, p1 = &t1 [5], p2 = &t2 [3].

t1 совпадает с & t1 [0]. p1 - ​​t1 == & t1 [5] - & t1 [0] == 5. t2 совпадает с & t2 [0]. p2 - t2 == & t2 [3] - & t2 [0] == 3.

Ответ 2

Вы правы в оценке t1, t2, p1 и p2.

while (!*p1++ || !*p2++);

Мне не нравится этот стиль кодирования, так как легко предположить, что программист по ошибке ошибочно разместил полуколону. Чтобы указать, что пустое тело действительно предназначено, пустое тело должно быть каким-то образом выделено (например, с комментарием, помещенным в отдельную строку или вместо него использовать фигурные скобки).

while входит в тело, пока условие true. Поскольку это выражение логическое или, как !*p1++, так и !*p2++ должно быть false до завершения цикла while. Это происходит, когда оба *p1++ и *p2++ становятся ненулевыми. Поскольку короткое замыкание с логическими или <сильными > (второе выражение не оценивается, если первое - true), то прогрессия p1 и p2 принимает в начале каждого итерации:

iter  p1     *p1    p2     *p2    condition
----  --     ---    --     ---    ---------
 0    &t1[0]  0     &t2[0]  0     !*p1++ is true,  !*p2++ not evaluated
 1    &t1[1]  0     &t2[0]  0     !*p1++ is true,  !*p2++ not evaluated
 2    &t1[2]  1     &t2[0]  0     !*p1++ is false, !*p2++ is true
 3    &t1[3]  1     &t2[1]  0     !*p1++ is false, !*p2++ is true
 4    &t1[4]  1     &t2[2]  1     !*p1++ is false, !*p2++ is false

Поскольку каждая итерация использует пост-приращение, p1 заканчивается значением &t1[5], а p2 заканчивается значением &t2[3].

Вычитание указателя внутри одного и того же массива измеряет расстояние между двумя указателями в терминах количества элементов массива. Имя массива, используемое в большинстве выражений, будет распадаться до значения, равного указателю на его первый элемент. Итак, t1 распадается на &t1[0], а t2 распадается на &t2[0].

Таким образом:

p1 - t1 => 5
p2 - t2 => 3

Ответ 3

Ключевое замечание здесь заключается в том, как оценивается выражение (a || b). Сначала оценивается выражение a. Если a возвращает true, b не оценивается, так как OR ничего с True есть True. Это называется короткозамкнутым.

Это помогает увеличить код следующим образом -

int main(void){
    int t1[] = {0,0,1,1,1}, t2[] = {0,0,1,1,1};
    int *p1 = t1, *p2 = t2;

    cout << *p1 << " " << *p2 << endl;
    cout << p1 << " " << p2 << endl;
    while (!*p1++ || !*p2++) { 
        cout << *p1 << " " << *p2 << endl;
        cout << p1 << " " << p2 << endl;
    }   
    cout << (p1-t1) << endl;
    cout << (p2-t2) << endl;
    return 0;
}

Вывод:

0 0
0x7fff550709d0 0x7fff550709f0
0 0
0x7fff550709d4 0x7fff550709f0
1 0
0x7fff550709d8 0x7fff550709f0
1 0
0x7fff550709dc 0x7fff550709f4
1 1
0x7fff550709e0 0x7fff550709f8
5 // Final p1 - t1
3 // Final p2 - t2

!*p1++ эквивалентен (!(*(p1++)). Это оператор post-increment. Он увеличивает указатель, но возвращает старое значение (до инкремента).

Выражение в цикле оценивается 5 раз.

  • В первой итерации p1 увеличивается. Поскольку текущее значение *p1 (до приращения) равно 0, a ! of 0 возвращает 1. Из-за короткого замыкания остальная часть выражения не оценивается. Таким образом, увеличивается только p1.

  • То же самое происходит в следующем цикле.

Теперь мы имеем p1 = t1 + 2 indices и p2 = t2.

  1. В третьей итерации текущее значение *p1 больше не 0. Таким образом, оба p1 и p2 увеличиваются.

  2. То же самое происходит и на четвертой итерации.

Обратите внимание, что в первых четырех итерациях либо p1, либо p2 указывает на 0 - поэтому not левой или правой стороны True и, следовательно, цикл while продолжается.

  1. На пятой итерации оба p1 и p2 увеличиваются, но поскольку ни одна из них не указывает на значение 0, цикл выходит.

Таким образом, p1 увеличивается в 5 раз, а p2 увеличивается в 3 раза.

Суммирование - p1 - t1 будет содержать 1 + число 0, появляющихся непрерывно в начале t1 и t2 (2 + 2 + 1). p2 - t2 будет оценивать до 1 + число 0, непрерывно появляющихся в начале t2 (2 + 1).

Ответ 4

Во-первых:

while (!*p1++ || !*p2++);

Это означает, что пока содержимое p1 равно 0, продолжайте циклическое добавление 1 в p1 каждый раз, пока оно не станет non-zero. После этого, когда содержимое p2 равно 0, продолжайте циклическое добавление 1 к p1 и p2 каждый раз. Если в любой момент содержимое p1 снова станет 0, логика повторится (я знаю, что это запутывает).

В основном в тесте стиля while(first || second) вторая часть тестируется только, если первая часть терпит неудачу. И указатель получает приращение независимо от того, проходит или не проходит тест.

Ваше предположение о (p1-t1) верное. Этот расчет дает вам число целых чисел между t1 и p1 (поскольку они являются указателями int). Поскольку t1 - начало массива, вычисление фактически дает вам индекс (смещение) в массив, на который указывает p1.

ПРИМЕЧАНИЕ # 1: Если p1 и t1 были char указателями, то вычитание их дало бы вам количество символов между ними. Если они были float указателями, то вычитание их дало бы вам количество поплавков и т.д. Арифметика указателя добавляет и вычитает в единицах типа данных, на которые они указывают.

ПРИМЕЧАНИЕ # 2: Строго говоря, t1 - это тип массива. Он сворачивает в указатель, когда вы используете его в контексте указателя. Например, в арифметике указателя или при назначении переменной указателю. Если это вас смущает, не беспокойтесь, в основном это просто работает как указатель, потому что компилятор делает преобразование автоматически, когда это подразумевается контекстом.

Ответ 5

Это отношение поможет вам лучше понять условия внутри цикла while:

arr[ i ] == * ( arr + i )

При выполнении вычитания указателя (если указатели имеют один и тот же тип), результатом является расстояние (в элементах массива) между двумя элементами.

Предположим, что p1 и p2 - оба указателя типа T*. Затем вычисленное значение:

( p2 - p1 ) == ( addr( p2 ) - addr( p1 ) ) / sizeof( T )

Ответ 6

Насколько вопрос заключается в том, что будет печатать на консоли, ответ 0 0 перед удалением; в конце цикла while.

Какое значение этого цикла?

Сначала вы используете OR, что означает, что если значение, на которое указывают p1 или p2, будет выполнено, будет выполняться блок 0. Итак, до тех пор, пока p1 не укажет на 3-й элемент (p1-t1), вы получите количество элементов, пересеченных в t1, тогда как (p2-t2) будет 0, поскольку (p1-t1) вернет true, поэтому второе условие не будет проверено. Когда p1 указывает на 1, тогда он начнет увеличивать p2 до тех пор, пока он не укажет на третий элемент t2 и не будет конца.

Это все, что у меня есть для вас, я считаю.