C: использование sprintf и strncpy для вставки данных в массив указателей - PullRequest
0 голосов
/ 31 марта 2009

У меня есть структура, которая имеет массив указателей. Я хотел бы вставить в массив цифры в строковом формате, то есть "1", "2" и т. Д.

Однако, есть ли разница в использовании sprintf или strncpy?

Есть большие ошибки в моем коде? Я знаю, что мне нужно звонить бесплатно, я сделаю это в другой части моего кода.

Большое спасибо за любые советы!

struct port_t
{
    char *collect_digits[100];

}ports[20];

/** store all the string digits in the array for the port number specified */
static void g_store_digit(char *digit, unsigned int port)
{
    static int marker = 0;
    /* allocate memory */
    ports[port].collect_digits[marker] = (char*) malloc(sizeof(digit)); /* sizeof includes 0 terminator */
    // sprintf(ports[port].collect_digits[marker++], "%s", digit);
    strncpy(ports[port].collect_digits[marker++], digit, sizeof(ports[port].collect_digits[marker]));
}

Ответы [ 5 ]

3 голосов
/ 31 марта 2009

Да, у вашего кода есть несколько проблем.

  • В C не приводите возвращаемое значение malloc(). Он не нужен и может скрывать ошибки.
  • Вы выделяете пространство на основе размера указателя, а не размера того, что вы хотите сохранить.
  • То же самое для копирования.
  • Неясно, что делает статический marker, и действительно ли логика вокруг него верна. port - это слот, который будет изменен, или он управляется статической переменной?

Хотите ли вы хранить в массиве только одну цифру на слот или многозначные числа?

Вот как могла бы выглядеть эта функция, учитывая объявление:

/* Initialize the given port position to hold the given number, as a decimal string. */
static void g_store_digit(struct port_t *ports, unsigned int port, unsigned int number)
{
  char tmp[32];

  snprintf(tmp, sizeof tmp, "%u", number);
  ports[port].collect_digits = strdup(tmp);
}
2 голосов
/ 31 марта 2009
strncpy(ports[port].collect_digits[marker++], digit, sizeof(ports[port].collect_digits[marker]));

Это неверно.

Вы выделили на collect_digits определенный объем памяти.

Вы копируете цифры * в эту память.

Длина, которую вы должны скопировать, strlen (цифры). На самом деле вы копируете это sizeof (ports [port] .collect_digits [marker]), который даст вам длину одного символа *.

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

Кроме того, даже если две длины всегда одинаковы, получение длины таким способом не является выразительным; это вводит в заблуждение читателя.

Обратите внимание, что strncpy () будет дополняться завершающими значениями NULL, если указанная длина копии превышает длину исходной строки. Таким образом, если цифры равны длине выделенной памяти, у вас будет неразрывная строка.

Строка sprintf () функционально корректна, но для того, что вы делаете, strcpy () (в отличие от strncpy ()), насколько я вижу и знаю о коде, правильный выбор.

Я должен сказать, я не знаю, что вы пытаетесь сделать, но код кажется очень неловким.

1 голос
/ 31 марта 2009

Еще одна проблема с оригинальным кодом: утверждение

strncpy(ports[port].collect_digits[marker++], digit, sizeof(ports[port].collect_digits[marker]));

ссылки marker и marker++ в одном выражении. Порядок вычисления для ++ в этом случае undefined - вторая ссылка на marker может быть оценена либо до, либо после выполнения увеличения.

1 голос
/ 31 марта 2009

Вместо использования malloc() и strncpy(), просто используйте strdup() - он выделяет буферную корзину достаточно для содержания и копирует содержимое в новую строку, все в одном кадре.

Так что вам вообще не нужно g_store_digit() - просто используйте strdup() и поддерживайте marker на уровне вызывающего.

1 голос
/ 31 марта 2009

Первое: зачем нужен массив указателей? Ожидаете ли вы несколько строк для объекта порта? Вероятно, вам нужен только простой массив или указатель (поскольку вы malloc -ing позже).

struct port_t
{
    char *collect_digits;
}ports[20];

Вам нужно передать адрес строки, в противном случае malloc действует на локальную копию, и вы никогда не получите обратно то, за что заплатили.

static void g_store_digit(char **digit, unsigned int port);

Наконец, sizeof применяется в контексте указателя и не дает вам правильный размер.

...