Локальные переменные и память в c - PullRequest
0 голосов
/ 22 января 2019

Я немного новичок в C и мне интересно узнать кое-что о распределении памяти. Моя функция заключается в следующем:

size_t count_nwords(const char* str) {
    //char* copied_str = strdup(str);  // because 'strtok()' alters the string it passes through
    char copied_str[strlen(str)];
    strcpy(copied_str, str);
    size_t count = 1;

    strtok(copied_str, " ");
    while(strtok(NULL, " ") != 0) {
        count++;
    }

    //free(copied_str);

    return count;
}

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

У меня есть два вопроса:

  1. Должен ли путь strdup() (который является закомментированной частью в коде) предпочтительнее, чем strcpy()? Насколько я понимаю, strcpy() достаточно и быстрее, но я не уверен.
  2. Поскольку для возвращаемого значения size_t не выделено памяти (это локальная переменная), следует ли это сделать для обеспечения надежности функции? Или использование size_t nwords = count_nwords(copied_input); абсолютно безопасно и всегда будет правильно возвращать возвращаемое значение?

Спасибо!

РЕДАКТИРОВАТЬ: Я принял единственный ответ, который касался именно моих вопросов, но я советую прочитать другие ответы, поскольку они дают хорошее представление об ошибках, которые я сделал в моем коде.

Ответы [ 3 ]

0 голосов
/ 22 января 2019

Невозможность учесть нулевой символ

// char copied_str[strlen(str)];
char copied_str[strlen(str) + 1];
strcpy(copied_str, str);

Неправильный алгоритм

Даже с вышеуказанным исправлением код возвращает 1 с count_nwords(" ")

Ненужное копирование строки

strtok() здесь не нужен. Копия строки не требуется.


Альтернатива: пройтись по струне.

size_t count_nwords(const char* str) {
  size_t count = 0;
  while (*str) {
    while (isspace((unsigned char) *str)) {
      str++; 
    }
    if (*str) {
      count++;
      while (!isspace((unsigned char) *str) && *str) {
        str++; 
      } 
    }
  }
  return count;
}
0 голосов
/ 22 января 2019

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

size_t count_words (const char *str)
{
    size_t words = 0;
    int in_word = 0;

    while (*str) {
        if (isspace ((unsigned char)*str))
            in_word = 0;
        else {
            if (!in_word)
                words++;
            in_word = 1;
        }
        str++;
    }

    return words;
}

Стоит разобраться во всех техниках.isspace требует включения ctype.h.

0 голосов
/ 22 января 2019
  1. Должен ли путь strdup () (который является закомментированной частью в коде) предпочтительнее пути strcpy ()?Насколько я понимаю, strcpy () достаточно и быстрее, но я не уверен.

Ваше решение чисто и работает хорошо, так что не беспокойтесь.Единственное, что вы используете VLA, который теперь является необязательным, тогда использование strdup будет менее стандартным.Что касается производительности, поскольку не указано, как реализованы VLA, производительность может варьироваться от компилятора / платформы к компилятору / платформе (известно, что gcc использует стек для VLA, но любой другой компилятор может использовать кучу).Мы только знаем, что strdup выделяет в куче, вот и все.Я сомневаюсь, что проблема с производительностью может возникнуть из-за такого выбора.

Примечание: размер выделенного вами файла неверен и должен быть не менее strlen(str)+1.

Поскольку для возвращаемого значения size_t не выделено памяти (это локальная переменная), следует ли это сделать для обеспечения устойчивости функции?Или использует size_t nwords = count_nwords (copied_input);полностью безопасен и всегда будет правильно возвращать возвращаемое значение?

Управление возвращаемыми значениями и памятью, подходящей для этого, является задачей компилятора.Обычно эти значения передаются в / из стека (имеют некоторое чтение в «кадре стека»).Как вы можете подозревать, пространство в стеке выделяется для него непосредственно перед вызовом и освобождается после вызова (как только вы отбрасываете или копируете возвращаемое значение).

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