Структура веб-строка стиль


Мне пришлось сделать несколько функций, чтобы включить некоторые структуры в строки. Я еще зеленый, когда дело доходит C, поэтому я не уверен, если я делаю это очень неуклюже. Систему я кодирования не имеет и snprintf, я знаю, что было бы гораздо более элегантно, однако я не могу использовать его. Какие-либо советы?

int device_to_string(char* const asString, pDevice dev, size_t maxLength)
{
  char* ipAsString;
  size_t actualLength;
  struct in_addr addr;

  if (dev == NULL)
  {
    return NULL_ERROR;
  }

  addr.s_addr = dev->ip;
  ipAsString = inet_ntoa(addr);

  actualLength = strlen("name=")  + strlen(dev->name) +
                 strlen("&ip=")   + strlen(ipAsString) +
                 strlen("&mac=")  + strlen(dev->mac)  +
                 strlen("&type=") + strlen(dev->type) + 1;

  if (actualLength > maxLength)
  {
    return SIZE_ERROR;
  }

  strncat(asString, "name=",    strlen("name="));
  strncat(asString, dev->name,  strlen(dev->name));
  strncat(asString, "&ip=",     strlen("&ip="));
  strncat(asString, ipAsString, strlen(ipAsString));
  strncat(asString, "&mac=",    strlen("&mac="));
  strncat(asString, dev->mac,   strlen(dev->mac));
  strncat(asString, "&type=",   strlen("&type="));
  strncat(asString, dev->type,  strlen(dev->type));

  asString[actualLength] = '\0';

  return NO_ERROR;
}


391
7
задан 28 января 2011 в 12:01 Источник Поделиться
Комментарии
4 ответа

Да, без и snprintf, и к sprintf это становится немного утомительным, но я думаю, что этот код на самом деле вполне понятно. Вы используете свой горизонтальных и вертикальных пробелов очень хорошо, и понятно, что ты делаешь с каждого блока кода. Вы также контролем за любые возможные проблемы, которые могут возникнуть (нулевой указатель, недостаточной длины буфера и т. д.). Может, есть более лаконичный способ сделать это, но с точки зрения ясности и ремонтопригодность я думаю, что этот код будет достаточно.

5
ответ дан 28 января 2011 в 12:01 Источник Поделиться

Одна вещь, я бы лично вообще бы двигать конкатенации строк в собственную функцию, так как я вижу, вы повторили те же 2 строчки несколько раз:

strncat(asString, "name=",    strlen("name="));
strncat(asString, dev->name, strlen(dev->name));

Я бы что-то вроде:

void addParam( char *paramString, const char *paramName, const char *paramValue )
{
strncat( paramString, paramName, strlen( paramName );
strncat( paramString, paramValue, strlen( paramValue );
}

Уменьшает длину вашей функции и делает ее немного чище, имхо.

5
ответ дан 28 января 2011 в 02:01 Источник Поделиться

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

Для справки, стандарт C говорит:


§7.21.3.2 в strncat функции

Синопсис

#include <string.h>
char *strncat(char * restrict s1, const char * restrict s2, size_t n);


Описание

В strncat функция добавляет не более чем на N символов (символ null и
символы, которые следуют за его не дописываются) из массива, на который указывает С2 до конца
строки, указанной в С1. Начальный символ С2 перезапись нулевого символа в
конец С1. Завершающий нуль-символ всегда добавляется к результату.261)

261) таким образом, максимальное количество символов, которые могут в конечном итоге в массиве, на который указывает С1 - это функция strlen(S1) в+Н+1.

Теперь, не поймите меня неправильно - то, что вы делаете является безопасным, потому что вы предварительно проверили длину, но strncat() ослик работа проверка длины-это действительно бессмысленно. Вы тоже не сказать всю правду strncat(); есть больше пространства в целевом буфере, чем вы признаете. Это безопасный, но это немного расточительно.


Я предлагаю использовать простую функцию, например:

char *copy_string(char *target, const char *source)
{
strcpy(target, source);
return(target + strlen(source));
}

target = asString;
target = copy_string(target, "name=");
target = copy_string(target, dev->name);
target = copy_string(target, "&ip=");
target = copy_string(target, ipAsString);
target = copy_string(target, "&mac=");
target = copy_string(target, dev->mac);
target = copy_string(target, "&type=");
target = copy_string(target, dev->type);

Одним из преимуществ этого является то, что он избегает квадратичного поведения. Как ваш выходной строкой становится все больше, в strncat() работы постепенно должен пропускать все больше и больше персонажей каждый раз это называется. При достаточно длинных строк, это может стать измеримые издержки. Это неприятность, что и strcpy() и соавт вернуть начальный адрес строки вместо адреса из null в конце строки; это означает, что вы в конечном итоге его дважды - один раз для копирования, Один раз, чтобы определить длину.


Что нотации-это все-таки немного нудно использовать. Я уже ранее созданной переменной длины функции список аргументов такой:

#include <stdarg.h>

char *vstrcpy(char *buffer, size_t buflen, ...)
{
const char *arg;
char *bufend = buffer + buflen;
char *target = buffer;
va_list args;

va_start(args, buflen);
while ((arg = va_arg(args, const char *)) != 0)
{
size_t arglen = strlen(arg);
if (target + arglen >= bufend)
return(0);
strcpy(target, arg);
target += arglen;
}
return(target);
}

target = vstrcpy(asString, maxLength, "name=", dev->name,
"&ip=", ipAsString,
"&mac=", dev->mac,
"&type=", dev->type, (const char *)0);
if (target == 0)
...something went wrong despite your pre-computation...

Обратите внимание, что вы должны предоставить явное приведение к окончательному прекращению маркер; вы бы вызывая неопределенное поведение на Windows 64 (как конкретный пример), если вы пропустили бросок. Это как notationally удобным эквивалентом и snprintf() (при этом необходимо оставить нулевой указатель в конце, а не формат строка, состоящая из неоднократных операции в начале), а так же безопасен. Вы можете выбрать лучшую возвращаемое значение - я выбрал значение null, чтобы указать, что было мало места и указатель на конечный нуль, если есть достаточно места. Можно имитировать и snprintf() более внимательно, если вы рассчитали полную длину, которые потребуются, просто убедившись, что пропустить копирование, когда вы достигли конца строки. Вы, наверное, тогда нужно использовать дополнительные ресурсы, а не указатели, потому что, хотя вы гарантированно сможете оценить буфера + buflen, вы не гарантированы, чтобы быть в состоянии оценить буфера + buflen + 1. Этот дизайн приводит к:

#include <stdarg.h>

size_t vstrcpy(char *buffer, size_t buflen, ...)
{
const char *arg;
char *target = buffer;
size_t offset = 0;
va_list args;

va_start(args, buflen);
while ((arg = va_arg(args, const char *)) != 0)
{
size_t arglen = strlen(arg);
if (offset + arglen < buflen)
strcpy(target + offset, arg);
offset += arglen;
}
return(offset);
}

len = vstrcpy(asString, maxLength, "name=", dev->name,
"&ip=", ipAsString,
"&mac=", dev->mac,
"&type=", dev->type, (const char *)0);

if (len >= maxLength)
...something went wrong despite your pre-computation...

Это только все копии аргументы, которые подходят. Если есть 20 символов, оставшихся в буфере, но следующая строка 30 символов, то 20 символов простаивают. Вы еще сказали, что, хотя фактическая длина необходимого пространства, и гарантированно без перелива. Если у вас есть другие требования (такие как копирование как можно больше, даже если это означает частичный аргумент копировать), а затем изменить код, чтобы сделать, как вам требуется. В моей книге, если струны не подходят, где я пытаюсь разместить их, я облажался.


Я не уверен, что современные оптимизирующие компиляторы предварительно вычислить значения strlen("строковая константа"). Если они не, вы можете получить небольшую выгоду от использования оператор sizeof("строковая константа")-1 , где в -1 приходится на терминальный ноль, что оператор sizeof() включает в размер возвращается.


Этот пример не является достаточно большим, чтобы извлечь выгоду из таблицы-ориентированный подход к сборке строки. Иногда, если структуры имеют достаточное количество элементов в них, вы можете использовать таблицу-ориентированный подход с offsetof() макрос определения начала расположения строки элементов в структуре данных. Вы, возможно, потребуется, чтобы закодировать типы смешанные типы (например, IP-адрес или номера, которые должны быть преобразованы в строку перед печатью). Это те осложнения, значит, вам нужно значительное количество элементов в структурах, прежде чем использовать технику.

В общих чертах, для структуры, которая содержит только пустые строки, завершающиеся, вы можете сделать:

struct description
{
const char *tag;
size_t offset;
};

static const struct description dev_desc[] =
{
{ "name=", offsetof(pDevice, name) },
{ "&ip=", offsetof(pDevice, ip) }, // Taking liberties here!
{ "&mac=", offsetof(pDevice, mac) },
{ "&type=", offsetof(pDevice, type) },
};
enum { DEV_DESC_SIZE = sizeof(dev_desc) / sizeof(*dev_desc) };

size_t offset = 0;
for (size_t i = 0; i < DEV_DESC_SIZE; i++)
{
offset = vstrcpy(&asString[offset], maxLength - offset,
dev_desc[i].tag, ((char *)&dev + dev_desc[i].offset),
(const char *)0);
if (offset > maxLength)
break;
}

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


Ваше заключительное задание ничего не покупает вам:

  asString[actualLength] = '\0';

Нулевая была вставлена окончательной strncat().

4
ответ дан 30 января 2011 в 10:01 Источник Поделиться

Здесь есть нечто большее-картину вопрос здесь; этот вид ручной работы со строками-это плохая идея. Это гарантированно глючный, часто с последствиями безопасности.

Вы должны использовать (предпочтительно) или по крайней мере реализовать утилита класса String. Вот пара примеров:

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

2
ответ дан 29 января 2011 в 04:01 Источник Поделиться