2009-11-18 3 views
3

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

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

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

} 

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

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

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

+2

Хорошо сделано для фактического пометки этого вопроса в качестве домашней работы для начала. :) – Noldorin

ответ

2

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

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

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

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

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

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; 
} 
+0

так, например, если я передал буфер размером 10 и строка «hello world» do say say buffer-size - каждый раз, когда я читаю символы? – c2009l123

+0

Нет, поскольку строки в C завершаются нулевым байтом, вы можете просто использовать 'strlen' для получения длины. Беспокойство относительно размеров буфера применяется к строкам, которые вы пишете. – Schwern

+0

Файл morse_size - это размер результата. Вы должны подсчитать, сколько символов вы положите в «morse» и остановитесь, когда вы доберетесь до morse_size -1 (так как вы хотите зарезервировать последний символ для nul-терминатора). Когда вы читаете символы из «ascii», вы просто читаете до конца, который будет символом nul. – nos

1

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

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

+0

он передан, но как его использовать, я уменьшаю его каждый раз, когда читаю символ или что? – c2009l123

+0

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

+0

Я всегда задавался вопросом, почему размер буфера не может быть определен только указателем. Это должно быть известно кем-то или свободным(), не будет работать. Есть ли техническая причина, почему не может быть функция «int alloc_to (void * ptr)»? Является ли это лишь одним из этих отверстий в стандартном API C? – Schwern

0

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

+0

Тэги C, а не C++. –

2
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 всегда будет размером, оставшимся в буфере для вашего использования.

+0

Ой, я думаю, я понял это, но еще одна вещь, поэтому я добавлю к morse char char, потому что так я переводил ascii в morse. как я могу проверять каждый раз, когда хочу добавить символ, достаточный объем памяти? я имею в виду, если бы я был передан буфер «hello world» достаточно только для «hel», как я знаю, что я должен остановиться там? не должен ли я уменьшать размер буфера каждый раз, когда я рассматриваю символ или что-то в этом роде? – c2009l123

+0

См. Последний абзац. Вы постоянно уменьшаете переменную morse_size по длине добавляемого сегмента кода Морзе. В тот момент, когда вы получаете 3-символьный сегмент кода Морзе, а morse_size - только два (например), у вас есть условие ошибки. Аналогично для символа окончательного символа строки. – paxdiablo

3

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

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

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

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

Не являются ли строки в крике? Добро пожаловать в самую крупную причину ошибок во всем мире компьютеров.

+0

Ничего себе.«Нет буфера» заставило меня пойти на все метафизические, a la the Matrix's «Нет ложки» :-) – paxdiablo

+0

«Ввод целевой строки до того, как источник плохой формы». ???? Считаете ли вы, что стандартные функции библиотеки «плохая форма»? – pmg

+1

@pmg Так как вы спросили, да. :) Я не являюсь программистом на языке C, поэтому мой стиль не является родным C. Стандартные соглашения библиотеки C были заложены более 30 лет назад, когда вычислительный мир был совсем другим местом. Они довольно устарели и не в ногу с остальной вселенной. Я думаю, это зависит от того, какая часть вашего окружающего кода следует за соглашениями C. Например, если вы сильно инвестируете в glib, вы должны, вероятно, придерживаться dest, source. – Schwern

0

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

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

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

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

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

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

+0

Но теперь у вас есть контракт с вашими потребителями API, чтобы выпустить эту память. Работоспособный, но уродливый и довольно опасный. – rpj

+0

@rpj Не может ли вызывающий абонент просто освободить его? – Schwern

Смежные вопросы