Обработка строк и выделение памяти - C

Я нахожусь в процессе изучения C. У меня есть метод, который берет 3 строки и комбинирует их, чтобы сделать некоторую операцию. Следующее было моей первой реализацией с помощью компилятора GCC.

void foo(const char *p1, const char *p2, const char *p3)
{
    size_t length = strlen(p1) + strlen(p2) + strlen(p3);
    char combined[length + 1];
    memset(combined, 0, length + 1);
    strcat(combined, p1);
    strcat(combined, p2);
    strcat(combined, p3);
    printf("Result : %s", combined);
}

int main()
{
    foo("hello ", "world ", "how");
    return 0;
}

Это работает хорошо. Но когда я скомпилировал это использование, cc -Wall -pedantic -g foo.c -o foo, Я начал получать предупреждения как ISO C90 forbids variable length array ‘combined’. MSVC не компилировал этот код. Измененный код как

void foo(const char *p1, const char *p2, const char *p3)
{
    size_t length = strlen(p1) + strlen(p2) + strlen(p3);
    char *combined = (char *) malloc(length + 1);
    memset(combined, 0, length + 1);
    strcat(combined, p1);
    strcat(combined, p2);
    strcat(combined, p3);
    printf("Result : %s", combined);
    free(combined);
}

Вопросы

  1. Действительно ли это - корректная реализация?
  2. Если массивы переменной длины не являются частью стандарта, почему GCC реализовала его? Если код, как будут ожидать, скомпилирует только на GCC, то использование переменных массивов будет лучшей альтернативой, чем использование malloc?
  3. Я думаю, что правило ползунка, если требуемая память, знает во время компиляции, используйте массивы, еще используют malloc для выделения требуемой памяти. Это корректно?
  4. Мой код, как ожидают, скомпилирует на GCC и MSVC. Я буду разрабатывать на GCC обычно. Таким образом, каковы флаги компилятора, который гарантирует максимальную мобильность? В настоящее время я использую -Wall -pedantic. Если я использую -ansi также? Каковы были бы эквивалентные флаги, доступные в MSVC?
  5. Что другие общие вещи состоят в том, чтобы рассмотреть при написании портативного кода C?
8
задан Navaneeth K N 7 July 2010 в 08:19
поделиться

4 ответа

Это работает хорошо. Но когда я скомпилировал его, используя cc -Wall -pedantic -g foo.c -o foo, я начал получать предупреждения типа ISO C90 запрещает массив переменной длины 'combined'.

Попробуйте скомпилировать с опцией -std=c99 (gcc).

MSVC не компилировал этот код. Измените код так

Если массивы переменной длины не являются частью стандарта, почему GCC реализовал это?

VLA являются частью ISO C99 (gcc и g++ (как расширение) поддерживают VLA). MSVC все еще поддерживает только C89.

Ожидается, что мой код будет компилироваться на GCC и MSVC.

Тогда вы не должны использовать VLA в своем коде ИМХО.

8
ответ дан 5 December 2019 в 09:24
поделиться
  1. Да, это так. Конкретных нарушений стандарта здесь нет. Однако набор memset - пустая трата времени, поскольку он все равно будет перезаписан (превратите свой первый strcat в strcpy ). И вы должны всегда проверять malloc , возвращающий NULL. Не смотря ни на что!
  2. C89 / 90 не является текущим стандартом, C99 - нет. И C1x не так уж и далеко. GCC идет в ногу со временем.
  3. Используйте локальные массивы только в том случае, если они вам не нужны, чтобы выжить после окончания функции. В противном случае malloc - ваш лучший выбор, особенно если вы хотите вернуть объединенную строку.
  4. Я думаю, что в gcc есть флаг -std = c89 или что-то подобное. В любом случае MSVC не всегда следует стандарту: -)
  5. Часто компилируйте и тестируйте его на обеих платформах. Это единственный способ убедиться.

Я бы выбрал:

void foo (const char *p1, const char *p2, const char *p3) {
    size_t length = strlen(p1) + strlen(p2) + strlen(p3);
    char *combined = (char *) malloc(length + 1);
    if (combined == NULL) {
        printf("Result : <unknown since I could't get any memory>\n");
    } else {
        strcpy(combined, p1);
        strcat(combined, p2);
        strcat(combined, p3);
        printf("Result : %s", combined);
        free(combined);
    }
}

или, поскольку вы на самом деле ничего не делаете со строкой, кроме ее печати:

void foo (const char *p1, const char *p2, const char *p3) {
    printf("Result : %s%s%s", p1, p2, p3);
}

: -)

Еще одна стратегия, которую я видел, - это «выделить только если вы должна "стратегия:

void foo (const char *p1, const char *p2, const char *p3) {
    char str1k[1024];
    char *combined;
    size_t length = strlen (p1) + strlen (p2) + strlen (p3) + 1;
    if (length <= sizeof(str1k))
        combined = str1k;
    else
        combined = malloc (length);
    if (combined == NULL) {
        printf ("Result : <unknown since I couldn't get any memory>\n");
    } else {
        strcpy (combined, p1);
        strcat (combined, p2);
        strcat (combined, p3);
        printf ("Result : %s", combined);
    }
    if (combined != str1k)
        free (combined);
}

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

7
ответ дан 5 December 2019 в 09:24
поделиться

Массивы переменной длины не были частью первого стандарта ISO C (по-разному называемого "C89", "C90" или "ANSI C"). Однако они являются частью последнего стандарта ISO C (известного как "C99").

GCC может компилировать ваш код в нескольких режимах, включая "строгий C90", "C90-with-GNU-C-extensions" и "C99" (хотя он не полностью реализует C99, он достаточно близок для большинства практических целей).

По умолчанию GCC использует "C90-with-GNU-C-extensions", поэтому ваш код компилируется без проблем. Использование -pedantic говорит ему выдавать все предупреждения, требуемые соответствующим стандартом (в данном случае C90), а такое предупреждение требуется вашему коду. Если вы зададите GCC флаг -std=c99 -pedantic, чтобы указать ему компилировать по базовому стандарту C99 и выдавать все необходимые предупреждения, ваш код скомпилируется нормально.

Если вы хотите, чтобы ваш код был совместим с базовым стандартом C90, то используйте -std=c90 -pedantic (или -ansi -pedantic: -ansi является синонимом -std=c90 при компиляции C-кода). Обратите внимание, что MSVC не поддерживает C99.

3
ответ дан 5 December 2019 в 09:24
поделиться

Очень распространенная идиома для решения этих проблем - разрешить вызывающей стороне управлять памятью. Таким образом, вместо того, чтобы выделять память самостоятельно (либо с использованием массива переменной длины в стеке, либо с помощью malloc чего-то или чего-то еще), вы ожидаете, что вызывающий объект предоставит память. Подумайте об этом:

int foo(const char *p1, const char *p2, const char *p3, char *buf, size_t bufsize)
{
    size_t requiredSize = strlen(p1) + strlen(p2) + strlen(p3) + 1;
    if (!buf)
        return requiredSize;
    if (requiredSize > bufsize)
        return -1;
    buf[0] = '\0';
    strcat(buf, p1);
    strcat(buf, p2);
    strcat(buf, p3);
    return requiredSize;
}

int main()
{
  /* simple case: caller knows that the buffer is large enough. */
  char buf[ 1024 ];
  foo( "Hello", "World", "Bar", buf, sizeof(buf) );
  printf("Result : %s\n", buf);

  /* complicated case: caller wants to allocate buffer of just the right size */
  size_t bufsize = foo( "Hello", "World", "Bar", NULL, 0 );
  char *buf2 = (char *)malloc(bufsize);
  foo( "Hello", "World", "Bar", buf2, bufsize );
  free( buf2 );
}

Преимущество этого подхода в том, что foo никогда не будет протекать. В дополнение к этому вызывающий может использовать простой массив на основе стека, если он ему подходит.Если он хочет узнать точный размер, он может вызвать foo и передать NULL в качестве четвертого аргумента.

0
ответ дан 5 December 2019 в 09:24
поделиться
Другие вопросы по тегам:

Похожие вопросы: