【C/C++】 配列は戻り値に出来ない【代替案まとめ】


C言語では関数の戻り値として配列を返すことが出来ません。

// Function cannot return array type 'int [3]'
// Brackets are not allowed here; to declare an array, place the brackets after the identifier
int[3] getDate() { return {2016, 2, 29}; }

// Function cannot return array type 'int [3]'
int getDate()[3] { return {2016, 2, 29}; }

代替案

4つの代替案を紹介します。

固定長配列を返す場合は「# 配列のポインタ渡し」の利用が適切です。可変長配列や定数型の配列を返す場合は# ポインタを返す方法が利用出来ます。

配列のポインタ渡し

もっともメジャーな方法です。

既定の配列を引数として受け取り、直接値を操作します。

void getDateArray(int date[3]) {
  date[0] = 2016;
  date[1] = 2;
  date[2] = 29;
}

呼び出し側では、以前に固定長配列を宣言しておく必要があります。

int main() {
  int date[3];
  getDateArray(date);
  printf("%d年%d月%d日", date[0], date[1], date[2]); // 2016年2月29日
}

配列はポインタと等価関係にあるため、呼び出し側の配列はきちんと書き換わります。

ポインタを返す

配列の代わりにポインタを返す方法があります。

int *getDatePointer() {
  int *date = malloc(sizeof(int) * 3);
  date[0] = 2016;
  date[1] = 2;
  date[2] = 29;
  return date;
}

受け取ったポインタは最後に開放する必要があります。

int main() {
  int *date = getDatePointer();
  printf("%d年%d月%d日", date[0], date[1], date[2]);
  free(date);
}
こちらの方法は利用用途が限られます。関数の結果が可変長配列になるような場合に使うと良いでしょう。

配列が定数値の場合

定数値として宣言された配列であれば、直接ポインタとして返すことが出来ます。

// 内部リンケージ
const int referenceDate[] = {2001, 1, 1};
const int *getReferenceDate() {
  return referenceDate;
}

静的変数を返すことも可能です。

const int *getReferenceDate() {
  // 静的変数
  static const int referenceDate[] = {2001, 1, 1};
  return referenceDate;
}

静的オブジェクトであれば、定数ではない配列を返すことも可能です。

char* ctoa(char c) {
  static char str[2] = {'\0', '\0'};
  str[0] = c;
  return str;
}

この手のコードはマルチスレッドに対応出来ないため、利用用途が限られます。

注意点

ローカル変数を戻り値として利用しないようにしてください。

int *getDate() {
  int date[3] = {2016, 2, 29};
  
  // 警告: Address of stack memory associated with local variable 'date' returned
  return date;
  
  // これも危険
  return (int[]){2016, 2, 29};
  // 警告(Clang++): Returning address of local temporary object
  // 警告(Clang++): Pointer is initialized by a temporary array, which will be destroyed at the end of the full-expression
}

date配列はgetDate関数内部でのみ有効です。受け取り側では無効な値になるため注意してください。

構造体を利用する

ポインタを用いない方法としては、構造体の利用が考えられます。

typedef struct { int year, month, day; } Date;
Date getDateSet() { return (Date){2016, 2, 29}; }

int main() {
  Date date = getDateSet();
  printf("%d年%d月%d日", date.year, date.month, date.day);
}

配列型メンバ変数を活用する

配列そのものを構造体でラップする方法もあります。

typedef struct { int array[3]; } DateArray;
DateArray getDateArray() { return (DateArray){{2016, 2, 29}}; }

int main() {
  DateArray date = getDateArray();
  printf("%d年%d月%d日", date.array[0], date.array[1], date.array[2]);
  
  int *array = date.array;
  printf("%d年%d月%d日", array[0], array[1], array[2]);
}

広告