C: использование sprintf и strncpy для вставки данных в массив указателей

У меня есть структура с массивом указателей. Я хотел бы вставить в массив цифры в строковом формате, т.е. "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]));
}

person ant2009    schedule 31.03.2009    source источник


Ответы (5)


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

  • В 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);
}
person unwind    schedule 31.03.2009

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

Это неправильно.

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

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

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

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

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

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

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

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

person Community    schedule 31.03.2009

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

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

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

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

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

person dirkgently    schedule 31.03.2009
comment
Оператор sizeof определенно применяется не только к массивам. { интервал х; printf(x равно %d символов\n, sizeof x); } совершенно корректно, массива не видно. - person unwind; 31.03.2009
comment
@unwind: Конечно. Я имел в виду в этом контексте. - person dirkgently; 31.03.2009

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

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

person qrdl    schedule 31.03.2009

Еще одна проблема с исходным кодом: оператор

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

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

person Dan Breslau    schedule 31.03.2009