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

Эффективность строк C-String vs С++

С++ Primer говорит

Для большинства приложений, помимо обеспечения безопасности, это также больше эффективно использовать строки библиотеки, а не строки C-стиля

Безопасность понятна. Почему библиотека строк С++ более эффективна? В конце концов, под всем этим, не строки, которые все еще представлены как массивы символов?

Чтобы уточнить, говорит ли автор об эффективности (понимании) программы или эффективности обработки?

4b9b3361

Ответ 1

С-строки обычно быстрее, потому что они не называют malloc/new. Но бывают случаи, когда std::string быстрее. Функция strlen() - O (N), но std::string::size() - O (1).

Также при поиске подстроки в строках C вам нужно проверить '\0' на каждый цикл, в std::string - нет. В наивном алгоритме поиска подстроки это не имеет большого значения, потому что вместо проверки на '\0' вам нужно проверить i<s.size(). Но современные высокопроизводительные алгоритмы поиска подстроки пересекают строки в многобайтовых шагах. И необходимость проверки '\0' в каждом байте замедляет их. Именно по этой причине GLIBC memmem в x2 раза быстрее, чем strstr. Я сделал много бенчмаркинга подстрочных алгоритмов.

Это верно не только для алгоритма поиска подстроки. Многие другие алгоритмы строковой обработки медленнее для строк с нулевым завершением.

Ответ 2

Почему библиотека строк С++ более эффективна? В конце концов, под всем этим, не строки, которые все еще представлены как массивы символов?

Потому что код, который использует char* или char[], скорее всего, будет неэффективным, если его не написать. Например, вы видели цикл следующим образом:

char *get_data();

char const *s = get_data(); 

for(size_t i = 0 ; i < strlen(s) ; ++i) //Is it efficent loop? No.
{
   //do something
}

Это эффективно? Нет. Сложность времени strlen() равна O(N), и, кроме того, она вычисляется на каждой итерации в приведенном выше коде.

Теперь вы можете сказать: "Я могу сделать его эффективным, если я вызову strlen() только один раз". Конечно вы можете. Но вы должны делать все такое оптимизирование самостоятельно и с умом. Если вы что-то пропустили, вы пропустили циклы процессора. Но с std::string, такая оптимизация выполняется самим классом. Поэтому вы можете написать это:

std::string get_data();

std::string const & s = get_data(); //avoid copy if you don't need  it

for(size_t i = 0 ; i < s.size() ; ++i) //Is it efficent loop? Yes.
{
   //do something
}

Это эффективно? Да. Сложность времени size() равна O(1). Не нужно оптимизировать его вручную, что часто заставляет код выглядеть уродливым и трудно читаемым. Результирующий код с std::string почти всегда опрятен и чист по сравнению с char*.

Также обратите внимание, что std::string не только делает ваш код эффективным с точки зрения циклов процессора, но также повышает эффективность программы!

Ответ 3

Есть случаи, когда std::string может бить char[]. Например, строки C-стиля обычно не имеют явной длины, передаваемой вокруг - вместо этого терминатор NUL неявно определяет длину.

Это означает, что цикл, который постоянно strcat на char[], фактически выполняет O (n²), работает, потому что каждый strcat должен обрабатывать всю строку, чтобы определить точку вставки. Напротив, единственная работа, которую должен выполнить a std::string для конкатенации в конце строки, - это копирование новых символов (и, возможно, перераспределение памяти), но для сравнения должно быть справедливо, вы должны заранее знать максимальный размер и reserve() it).

Ответ 4

A std::string знает свою длину, что делает многие операции быстрее.

Например, данный:

const char* c1 = "Hello, world!";
const char* c2 = "Hello, world plus dog!";
std::string s1 = c1;
std::string s2 = c2;

strlen(c1) медленнее, чем s1.length(). Для сравнения strcmp(c1, c2) должен сравнивать несколько символов, чтобы определить, что строки не равны, но s1 == s2 может указывать, что длины не совпадают и немедленно возвращать false.

Другие операции также выигрывают от знания длины заранее, например. strcat(buf, c1) должен найти нулевой терминатор в buf, чтобы найти, где добавить данные, но s1 += s2 уже знает длину s1 и может сразу добавить новые символы в нужное место.

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

Ответ 5

Строки - это объект, который содержит массивы символов внутри себя, а также их размер и другие функциональные возможности. Лучше использовать строки из библиотеки строк, поскольку они избавляют вас от выделения и освобождения памяти, глядя на утечки памяти и другие опасности указателей. Но поскольку строки являются объектами, поэтому они занимают дополнительное пространство в памяти.

Cstrings - это просто массивы символов. Они ДОЛЖНЫ использоваться, когда вы работаете в режиме реального времени; когда вы не знаете полностью о том, сколько памяти у вас в руках. Если вы используете cstrings, вам придется позаботиться о распределении памяти, а затем скопировать данные в нее с помощью strcpy или символа по символу, а затем освободиться после его использования и т.д. И т.д. Поэтому лучше использовать строки из библиотеки строк, если вы хотите избежать кучи головных болей.

Строки повышают эффективность программы, но уменьшают эффективность обработки (хотя и не обязательно). И наоборот: cstrings

Ответ 6

Ну, очевидная и простая вещь, как они могут быть практически эффективнее (относительно времени исполнения), заключается в том, что они сохраняют длину строки вместе с данными (или, по крайней мере, их метод size должен быть O (1), который говорит практически то же самое).

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

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

Ответ 7

Вот короткая точка зрения.

Прежде всего, строки С++ являются объектами, поэтому их более последовательно использовать на объектно-ориентированном языке.

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

Наконец, строки C - это указатели, которые являются довольно сложными, когда вы новичок, и они приносят сложность. Поскольку ссылки предпочтительнее, чем указатели на С++, это делает более разумным использование std::string вместо строки C.

Надеюсь, я помог.

Ответ 8

Трудность с строками в стиле C заключается в том, что с ними действительно невозможно многое сделать, если не знать о структурах данных, в которых они содержатся. Например, при использовании "strcpy" необходимо знать, что буфер назначения доступен для записи и имеет достаточно места для размещения всего до первого нулевого байта в источнике (конечно, во всех слишком многих случаях на самом деле знайте это наверняка...). Очень немногие подпрограммы библиотеки предоставляют любую поддержку для распределения пространства по требованию, и я думаю, что все те, которые работают, выделяют его безоговорочно (так что если у вас есть буфер с пространством на 1000 байт, а один хочет скопировать 900-байтовую строку, код используя эти методы, пришлось бы отказаться от 1000-байтового буфера, а затем создать новый 900-байтовый буфер, хотя было бы лучше просто повторно использовать 1000-байтовый буфер).

Работа с объектно-ориентированным строковым типом во многих случаях не была бы столь же эффективной, как работа со стандартными C-строками, но выяснение оптимальных способов распределения и повторного использования. С другой стороны, код, который написан для оптимального распределения и повторного использования строк, может быть очень хрупким, и незначительные изменения в требованиях могут потребовать внесения большого количества сложных хитростей в код - неспособность полностью настроить код, вероятно, приведет к ошибкам, которые может быть очевидным и серьезным, или тонким, но даже более серьезным. Наиболее практичным способом избежать хрупкости в коде, который использует стандартные строки C, является его разработка очень консервативно. Документируйте максимальные размеры входных данных, обрезайте все, что слишком велико, и используйте большие буферы для всего. Работоспособный, но не очень эффективный.

В отличие от этого, если использовать объектно-ориентированные типы строк, используемые ими шаблоны распределения, вероятно, не будут оптимальными, но, вероятно, будут лучше, чем подход "распределить все большие". Таким образом, они сочетают большую часть эффективности во время ручного оптимизированного кода с безопасностью, которая лучше, чем подход "распределить все большие".