2011-04-10 6 views
3

A comment по одному из моих ответов оставил меня озадаченным. При попытке вычислить, сколько памяти нужно Concat две строки в новый блок памяти, это было сказано, что с помощью snprintf предпочтение было отдано strlen, как показано ниже:Различные способы расчета длины строки

size_t length = snprintf(0, 0, "%s%s", str1, str2); 
// preferred over: 
size_t length = strlen(str1) + strlen(str2); 

Могу ли я получить некоторые рассуждения за этим? В чем преимущество, , если есть, и будет ли когда-нибудь увидеть, что один результат отличается от другого?

+2

Почему бы вам просто не спросить комментатора? –

ответ

5

Я был один, кто это сказал, и я ушел из в my comment в +1, которая была написана быстро и небрежно , поэтому позвольте мне объяснить. Моя точка зрения заключалась лишь в том, что вы должны использовать шаблон использования того же метода для вычисления длины, которая будет в конечном счете, будет использоваться для заполнения строки, а не для использования двух разных методов, которые могут потенциально отличаться тонким образом.

Например, если у вас было три строки, а не два, и два или более из них перекрываются, было бы возможно, что strlen(str1)+strlen(str2)+strlen(str3)+1 превышает SIZE_MAX и оборачивает мимо нулю, что приводит к недостаточному выделению и усечения на выходе (если snprintf) или чрезвычайно опасное повреждение памяти (если используются strcpy и strcat).

snprintf вернется -1 с errno=EOVERFLOW когда результирующая строка будет длиннее, чем INT_MAX, поэтому вы защищены. Вам нужно проверить возвращаемое значение, прежде чем использовать его, и добавить его для нулевого терминатора.

0

Одним из преимуществ будет то, что входные строки сканируются только один раз (внутри snprintf()) вместо дважды для раствора в strlen/strcpy.

На самом деле, на перечитывая этот вопрос и комментарий на ваш предыдущий ответ, я не понимаю, что точка находится в использовании sprintf() только стоимость каскадный длины строки. Если вы на самом деле делает конкатенация, мой абзац выше.

+0

snprintf (0, ...) не может работать. –

+4

@Pete Wilson: Вы неверны. Из 'man snprintf' в моей системе:« когда вызывается snprintf() с размером = 0 ... C99 позволяет str быть NULL в этом случае и дает возвращаемое значение (как всегда) в виде количества символов, которые будут иметь было написано в случае, если выходная строка была достаточно большой ». –

+1

@Pete Wilson: неправильно, см. Мой комментарий к вашему ответу. –

0

EDIT: случайный, ошибочный вздор удален. I сказать, что?

EDIT: Маттео в своем комментарии ниже абсолютно прав, и я был абсолютно неправ.

От C99:

2 Функция snprintf эквивалентна fprintf, за исключением того, что выходной сигнал записывается в массив (заданный аргументом с), а не к потоку. Если n равно нулю, ничего не записывается, и s может быть нулевым указателем. В противном случае выходные символы, выходящие за пределы n-1, равны , отбрасываются, а не записываются в массив, а нулевой символ записывается в конце символов, фактически записанных в массив. Если копирование происходит между объектами , которые перекрываются, поведение не определено.

Возвращает 3 Функция snprintf возвращает количество символов, которые бы были написаны бы п были достаточно большими, не считая завершающий нулевой символ, или отр ческих значения, если произошла ошибка кодирования. Таким образом, вывод с нулевым завершением был полностью написанным тогда и только тогда, когда возвращаемое значение неотрицательно и меньше n.

Спасибо, Маттео, и я приношу свои извинения перед OP.

Это отличная новость, потому что она дает положительный ответ на вопрос question I'd asked here всего три недели назад. Я не могу объяснить, почему я не прочитал все ответы, которые дали мне то, что я хотел. Потрясающие!

+2

Из справочной страницы 'snprintf':« Что касается возвращаемого значения 'snprintf()', SUSv2 и C99 противоречат друг другу: когда 'snprintf()' вызывается с 'size = 0', то SUSv2 определяет неопределенное возвращаемое значение меньше чем 1, в то время как C99 позволяет 'str' быть' NULL' в этом случае и дает возвращаемое значение (как всегда) как число символов, которые были бы записаны, если выходная строка была достаточно большой. " Таким образом, в обоих случаях нет дампа ядра, и, насколько ваш компилятор соответствует стандарту C99, этот вызов четко определен. –

+0

И, из стандарта C99 (§7.19.6.5 ¶2) «Если« n »равно нулю, ничего не записывается, а' s' может быть нулевым указателем ». (и ¶ 3) Функция 'snprintf' возвращает количество символов, которые были бы написаны, если бы' n' был достаточно большим, не считая завершающего нулевого символа. –

+0

сделано хорошо, удалено нижнее. :) –

3

Если вам нужно только определить, насколько велика будет конкатенация двух строк, я не вижу особых причин предпочесть snprintf, так как минимальные операции для определения общей длины двух строк - это то, что два strlen звонки. snprintf почти наверняка будет медленнее, потому что он должен проверять параметры и анализировать строку формата, кроме того, что просто идет по двум строкам, подсчитывающим символы.

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

/* static buffer "big enough" for most cases */ 
char buffer[256]; 
/* pointer used in the part where work on the string is actually done */ 
char * outputStr=buffer; 
/* try to concatenate, get the length of the resulting string */ 
int length = snprintf(buffer, sizeof(buffer), "%s%s", str1, str2); 
if(length<0) 
{ 
    /* error, panic and death */ 
} 
else if(length>sizeof(buffer)-1) 
{ 
    /* buffer wasn't enough, allocate dynamically */ 
    outputStr=malloc(length+1); 
    if(outputStr==NULL) 
    { 
     /* allocation error, death and panic */ 
    } 
    if(snprintf(outputStr, length, "%s%s", str1, str2)<0) 
    { 
     /* error, the world is doomed */ 
    } 
} 

/* here do whatever you want with outputStr */ 

if(outputStr!=buffer) 
    free(outputStr); 
0

вы должны добавить 1 к STRLEN), например, (. Помните, что вам нужно выделить пространство для nul завершающего байта.

+0

Добавление 1 в выражение 'snprintf' также необходимо, поскольку оно не включено в возвращаемое значение. Я просто опустил его как для простоты. –

0

«Преимущество», которое я вижу здесь, состоит в том, что strlen(NULL) может вызвать ошибку сегментации, в то время как (по крайней мере glibc's) snprintf() обрабатывает NULL параметров без сбоев.

Таким образом, с glibc- snprintf() вам не нужно, чтобы проверить, является ли одна из струн NULL, хотя length может быть немного больше, чем нужно, потому что (по крайней мере, на моей системе) printf("%s", NULL); печатает «(NULL)» вместо из ничего.


Я бы не рекомендовал использовать snprintf() вместо strlen(). Это просто не очевидно. Гораздо лучшим решением является оболочкой для strlen(), который возвращает 0, когда аргумент NULL:

size_t my_strlen(const char *str) 
{ 
    return str ? strlen(str) : 0; 
} 
+0

'snprintf' не принимает' NULL' для аргументов '% s'-specifier. –

+0

R ..: это еще раз глупое расширение GNU? Поскольку мой glibc-'snprintf()' счастливо принимает аргументы «NULL» для спецификатора '% s'. – Philip

+0

Это UB, поэтому реализация имеет право делать все, что захочет. glibc действует так, как если бы аргумент был «" (null) "в этом случае. –

0

Так snprintf() дает мне размер строка была бы. Это означает, что я могу malloc() место для этого парня. Очень полезно.

Я хотел (но не нашел до сих пор) эту функцию snprintf(), потому что я форматирую тонны строк для вывода позже; но мне не нужно было назначать статические bufs для выходов, потому что трудно предсказать, сколько будет времени на выходе. Так что я в конечном итоге с большим количеством 4096-длинных символьных массивов :-(

Но теперь - с помощью этого недавно обнаруженное (для меня) snprintf() функции углеродно счета, я могу таНос() выход BUFS и спать по ночам, и другое.

Еще раз спасибо и извинения ОП и к Маттео.

+0

Итак, почему анонимный downvote ЭТОТ время :-) –

Смежные вопросы