On Wed, Nov 28, 2007 at 07:35:18PM +0300, Alexey Tourbin wrote: > > > UINT4 rc_map2id(char *name) > > > { > > > struct map2id_s *p; > > > char ttyname[PATH_MAX]; > > > > > > *ttyname = '\0'; > > > if (*name != '/') > > > strcpy(ttyname, "/dev/"); > > > > > > strncat(ttyname, name, sizeof(ttyname)); > > > > > > for(p = map2id_list; p; p = p->next) > > > if (!strcmp(ttyname, p->name)) return p->id; > > > > > > warn("rc_map2id: can't find tty %s in map database", ttyname); > > > > > > return 0; > > > } > > > > > > Есть любители кодить на язычке Си всякие прикладные вещи. > > > И там это ещё называется "качество кода" или как-то так. > > Это не качество кода, это несоответствие занимаемой должности. :-) > > Дело в том что в язычке Си нет стандартного и эффективного способа > конкатенации двух строк. Простейший "стандартный" варинат -- использовать snprintf: char dest[PATH_MAX]; snprintf(dest, sizeof(dest), "%s%s", s1, s2); Тут недостаток в том, что резервируется место на стеке, которое заведомо многократно превышает то место, которое скорее всего потребутеся. Это уже плохо. PATH_MAX это кажется 4096, то есть это размер страницы памяти в ядре. При входе в такую функцию ядро будет "раздвигать" стек, если он ещё недостаточно раздвинут, что, в общем, не дёшево. Это же может "затриггить" своп! Но, вместе с тем, полной гарантии от "обрезания" окончания строки нет, то есть места всё же может на хватить. Другой полустандартный вариант -- это asprintf. const char *str = NULL; if (asprintf(&str, "%s%s", s1, s2) > 0 && str) { ... } else { /* обломалось */ } asprintf внутренне вызывает malloc, а это тоже не самая дешёвая операция, которая к тому же приводит к фрагментации памяти (к глибсишному маллоку почему-то много претензий на эту тему). К тому же по смыслу понятно, что malloc() не нужен для "короткоживущих" объектов, которые существуют лишь в пределах вызова функции. Для таких короткоживущих объектов лучше всего подходит стек. Нужно также заметить, что функции типа snprintf и asnprintf являются по сути мини-инерпретаторами -- они парсят строку формата и в рантайме побуквенно её анализируют, разгребая stdargs аргменты по мере необходимости. Это не самый эффективный способ конкатенации строк. Эти мои соображения в основном касаются "эффективности" сишного кода. То есть этот код не настолько эффективен, насколько мог бы быть. Но инсинуации на тему эффективности сишного кода зачастую сильно преувеличны. Так что эти варианты, в принципе, сойдут. У меня был реально случай, когда требуется повышенная эффективность конкатенации строк. Это связано с тем, что там в цикле перебирается практически астрономическое количество всяких вариантов, по числу файлов во всех пакетах в репозитарии. Речь идёт о apt-0.5.15lorg2-alt-genpkglist-reqfiles.patch. Вот мой вариант "эффективной" конкатенации строк в условиях суровой необходимости. +static +bool isRequiredPath(const char *dir, const char *basename) +{ + char fullname[strlen(dir) + strlen(basename) + 1]; + strcpy(fullname, dir); + strcat(fullname, basename); + if (reqfiles.find(fullname) != reqfiles.end()) + return true; + return false; +} Здесь используется нестандартная gcc'шная фича объявления стекового массива "переменной" или же "параметризуемой" длины. Кажется, эта фича есть в стандарте C99 (который gcc пока не до конца поддерживает), но тут всё равно нужно быть очень осторожным, потому что размер массива должен быть положительным! В общем, когда вопрос "эффективности" кода актуален, что случается редко, то, оказывается, проверки размеров буфера проще делать вручную и дальше использовать "старые добрые" strcpy и strcat. В общем-то я никому не советую использовать этот мой подход, unless, как говорится, you really-really know what you're doing. Общая проблема тут скорее в том что на Си всего этого лучше не делать вообще.