Я нахожусь в процессе изучения 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);
}
Вопросы
-Wall -pedantic
. Если я использую -ansi
также? Каковы были бы эквивалентные флаги, доступные в MSVC?Это работает хорошо. Но когда я скомпилировал его, используя 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 в своем коде ИМХО.
memset
- пустая трата времени, поскольку он все равно будет перезаписан (превратите свой первый strcat
в strcpy
). И вы должны всегда проверять malloc
, возвращающий NULL. Не смотря ни на что! malloc
- ваш лучший выбор, особенно если вы хотите вернуть объединенную строку. -std = c89
или что-то подобное. В любом случае 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);
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);
}
, которая использует стековое хранилище, если объединенная строка подходит, и выделяет память, только если это не так. Это часто может привести к значительному увеличению скорости, если большая часть струн объединится в меньше, чем предел.
Массивы переменной длины не были частью первого стандарта 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.
Очень распространенная идиома для решения этих проблем - разрешить вызывающей стороне управлять памятью. Таким образом, вместо того, чтобы выделять память самостоятельно (либо с использованием массива переменной длины в стеке, либо с помощью 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
в качестве четвертого аргумента.