1

Почему в коде на С++

#include <iostream>
int main(){
    std::string E;
    E[0] = 'a';
    std::cout << E[0];
    std::cout << E;
}

вывод строки E пустой, хотя строка не должна быть пустой после присвоения значения первому элементу строки? И где найти информацию по этому поводу?

Harry
  • 221,325
Ilya
  • 13
  • 4
    Строка пуста, и у нее НЕТ первого элемента. По сути, это выход за пределы массива. Выведите дополнительно E.size() - вы увидите 0. Лучше уж писать E='a';... – Harry May 14 '21 at 04:09
  • да что же это такое. E='a'; не скомпилится. Но три плюса вверх поставили. – KoVadim May 14 '21 at 07:07
  • @KoVadim, https://ideone.com/MCBDzW – insolor May 14 '21 at 10:55
  • странно, у меня такое раньше не компилировалось.... пойду поищу этот проект – KoVadim May 14 '21 at 11:06

3 Answers3

1

Запустим такой код:

string E;
cout << E.size() << endl;

и увидим вывод - 0. Т.е. нет никаких элементов строки вовсе, и запись любого элемента пустой строки есть по сути выход за границы выделенной памяти и UB.

Как именно отреагирует на ваше самовольство программа - зависит от конкретного компилятора, реализации string, уровня оптимизации etc etc - на то это поведение и undefined...

Можно просто присвоить E = 'a';, можно E += 'a'; - словом, по-разному, но в рамках стандарта :)

Harry
  • 221,325
  • 1
    я бы не был столь категоричен. Чтение элемента по размеру строки это нормально. Вот с записью уже хуже. То есть, ещё раз, std::string s; std::cout << s[0]; - здесь все ок. – KoVadim May 14 '21 at 07:06
  • @KoVadim Если не ошибаюсь, не гарантируется, что в неинициализированной (не string E = "") строке указатель вообще куда-то указывает? он может быть и нулевым, нет? В стандарте сказано только size() is equal to 0, а о том, куда указывает data() - ничего не пишут. Тогда это может быть и nullptr, нет? а в какой мере нормально чтение ((char*)nullptr)[0]? (Еще раз - я говорю о теоретически возможном варианте реализации, а не о том, как обычно string реализован на практике.) – Harry May 14 '21 at 08:27
  • а какая разница, что там именно внутри. К тому же при чтении по индексу равному размеру может просто быть отдельная ветка, которая возвращает \0. – KoVadim May 14 '21 at 08:30
  • @KoVadim Да, в принципе есть такое... constexpr const_reference operator[](size_type pos) const; - Returns: *(begin() + pos) if pos < size(). Otherwise, returns a reference to an object of type charT with value charT(), where modifying the object to any value other than charT() leads to undefined behavior. Так что с замечанием о чтении соглашусь. – Harry May 14 '21 at 08:31
  • Если бы не это в стандарте - то разница может быть, и очень большая. "Может быть" и "стандарт гарантирует" - все же две большие разницы... – Harry May 14 '21 at 08:32
  • я знаю, что такое UB, я знаю, какие могут быть последствия. Остальные ошибки в "ответе" исправите? – KoVadim May 14 '21 at 09:06
  • @KoVadim по наличию терминального нуля в std::string есть вопрос – αλεχολυτ May 14 '21 at 11:49
1

У вас, как уже выше отметили, отсутствует инициализация строки. Поэтому на момент вызова она пуста. Цитирую сайт https://metanit.com/cpp/tutorial/2.16.php. Для инициализации строк можно использовать различные способы:

#include <string>
#include <iostream>

int main() { std::string s1; //пустая строка std::string s2 = "hello"; //hello std::string s3("welcome"); //welcome std::string s4(5, 'h'); //hhhhh std::string s5 = s2; //hello

std::cout &lt;&lt; s1 &lt;&lt; &quot;\n&quot;;
std::cout &lt;&lt; s2 &lt;&lt; &quot;\n&quot;;
std::cout &lt;&lt; s3 &lt;&lt; &quot;\n&quot;;
std::cout &lt;&lt; s4 &lt;&lt; &quot;\n&quot;;
std::cout &lt;&lt; s5 &lt;&lt; &quot;\n&quot;;
return 0;

}

  • у автора вопроса есть инициализация строки. Тем более Вы сами приводите такую инициализацию в своем примере. – KoVadim May 14 '21 at 07:06
  • @KoVadim я все таки имел ввиду инициализацию присвоением значения. У автора выше это нельзя назвать инициализацией в полной мере, а лишь объявлением. – Kinzheyev Madi May 14 '21 at 11:45
  • почему? и у него определение, а не объявление. – KoVadim May 14 '21 at 12:09
1

Изменение строки по индексу с помощью [] приводит к неопределённому поведению. Этот оператор предназначен для быстрой обработки строк. Если программист знает, что хочет.

А если не уверен, то надо использовать другую функцию at, которая следит за неправильным доступом к строке и вызовет исключение.

#include <iostream>
int main(){
    try {
        std::string E;
        E.at(0) = 'a';
        std::cout << E.at(0);
        std::cout << E;   }
    catch(std::exception const & ex){
        std::cerr<<"exception : " << ex.what()<<std::endl;
    }
}

проверяем :

exception : basic_string::at: __n (which is 0) >= this->size() (which is 0)

Исключение : аргумент индекса равен нулю, который больше или равен размеру строки, которая равняется нулю.

Чтобы добавить буквы к строке надо использовать другие функции : например оператор +=

E += 'a' ;

Почему у вас пустая строка - это неопределённое поведение. Зависит от реализации компилятора и случая.

AlexGlebe
  • 17,227