Объясните как это работает

Рейтинг: 2Ответов: 3Опубликовано: 27.02.2023

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

int main()
{   
    cout << 4 + "0" << "\n";
    cout << 8 + "0" << "\n";
    cout << 12 + "0" << "\n";
    cout << 12 + "0" << "\n";
    cout << 16 + "0" << "\n";

    cout << "h"- "0" << "\n";
    cout << "e" - "0" << "\n";
    cout << "l" - "0" << "\n";
    cout << "l" - "0" << "\n";
    cout << "o" - "0" << "\n";
    cout << "g" - "0" << "\n";
    cout << "l" - "0" << "\n";



}

выводит введите сюда описание изображения

Ответы

▲ 4Принят

Вы столкнулись с неопределённым поведением. То, что вы видите, может меняться от компилятора к компилятору, и от компьютера к компьютеру.

В своём коде вы складываете и вычитаете не строки, а указатели на эти строки:

4 + "0";    // const char*
"h" - "0"   // ptrdiff_t

В первом случае вы получаете указатель на некоторый участок памяти. И пробуете вывести строку, начинающуюся с этого участка. Компьютер будет пытаться представить байты этой памяти как символы в некоторой кодировке до тех пор, пока не встретит нуль-терминатор.

Скорее всего, ваш компилятор поместил строки "0", "h", "e", "l", "o", "g" достаточно близко в памяти. Строка "0" находится раньше всех. И когда вы прибавляете к её указателю небольшое число, вы попадаете на строку "h".

Обратите внимание, что я говорю именно строки, не символы. Потому что "h" содержит 2 символа — 'h' и '\0'. Последний как раз и представляет собой нуль-терминатор. И если вы попадаете указателем в 'h', то компьютер заканчивает печать на следующем же символе.

Во втором случае результат выражения неявно приводится к числу. И вы просто видите разницу между указателями.

▲ 2

Стало интересно повторить. Итак, на VC++ это достигается, например, так:

const char * m[] = {"0","h","e","l","o"};

int main()
{   
    cout << 4 + "0" << "\n";
    cout << 8 + "0" << "\n";
    cout << 12 + "0" << "\n";
    cout << 12 + "0" << "\n";
    cout << 16 + "0" << "\n";
}

Но! обязательно должна быть включена (вернее, не отключена) опция /GF (включить объединение строк только для чтения). Стоит скомпилировать как /GF-, как все "0" становятся разными и всё становится очень грустно...

▲ 1

Вы складываете и вычитаете указатели.

Вычитание работает правильно только если указатели указывают на элементы одного и того же массива (&a[i] - &a[j] == i - j), иначе вы получаете неопределенное поведение. В вашем примере они указывают на первые элементы разных массивов, поэтому поведение не определено.

А сложение работает так: &a[i] + x == &a[i + x]. Если индекс получившегося элемента выходит за границы массива, опять же происходит неопределенное поведение, как в вашем случае.


Почему вы видите именно такие числа? Потому что компилятор поместил все ваши строки рядом в памяти. Грубо говоря, там что-то типа массива const char x[] = {'0','\0','h','\0','e', ...}; Конкретный порядок строк стандартом не гарантируется, как и то, что они будут в памяти рядом, и то, будут ли повторяющиеся строки иметь разные адреса.

Здесь "h" превращается в &x[2], и т.п. Дальше подставляете результат в формулы выше. Можете поэкспериментировать с порядком элементов в массиве, чтобы получить такие же числа, как у вас сейчас.