Обработка массивов в С сильно отличается от обработки в Java, и вам придется соответствующим образом изменить свое мышление. Массивы в C не являются объектами первого класса (то есть выражение массива не сохраняет свою «массивность» в большинстве контекстов). В Си выражение типа «массив N-элементов T
» будет неявно преобразовано («распад») в выражение типа «указатель на T
», за исключением случаев, когда выражение массива является операндом sizeof
или унарных &
операторов, или если Выражение массива - это строковый литерал, используемый для инициализации другого массива в объявлении.
Среди прочего, это означает, что вы не можете передать выражение массива функции и получить его как тип массива ; функция фактически получает тип указателя:
void foo(char *a, size_t asize)
{
// do something with a
}
int bar(void)
{
char str[6] = "Hello";
foo(str, sizeof str);
}
При вызове foo
выражение str
преобразуется из типа char [6]
в char *
, поэтому вместо параметра foo
объявляется первый параметр . Так как выражение массива является операндом оператора, оно не преобразуется в тип указателя, поэтому вы получаете количество байтов в массиве (6). char *a
char a[6]
sizeof str
sizeof
Если вам действительно интересно, вы можете прочитать « Развитие языка Си» Денниса Ричи, чтобы понять, откуда это лечение.
В результате функции не могут возвращать типы массивов, что нормально, поскольку выражения массива также не могут быть целью назначения.
Самый безопасный метод для вызывающего - определить массив и передать его адрес и размер функции, которая должна записать в него:
void returnArray(const char *srcArray, size_t srcSize, char *dstArray, char dstSize)
{
...
dstArray[i] = some_value_derived_from(srcArray[i]);
...
}
int main(void)
{
char src[] = "This is a test";
char dst[sizeof src];
...
returnArray(src, sizeof src, dst, sizeof dst);
...
}
Другой метод заключается в том, что функция динамически выделяет массив и возвращает указатель и размер:
char *returnArray(const char *srcArray, size_t srcSize, size_t *dstSize)
{
char *dstArray = malloc(srcSize);
if (dstArray)
{
*dstSize = srcSize;
...
}
return dstArray;
}
int main(void)
{
char src[] = "This is a test";
char *dst;
size_t dstSize;
dst = returnArray(src, sizeof src, &dstSize);
...
free(dst);
...
}
В этом случае вызывающая сторона отвечает за освобождение массива с помощью free
функции библиотеки.
Обратите внимание, что dst
в приведенном выше коде это простой указатель на char
, а не указатель на массив char
. Указатель C и семантика массива таковы, что вы можете применить оператор индекса []
к выражению типа массива или типа указателя; оба src[i]
и dst[i]
получат доступ к i
'-ому элементу массива (даже если src
имеет только тип массива).
Вы можете объявить указатель на массив из N элементов T
и сделать нечто подобное:
char (*returnArray(const char *srcArr, size_t srcSize))[SOME_SIZE]
{
char (*dstArr)[SOME_SIZE] = malloc(sizeof *dstArr);
if (dstArr)
{
...
(*dstArr)[i] = ...;
...
}
return dstArr;
}
int main(void)
{
char src[] = "This is a test";
char (*dst)[SOME_SIZE];
...
dst = returnArray(src, sizeof src);
...
printf("%c", (*dst)[j]);
...
}
Несколько недостатков с вышесказанным. Прежде всего, более старые версии C ожидают SOME_SIZE
быть константой времени компиляции, то есть функция будет работать только с одним размером массива. Во-вторых, вы должны разыменовать указатель перед применением индекса, который загромождает код. Указатели на массивы работают лучше, когда вы имеете дело с многомерными массивами.