Размер буфера в С - PullRequest
       13

Размер буфера в С

3 голосов
/ 19 ноября 2009

Когда предоставляется размер буфера в C, как я могу узнать, сколько еще осталось и когда мне нужно прекратить использование памяти?

Например, если функция, которую я пишу, такова:

void ascii_morse (lookuptable *table, char* morse, char* ascii, int morse_size) {

}

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

Конечно, выходные данные будут по морзе (так что я буду добавлять строку к морзе, но я думаю, я знаю, как это сделать, просто размер буфера - это то, что мне трудно понять)

Если вам нужна дополнительная информация, чтобы понять проблему, пожалуйста, скажите мне, я изо всех сил пытался ее объяснить.

Ответы [ 6 ]

3 голосов
/ 19 ноября 2009

Звучит так, как будто есть некоторая путаница с "буфером". Там нет буфера. morse-size сообщает вам, сколько памяти было выделено для morse (технически, часть памяти, на которую указывает morse). Если morse-size равен 20, то у вас есть 20 байтов. Это 19 байт полезного пространства, потому что строки заканчиваются нулевым байтом. Вы можете думать о morse-size как о «максимальной длине строки плюс один».

Вам необходимо проверить morse-size, чтобы убедиться, что вы не записываете больше байтов в morse, чем может вместить. morse - это не более чем число, указывающее на одно место в памяти. Не дальность, а единственное пятно. То, что было выделено на morse, приходит после этого. Если вы положите больше, чем это в morse, вы рискуете перезаписать чужую память. C НЕ будет проверять это для вас, это цена максимальной производительности.

Это похоже на то, как если бы вы пошли в театр, и служитель сказал вам: «Вы можете занять место A3 и следующие 5», а затем уйти. Нужно быть вежливым и не занимать 6 мест, кому-то другому дали A8.

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

Разве струны в C не кричат? Добро пожаловать в крупнейшую причину ошибок во всем компьютерном мире.

2 голосов
/ 19 ноября 2009
void ascii-morse (lookuptable *table, char* morse, char* ascii, int morse-size)

У вас уже есть размер выходного буфера, как видно из приведенного выше прототипа.

ascii без сомнения будет строкой с нулевым символом в конце, а morse будет выходным буфером: morse_size ( не morse-size, как у вас есть, поскольку это недопустимый идентификатор) будет количество символов, которое вам разрешено писать.

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

set apointer to start of ascii, mpointer to start of morse.
while apointer not at end of ascii:
    get translation from lookuptable, using the character at apointer.
    if length of translation is greater than morse_size:
        return an error.
    store translation to mpointer.
    add 1 to apointer.
    add length of translation to mpointer.
    subtract length of translation from morse_size.
if morse_size is zero:
    return an error.
store string terminator to mpointer.

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

Указатели используются для извлечения и вставки в соответствующие строки. Для каждого символа вы в основном проверяете, достаточно ли места в выходном буфере для добавления сегмента кода Морзе. И, наконец, вам также нужно проверить, достаточно ли места для символа конца строки '\0';

Способ , в котором вы проверяете, достаточно ли места, заключается в уменьшении переменной morse_size на длину строки, которую вы добавляете к morse каждый раз в цикле. Таким образом, morse_size всегда будет размером, оставшимся в буфере для вашего использования.

2 голосов
/ 19 ноября 2009

Вам нужно передать размер буфера вместе с указателем.

int
ascii_to_morse(lookuptable *table,
               char* morse, int morse_size,
               char* ascii);

Размер буфера не обязательно совпадает с текущей длиной строки (которую вы можете найти с помощью strlen).

Функция, как указано выше, будет читать строку ascii (не нужно знать размер буфера, чтобы она не передавалась) и записывать в буфер, на который указывает morse, размера morse_size. Возвращает количество записанных байтов (не считая нуля).

Редактировать: Вот реализация этой функции, которая, хотя она не в состоянии использовать правильные значения для кода Морзе, показывает, как управлять буфером:

typedef void lookuptable; // we ignore this parameter below anyway
// but using void lets us compile the code

int
ascii_to_morse(lookuptable *table,
               char* morse, int morse_size,
               char* ascii)
{
  if (!ascii || !morse || morse_size < 1) { // check preconditions
    return 0; // and handle it as appropriate
    // you may wish to do something else if morse is null
    // such as calculate the needed size
  }
  int remaining_size = morse_size;
  while (*ascii) { // false when *ascii == '\0'
    char* mc_for_letter = ".-"; //BUG: wrong morse code value
    ++ascii;
    int len = strlen(mc_for_letter);
    if (remaining_size <= len) { // not enough room
      // 'or equal' because we must write a '\0' still
      break;
    }
    strcpy(morse, mc_for_letter);
    morse += len; // keep morse always pointing at the next location to write
    remaining_size -= len;
  }
  *morse = '\0';
  return morse_size - remaining_size;
}

// test the above function:
int main() {
  char buf[10];
  printf("%d \"%s\"\n", ascii_to_morse(0, buf, sizeof buf, "aaa"), buf);
  printf("%d \"%s\"\n", ascii_to_morse(0, buf, sizeof buf, "a"), buf);
  printf("%d \"%s\"\n", ascii_to_morse(0, buf, sizeof buf, "aaaaa"), buf);
  return 0;
}
1 голос
/ 19 ноября 2009

Размер буфера не может быть выведен из одного указателя . Его нужно либо передать в качестве аргумента, либо каким-либо образом знать (как из значений DEFINE или других констант) или неявно знать ... (этот последний, неявный подход «опасен», если размер каким-то образом изменяется, но такие изменения не отражается в местах, где используется буфер ...)

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

0 голосов
/ 19 ноября 2009

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

char *ascii2morse(const char *ascii, lookuptable *table)

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

Альтернативой является использование realloc для непрерывного наращивания строки по мере необходимости. Вы выясняете, сколько байтов нужно для кодирования следующего символа, перераспределяете его и добавляете в строку. Это может быть медленнее, распределители памяти в наши дни довольно сложны, но они будут использовать ровно столько памяти, сколько вам нужно.

ОБА избегайте ловушки, в которой пользователь должен предварительно выделить неизвестный объем памяти, а ОБА устраняет ненужное условие ошибки «пользователь не выделил достаточно памяти».

Если бы вы действительно хотели сохранить память, я бы сохранял каждую точку / тире в азбуке Морзе как 2 бита, а не 8 бит. У вас есть три «слова», короткие и длинные буквы. Это минимум 2 бита.

0 голосов
/ 19 ноября 2009

Одно из возможных (медленных) решений - позволить функции обрабатывать нулевой указатель буфера и возвращать требуемый размер буфера. Затем вызовите его второй раз с буфером правильного размера

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...