Моделировать Паскаля/Делфи вставить в C


В Паскале/Делфи есть встроенные функции для вставки подстроки в основной строке.

procedure Insert ( const InsertStr : string; var TargetStr : string; Position : Integer ) ;

Я привык к этой функции, поэтому я хочу, чтобы имитировать это в C.

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

  • Чтобы иметь некоторое дополнительное пространство в char массив выделяется на стеке, или
  • Использование динамически выделяемой строки.

Я выбрал второй вариант. На данный момент мой код выглядит так. Он использует стандарт C string.h библиотека.

#include "stdlib.h"
#include "string.h"

void myStrInsert(const char *pWord, char **pStr, int pos)
{
    // Only dynamically allocated strings are allowed.
    if (pos < 0)
        return;
    int lenStr = strlen(*pStr);
    if (pos > lenStr)
        pos = lenStr;
    int lenWord = strlen(pWord);

    char *pResult = (char *)malloc(lenStr + lenWord + 1);
    if (pResult != NULL) {
        memcpy(pResult, *pStr, pos);
        memcpy(pResult + pos, pWord, lenWord);
        memcpy(pResult + pos + lenWord, *pStr + pos, lenStr - pos + 1);
        free(*pStr);
        *pStr = pResult;
    }
}

Может эта функция быть упрощены и оптимизированы?



524
11
c
задан 1 апреля 2018 в 01:04 Источник Поделиться
Комментарии
2 ответа

Есть целый ряд вещей, которые я вижу, которые могут помочь вам улучшить ваш код.

Используйте соответствующую форму для #includeс

Код имеет соответствующие #include файлы, но они должны быть в угловые скобки, а не кавычки. Когда вы пишете #include "math.h" Он отличается от #include <math.h>. Для стандартных заголовков, вы должны использовать <> форма. Если вы не знаете о разнице, см. Этот вопрос более подробно.

Не используйте венгерскую нотацию

Предварив каждую переменную с аббревиатурой своего типа (например, pStr) обычно называется "венгерская нотация", и это было когда-то популярно. Даже тогда это была плохая идея. Не загромождать исходный код, что, вместо этого, сосредоточиться на определении значимых имен для каждой переменной и правильно выбрать типы.

Возвращает код ошибки

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

Избежать фрагментации памяти

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

Делать больше ошибок

Это хорошо, что pos проверяется на отрицательное число, но было бы неплохо также проверить, чтобы убедиться, что указатели не NULL прежде чем разыменование их.

Можно переписать

Используя все эти рекомендации, можно переписать на что-то вроде этого:

int myStrInsert(const char *inserted, char **target, int pos)
{
if (inserted == NULL || target == NULL || *target == NULL || pos < 0) {
return -2;
}
size_t wordlen = strlen(inserted);
size_t origlen = strlen(*target);
char *orig = realloc(*target, origlen+wordlen+1);
if (orig == NULL) {
return -1;
}
if (pos > origlen) {
pos = origlen;
} else {
memmove(&orig[pos+wordlen], &orig[pos], origlen-pos);
}
memmove(&orig[pos], inserted, wordlen);
orig[origlen+wordlen] = '\0';
*target = orig;
return 0;
}

Ограничения

Одна вещь, я думаю, я должен упомянуть, что в отличии от оригинального кода, этот код не надежный если мы попытаемся соединить строки в себя. То есть, если мы попытаемся вставить "Фокс" в строку "лиса", передав в качестве первого параметра указатель на ту же строку мы уточняем, это не обязательно вели себя хорошо. Это нечто такое, что не сложно исправить, но я хотел убедиться, что читатели были в курсе ограничения, прежде чем пытаться использовать этот код.

11
ответ дан 1 апреля 2018 в 02:04 Источник Поделиться

Да, это примерно как можно реализовать Паскаль-стиль insert на C-стиль завершающими нулевыми символами. Я хотел бы отметить, однако, что в мире Паскаль, строки были нормально посчитано, а не нуль-завершенной, и если вы думаете, что в Паскале, вы можете быть склонны тонко делать предположения, что не держите на завершенные строки, либо относительно доброкачественные — например, то и дело забывая о том, что strlen() не за O(1) но о(н) операция — или потенциально опасных — таких, как случайно столкнула Терминаторы на ваш не-8-бит-чистые строки. Я хотел бы также отметить, что в Паскале, обычно количество символов в строке от 1 года; вы интерпретируете pos в стиле C, так это несоответствие с понятием 'Паскаль-стиль' insertОн определенно заслуживают краткого упоминания в документации, таких как полезный комментарий выше функции.

Эдвард прав #insert <string.h> и соавт за #insert "string.h". Я не согласен с ним по поводу "ошибок"; в контексте Паскаль-стиль обработки строк, это граничные условия, не ошибки, и если вы ищете, чтобы быть в Паскаль-стиле insertвы, вероятно, не заботятся о получении кодов ошибок для этих условий от вашей функции, так что это будет пустая трата мощности мозга, чтобы придумать и запомнить кодировку системы для них. Тем не менее, вы лечите случае pos вдали слева или справа *pStr двумя различными способами: в первом случае, ты не вставляя ничего, и во втором случае, вы установка pWord в правом конце *pStr. Вполне вероятно, что эта последовательность будет предпочтительнее здесь, либо вводя в обоих случаях или не вводить в обоих случаях, если вы желаете баг совместимости с некоторыми уже существующие реализации.

Что касается Эдварда рекомендации realloc() за malloc() — я бы не советовал, если только ваша система работает слишком медленно на практике. Преждевременная оптимизация-это корень всех зол. Памяти фрагментации вид, вы можете получить по телефону malloc() не реальная проблема, в современных вычислительных системах со страничной виртуальной памяти.

Кроме венгерской нотации, которые я также рекомендую против, если ты, действуя под кодовым стиль руководства, что перед ней — это плохо, но наверное не стоит бунт — есть еще одна стилистическая особенность код, что немного беспокоит меня: ты типажей результат malloc() явно (char *). В основе этой идеи лежит куча старого — как в девяностых — составителям за странный язык назвали "Си/Си++", в котором смешались идеи из C и C++. Реальные C не нужно это нелегко, потому что в C, void * и char * являются совместимыми; c++ нужно это, а так же рыбным "с/с++" компиляторы, но вы будете использовать new char[...] (или, вполне возможно, new std::string(...)) если бы Вы были программирования в реальном языке C++. Так что, я склонен рекомендовать вам вывезти типизировать.

И наконец, если вы используете malloc() и free()вам понадобится #include <stdlib.h>. Вы можете получить компилятором, не делая его явным, но это обычно считается хорошей практикой, чтобы явно включать заголовки всех функций и типов вы напрямую обратитесь в ваш код.

6
ответ дан 1 апреля 2018 в 11:04 Источник Поделиться