残念ながら、C は関数からの任意の配列や無名構造体の戻りをサポートしていませんが、2 つの回避策があります。
最初の回避策は、配列を含む構造体を作成することです。配列と同じサイズになり、スペースの局所性を確保するためにスタックに割り当てられます。
例えば
typedef struct {
int arr[5];
} my_array1;
typedef struct {
int values[3];
} factorFunctionReturnType_t;
2 番目の回避策は、静的メモリ プール (たとえば、この配列のみのカスタム malloc/free を持つ配列) を発明することです。これにより、効果的にこのプールからメモリを動的に割り当てることができるようになり、連続したスタック空間へのポインタが効果的に返されます。定義上、配列です。
例えば
#include <stdint.h>
static uint8_t static_pool[2048];
次に、この特定のメモリ プールを管理する my_alloc、my_free を実装し、作成されたメモリがスタック上にあり、そのメモリがアプリオリにアプリケーションによって既に所有されていることを確認します (一方、malloc は、以前はアプリケーションがアクセスできなかったメモリを割り当てたり、十分なメモリがない場合にクラッシュしたりする可能性があります)。失敗の戻りをチェックしませんでした)。
3 番目の回避策は、関数がローカル変数をコールバック関数に返すようにすることです。これにより、関数が終了すると、メモリを使用する関数がスタックを含むスコープの上に位置するため、スタックを破損することなく結果を使用できます。
#include <stdint.h>
#include <stdio.h>
void returnsAnArray(void (*accept)(void *)) {
char result[] = "Hello, World!";
accept(result);
}
void on_accept(void *result) {
puts((char *)result);
}
int main(int argc, char **argv)
{
returnsAnArray(on_accept);
return 0;
}
これは、フラグメンテーションを回避するために決定を行う必要がある非スタックのようなメモリ プールよりも効率的です。また、文字列をコピーする必要がないため、結果をスタックの一番上に置くだけのスタックのようなメモリ プールよりも効率的です。関数の戻り値が別のスレッドによって上書きされるリスクがないため (ロックが使用されない限り)、スレッドセーフです。
面白いことに、すべての「関数型プログラミング パラダイム」プログラムは、コールバック関数を連鎖させ、効果的にスタックに割り当てることで、malloc なしで C プログラムを記述できることを強調することになります。
これには、リンクされたリストが敵対的にキャッシュされなくなるという興味深い副作用があります (スタックに割り当てられたコールバックのリンクされたリストはすべてスタック上で互いに近くにあるため、malloc なしで実行時の文字列操作などを行うことができ、 malloc を実行せずに未知のサイズのユーザー入力を作成しました。