Почему строка пустая?
От: indee  
Дата: 23.04.21 09:52
Оценка:
Почему mystr пустая и длина = 0 (mystr.length())?


Спасибо!


#include <iostream>

using namespace std;

void test(char* value) {

    string str = "Hello!";

    copy(str.begin(), str.end(), value);
    value[str.size()] = '\0';

}

int main()
{
    
    string mystr = "";

    test(&mystr[0]);

    std::cout << mystr << " Length = " << mystr.length() << std::endl;

//    mystr = mystr.c_str(); после этого все ОК
//    std::cout << mystr << " Length = " << mystr.length() << std::endl;mystr = mystr.c_str();

}
Re: Почему строка пустая?
От: watchmaker  
Дата: 23.04.21 10:23
Оценка: +2
Здравствуйте, indee, Вы писали:

I>Почему mystr пустая и длина = 0 (mystr.length())?


У тебя в программе неопределённое поведение. Оно может проявляться множеством способов, в том числе и таким.

Когда ты записываешь что-то в буфер строки s напрямую, то запись можно делать только в область памяти от s.data() до s.data()+s.size() причём в последнюю позицию s.data()+s.size() можно записывать только '\0'.
В функции test это требование нарушается и запись идёт за пределы этого диапазона, перезатирая чужие данные.


  Скрытый текст
Технически, длина строки mystr не меняется и остаётся нулём, так как она хранится отдельно и от содержимого буфера [ s.data(), s.data()+s.size() ] не зависит. А функция test никак не обновляет размер. Но это деталь реализации. И она не особо важна, пока в программе есть неопределённое поведение.



I>//    mystr = mystr.c_str(); после этого все ОК
I>//    std::cout << mystr << " Length = " << mystr.length() << std::endl;mystr = mystr.c_str();


Что ты тут подразумеваешь, говоря что "все ОК"?
То что дальше программа иногда выводит мусор вместо исходной строки, а иногда падает?
Re[2]: Почему строка пустая?
От: indee  
Дата: 23.04.21 11:02
Оценка:
W>В функции test это требование нарушается и запись идёт за пределы этого диапазона, перезатирая чужие данные.

Что же делать?
Re: Почему строка пустая?
От: saf_e  
Дата: 23.04.21 11:12
Оценка:
Здравствуйте, indee, Вы писали:

I>Почему mystr пустая и длина = 0 (mystr.length())?



I>Спасибо!


Это вам еще повезло что она просто пустая, при таком подходе обычно программа падает.

Давайте попробуем пройтись по пунктах:
1. Почему строка пустая?

std::string это контейнер, который выделяет память под строку. Когда вы делаете &mystr[0] вы получаете указатель на этот буфер (опустим скучные подробности). В вашем случае контейнер изначально пустой, по идее даже сам факт обращения по индексу внутрь такой строки — UB, но это нам знатоки стандарта подскажут.
Скорее всего имеет место оптимизация для хранения маленьких строк, поэтому там не скучный null указатель, а указатель на некий внутренний буфер. Поэтому вам удается записать вовнутрь немного текста. Скорее всего если вы запишите побольше, оно все-таки упадет.
Почему же не смотря на то что вы туда что-то смогли записать она все еще пустая? Строка хранит свой размер, а не вычисляет его каждый раз заново. Поэтому даже после модификации буфера и записи 0 — она все еще считает себя пустой.

2. Что же делать?
Писать на С++:

void test(string &value) {

    string str = "Hello!";

    value = str;

}

или
string test() {

    string str = "Hello!";

    return str;
}

mystr = test();


3. Хочу писать на С!
Этот вариант сложнее, т.к. С ничего не знает про с++ обьекты, и памятью придется управлять самому. Обычно есть два подхода:
* как-то узнать нужный размер буфер и выделить его заранее
* выделить буфер фиксированного размера и молиться и передать размер в ф-цию
Часто совмещают оба подхода, типа:
int test(char *buf, int size) {

    string str = "Hello!";

    if(size > str.size())
    {
    // copy string
    }
    return str.size();
}

string mystring(test(nullptr, 0));
test(mystring.data(), mystring.size());


Я опускаю скучные подробности по анализу кода ошибки.

Ниже идет сообщение которое я планировал писать изначально:

Вы явно плохо понимаете что вы пишите, для начала стоит почитать книжку для начинающих, типа Страуструпа, или еще что-то подобное. Тут периодически проскакивают хорошие варианты.
А потом, если остануться непонятные моменты — уже можно спрашивать в интернете.
Re[3]: Почему строка пустая?
От: ArtDenis Россия  
Дата: 23.04.21 11:34
Оценка:
Здравствуйте, indee, Вы писали:

W>>В функции test это требование нарушается и запись идёт за пределы этого диапазона, перезатирая чужие данные.


I>Что же делать?


Не делать так, а делать правильно
[ 🎯 Дартс-лига Уфы | 🌙 Программа для сложения астрофото ]
Re[2]: Почему строка пустая?
От: удусекшл  
Дата: 23.04.21 11:36
Оценка:
Здравствуйте, saf_e, Вы писали:

_>Давайте попробуем пройтись по пунктах:

_>1. Почему строка пустая?

_>std::string это контейнер, который выделяет память под строку. Когда вы делаете &mystr[0] вы получаете указатель на этот буфер (опустим скучные подробности). В вашем случае контейнер изначально пустой, по идее даже сам факт обращения по индексу внутрь такой строки — UB, но это нам знатоки стандарта подскажут.

_>Скорее всего имеет место оптимизация для хранения маленьких строк, поэтому там не скучный null указатель, а указатель на некий внутренний буфер. Поэтому вам удается записать вовнутрь немного текста. Скорее всего если вы запишите побольше, оно все-таки упадет.

Это справедливо для вектора, и не совсем справедливо для std::string. Хотя чисто теоретически c_str() может отдельно выделять память под Z-terminated строку, скорее всего ноль в строке лежит всегда, и даже когда она пустая. Но да, small string optimization наверное тоже — иначе для пустой строки до первого вызова c_str() или data() память бы не выделялась.

И да, для строк, начиная с C++ 11, допустимо обращаться по индексу "pos == size()" —

If pos == size(), a reference to the character with value CharT() (the null character) is returned.

Re[2]: Почему строка пустая?
От: indee  
Дата: 29.04.21 07:38
Оценка:
А так, при условии, что в длина string str < 30?
#include <iostream>

using namespace std;

void test(char* value) {

  string str = "Hello!";

  copy(str.begin(), str.end(), value);
  value[str.size()] = '\0';

}

int main()
{
        
  char tt[30];

  test(&tt[0]);

  sttd::cout << " tt = " << tt << std::endl;

}



Здравствуйте, saf_e, Вы писали:

_>Здравствуйте, indee, Вы писали:


I>>Почему mystr пустая и длина = 0 (mystr.length())?



I>>Спасибо!


_>Это вам еще повезло что она просто пустая, при таком подходе обычно программа падает.


_>Давайте попробуем пройтись по пунктах:

_>1. Почему строка пустая?

_>std::string это контейнер, который выделяет память под строку. Когда вы делаете &mystr[0] вы получаете указатель на этот буфер (опустим скучные подробности). В вашем случае контейнер изначально пустой, по идее даже сам факт обращения по индексу внутрь такой строки — UB, но это нам знатоки стандарта подскажут.

_>Скорее всего имеет место оптимизация для хранения маленьких строк, поэтому там не скучный null указатель, а указатель на некий внутренний буфер. Поэтому вам удается записать вовнутрь немного текста. Скорее всего если вы запишите побольше, оно все-таки упадет.
_>Почему же не смотря на то что вы туда что-то смогли записать она все еще пустая? Строка хранит свой размер, а не вычисляет его каждый раз заново. Поэтому даже после модификации буфера и записи 0 — она все еще считает себя пустой.

_>2. Что же делать?

_>Писать на С++:

_>
_>void test(string &value) {

_>    string str = "Hello!";

_>    value = str;

_>}
_>

_>или
_>
_>string test() {

_>    string str = "Hello!";

_>    return str;
_>}

_>mystr = test();
_>


_>3. Хочу писать на С!

_>Этот вариант сложнее, т.к. С ничего не знает про с++ обьекты, и памятью придется управлять самому. Обычно есть два подхода:
_> * как-то узнать нужный размер буфер и выделить его заранее
_> * выделить буфер фиксированного размера и молиться и передать размер в ф-цию
_>Часто совмещают оба подхода, типа:
_>
_>int test(char *buf, int size) {

_>    string str = "Hello!";

_>    if(size > str.size())
_>    {
_>    // copy string
_>    }
_>    return str.size();
_>}

_>string mystring(test(nullptr, 0));
_>test(mystring.data(), mystring.size());
_>


_>Я опускаю скучные подробности по анализу кода ошибки.


_>Ниже идет сообщение которое я планировал писать изначально:

_>
_>Вы явно плохо понимаете что вы пишите, для начала стоит почитать книжку для начинающих, типа Страуструпа, или еще что-то подобное. Тут периодически проскакивают хорошие варианты.
_>А потом, если остануться непонятные моменты — уже можно спрашивать в интернете.
_>
Re[3]: Почему строка пустая?
От: saf_e  
Дата: 29.04.21 13:29
Оценка:
Здравствуйте, indee, Вы писали:

I>А так, при условии, что в длина string str < 30?

I>
I>#include <iostream>

I>using namespace std;

I>void test(char* value) {

I>  string str = "Hello!";

I>  copy(str.begin(), str.end(), value);
I>  value[str.size()] = '\0';

I>}

I>int main()
I>{
        
I>  char tt[30];

I>  test(&tt[0]);

I>  sttd::cout << " tt = " << tt << std::endl;

I>}
I>


при указанных условиях — да, но я бы уже делал:

template<size_t N>
void test(char (&buf)[N])
{
  std::string test;
  std::copy_n(test.begin(), std::min(N, test.size()), buf);
}

или хотя бы

void test(char *buf, size_t N)
{
  std::string test;
  std::copy_n(test.begin(), std::min(N, test.size()), buf);
}


иначе никаких гарантий что где-то не произойдет переполнение — нет
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.