C рефакторинг аргумента переменной

У меня есть функция irc_sendline как это можно назвать printf может

irc_sendline(s, "A strange game.\nThe only %s is not to play.", "winning move");

Это работает отлично, но я не доволен его реализацией:

int irc_sendline(irc *iobj, char *msg, ...)
{
   char tmp_msg[BUFSIZE], fmsg[BUFSIZE];
   va_list args;
   int len;

   va_start(args, msg);

   strncpy(tmp_msg, msg, BUFSIZE);
   strncat(tmp_msg, "\r\n", BUFSIZE);

   len = vsnprintf(fmsg, BUFSIZE, tmp_msg, args);
   len = send(iobj->fd, fmsg, len, 0);

   return len;
}

Вы видите, я использую 2 "временных" буфера здесь, потому что я сначала должен скопировать исходное сообщение с аргументов функции на временный буфер, чтобы добавить "\r\n" к нему и затем скопировать тот временный буфер в другой временный буфер, чтобы сделать фактическое форматирование с аргументами, предоставленными от вызова функции, и только ЗАТЕМ я могу отправить продвигающийся материал.

Как я мог сделать этот инструмент для очистки, лучше?


Спасибо за весь вход здесь, я думал, что моей единственной проблемой была путаница там, но это была на самом деле отсчитывающая бомба замедленного действия! Моя новая функция похожа на это:

int irc_sendline(irc *iobj, char *msg, ...)
{
   char buffer[BUFSIZE];
   va_list args;
   int res_str_len;
   int sent;

   va_start(args, msg);

   res_str_len = vsnprintf(buffer, BUFSIZE, msg, args);

   sent =  send(iobj->fd, buffer, res_str_len, 0);
   sent += send(iobj->fd, "\r\n", 2, 0);

   return sent;
}

Если бы я мог, я принять несколько ответов здесь, но meh.

6
задан LukeN 14 May 2010 в 17:07
поделиться

5 ответов

Сначала используйте vsnprintf для форматирования данных, затем добавьте "\ r \ n" к результату. В качестве альтернативы, просто используйте второй вызов, чтобы отправить на отправил "\ r \ n".

5
ответ дан 10 December 2019 в 02:43
поделиться

Если вы не хотите использовать msg для strcat (небезопасно и опасно, потому что вы не знаете размер строки), я думаю, вам придется жить с двумя буферами.

В стороне, я бы рассмотрел strncpy (..., BUFSIZE-2), чтобы \ r \ n всегда помещался в ваши сообщения и, следовательно, строки всегда переносились.

0
ответ дан 10 December 2019 в 02:43
поделиться

Поскольку \ r \ n будет в конце форматированной строки, почему бы не скопировать потом:

va_start(args, msg);
len = vsnprintf(fmsg, BUFSIZE, msg, args);
strncat(fmsg, "\r\n", BUFSIZE - strlen(fmsg) - 1);

Обратите внимание, что я также исправил аргументы для strncat.

1
ответ дан 10 December 2019 в 02:43
поделиться

Во-первых, если вас интересует «очиститель», прекратите использовать strncpy . Несмотря на вводящее в заблуждение название (и вопреки распространенному мнению), это не функция копирования строк ограниченной длины. Можно с уверенностью сказать, что strncpy - это функция, которая сегодня практически не используется. Когда вы видите, что в коде используется strncpy , «очиститель» сразу перестает быть предметом обсуждения (за исключением исчезающе узкого набора случаев, для которых strncpy действительно предназначался для использования).

Фактически, ваш код не работает именно потому, что вы использовали strncpy : если длина строки формата больше, чем длина буфера, strncpy не добавляет завершающий нулевой символ в результате, означающий, что последующий вызов strncat завершится сбоем (в лучшем случае).

Функция копирования ограниченной длины не существует в стандартной библиотеке, но она часто предоставляется реализацией под именем strlcpy . Если используемая вами реализация не предоставляет такой возможности - напишите ее самостоятельно и используйте.

Ваш код также не работает из-за неправильного использования strncat : strncat не принимает длину полного буфера в качестве последнего аргумента. Вместо этого strncat ожидает длину доступного остатка буфера. Итак, если вы хотите использовать strncat , вы должны сначала вычислить, сколько места осталось в конце буфера после предыдущего копирования. Опять же, хотя strncat более полезен, чем strncpy , вам может быть лучше использовать нестандартную (но часто предоставляемую реализацией) функцию strlcat , который на самом деле работает так, как вы думали, работает strncat .

Во-вторых, вместо того, чтобы добавлять часть \ r \ n заранее, почему бы вам не сделать это потом? Используйте тот факт, что vsnprintf возвращает количество символов, записанных в выходной буфер, и просто добавьте \ r , \ n и \ 0 символов в конце после завершения работы vsnprintf . Для этого не обязательно использовать strncat . Просто запишите символы прямо в буфер, конечно, следя за тем, чтобы вы не пересекали границу.

3
ответ дан 10 December 2019 в 02:43
поделиться

Одна из основных проблем вашего кода - vsnprintf возвращает количество символов, которые были бы помещены в буфер, если бы он был бесконечно большим, что может быть больше, чем BUFSIZE, если буфер недостаточно велик. . Поэтому, если у вас есть сообщение, которое переполняется, вы в конечном итоге отправите случайный мусор после окончания вашего буфера. Вам нужно добавить строку

if (res_str_len >= BUFSIZE) res_str_len = BUFSIZE-1

после vprintf, если вы действительно хотите обрезать сообщение

0
ответ дан 10 December 2019 в 02:43
поделиться
Другие вопросы по тегам:

Похожие вопросы: