В C объект имеет одну из четырех длительностей хранения (также называемых продолжительностью жизни): статическую, потоковую, автоматическую и распределенную (C 2018 6.2.4 1).
Автоматически создаются объекты с автоматической продолжительностьювнутри функции и перестают существовать, когда выполнение функции заканчивается, поэтому вы не можете использовать их, созданные внутри вашей функции, для возврата значения.
Объекты с выделенной продолжительностью хранения сохраняются до освобождения, но вы попросилиисключите их.
Длительность хранения потока, скорее всего, не применима к вашей ситуации или фактически эквивалентна длительности статического хранения, о которой я расскажу ниже.
Это означает, что вы можете выбрать следующие варианты:
- Пусть вызывающий передаст вам объект, в который нужно вернуть данные.Этот объект может иметь любую продолжительность хранения - вашей функции не нужно знать, поскольку она не будет ни выделять, ни освобождать ее.Если вы сделаете это, вызывающая сторона должна предоставить объект, достаточно большой для возврата данных.Если этот размер заранее неизвестен, вы можете либо предоставить отдельную функцию для его вычисления (которую вызывающая сторона затем будет использовать для выделения необходимого пространства), либо включить ее в свою функцию в качестве специального режима, в котором он обеспечивает требуемый размер безпредоставление данных еще.
- Использование объекта со статической продолжительностью хранения.Поскольку этот объект создается при запуске программы, вы не можете регулировать размер в пределах вашей функции.Вы должны встроить ограничение размера в программу.Существенная проблема этого подхода заключается в том, что функция должна возвращать только один объект, поэтому одновременно может использоваться только один объект.Это означает, что после вызова функции ее не следует вызывать до тех пор, пока вызывающая сторона не закончит использовать данные в объекте.Это серьезное ограничение в разработке программ и возможность ошибок, поэтому он редко используется.
Таким образом, типичное решение выглядит следующим образом:
size_t HowMuchSpaceIsNeeded(char original[1000][1000])
{
… Calculate size.
return SizeNeeded;
}
void modify(char destination[][1000], char original[1000][1000])
{
… Put results in destination.
}
Вариантдля обеспечения безопасности:
void modify(char destination[][1000], size_t size, char original[1000][1000])
{
if (size < amount needed)
… Report error (possibly by return value, or program abort).
… Put results in destination.
}
Затем вызывающая сторона делает что-то вроде:
size_t size = HowMuchSpaceIsNeeded(original);
char (*results)[1000] = malloc(size);
if (!results)
… Report error.
modify(results, size, original)
… Work with results.
free(results);
Как Давистор Примечания , функция может вернутьмассив встроен в структуру.С точки зрения семантики C это позволяет избежать проблемы времени жизни объекта, возвращая значение, а не объект.(Все содержимое структуры является значением структуры.) С точки зрения фактической аппаратной реализации, оно в значительной степени эквивалентно методу вызывающего прохода объекта, описанному выше.(Рассуждения здесь основаны на логике работы компьютеров, а не на спецификации C: чтобы функция возвращала значение, для представления которого требуется много места, вызывающая сторона должна предоставить требуемое пространство для вызываемой функции.Обычно вызывающая сторона выделяет место в стеке и предоставляет его вызываемой функции.Это может быть быстрее, чем malloc
, но может также использовать значительное количество стекового пространства.Обычно мы избегаем использования значительных объемов стекового пространства, чтобы избежать переполнения стека.