Является ли повторяющееся создание буферов в стеке в al oop в C плохой практикой? - PullRequest
2 голосов
/ 10 июля 2020

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

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

Раньше моя функция создавала буфер размером 2048 байт. Затем введите l oop. Во время каждой итерации l oop буфер заполняется путем к целевому каталогу плюс текущее имя файла в каталоге, присоединенное к его концу. Используя новый путь в буфере, я выполняю несколько небольших файловых операций. Это происходит до тех пор, пока не будет достигнуто окончательное имя файла в структуре.

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

Возвращаясь к этой функции, я переместил создание буфера в l oop, и каждая итерация l oop создает буфер размером n, где n - the length of the target directory + the length of the current filename within the directory.

Мне интересно, можно ли это считать плохой практикой или чем-то еще. Лучше ли мне создать буфер заранее и всегда иметь для него заданный размер, даже если 2/3 буфера иногда не используются? Или лучше создать буфер только того размера, который мне нужен?

Надеюсь, я дал достаточно информации ... Заранее спасибо!

Здесь - рассматриваемая функция.

int verifyFiles(DIR *dp, const char *pathroot){
    struct dirent *dir;
    struct stat pathstat;
    //char path[2048];
    int status = 0;

    while((dir = readdir(dp)) != NULL){
        if(!strncmp(dir->d_name, ".", 1))
            continue;

        size_t len = strlen(pathroot) + strlen(dir->d_name) + 2;
        char path[len];
        snprintf(path, sizeof(path), "%s/%s", pathroot, dir->d_name);
        
        // verify shebang is present on the first line of path's contents.
        if(!shebangPresent(path)){
            status = -1;
            break;
        }

        // verify path belongs to the user.
        stat(path, &pathstat);
        if(pathstat.st_uid != getuid()){
            status = -1;
            break;
        }
    }

    return status;
}

Ответы [ 2 ]

1 голос
/ 10 июля 2020

Нет ничего плохого в том, чтобы иметь такой фиксированный буфер. Не беспокойтесь о таких мелких деталях. Функция выделит 2 КБ памяти, выполнит свою работу и затем освободит ее. Если это проблема, значит, у вас проблемы посерьезнее, чем этот фрагмент кода.

Я бы беспокоился о таких вещах только в случае рекурсивных функций. Например, если бы у вас было что-то вроде этого:

int foo(int n) 
{
    char buf[2048];
    int r = foo(n-1);
    // Do something with buf and return
}

Приведенный выше код быстро съел бы стек при большом n. Но в вашем случае я бы действительно не беспокоился, пока у вас не будет доказательств или хотя бы разумного подозрения, что это действительно вызывает проблему.

Если бы это был буфер гораздо большего размера, скажем, порядка 100 КБ, я бы обязательно используйте распределение динамических c. В стеке обычно 1 МБ на Windows и 8 МБ на Linux. Так что дело не в том, чтобы «не тратить зря память», а в том, чтобы не взорвать стек.

0 голосов
/ 10 июля 2020

Является ли повторное создание буферов в стеке в al oop в C плохой практикой? Мне интересно, можно ли это считать плохой практикой или чем-то еще.

Нет, char path[len]; не проблема.

Тем не менее, подход, использованный здесь для определения буфера размер слабый.

Код ниже повторно вычисляет длину строки pathroot. Может быть, хороший компилятор проанализирует и увидит, что повторный вызов не нужен. Достаточно просто, чтобы расчет был произведен один раз.

size_t pathlen = strlen(pathroot); // add

while((dir = readdir(dp)) != NULL){
    if(!strncmp(dir->d_name, ".", 1))
        continue;

    // size_t len = strlen(pathroot) + strlen(dir->d_name) + 2;
    size_t len = pathlen + strlen(dir->d_name) + 2;

    char path[len];

Может мне лучше создать буфер заранее и всегда иметь для него установленный размер, даже если 2/3 буфера иногда не используются?

В this * В случае 1021 * размер пути, вероятно, имеет верхний предел среды: возможно, доступен как MAXPATH или MAXPATHLEN.

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

char path[MAXPATH + 1];  // or malloc()
int len = snprintf(path, sizeof path, "%s/", pathroot);
if (len < 0 || len >= sizeof path) Handle_OutOfRoom();

while((dir = readdir(dp)) != NULL){
  int len2 = snprintf(path + len, sizeof path - len, "%s", dir->d_name);
  if (len2 < 0 || len2 >= sizeof path - len) Handle_OutOfRoom();
  ...
...