てけもぐ Tech 忘備録

C言語の配列、ローカルに確保した場合

対象

C言語初学者

内容

またC言語の配列の話(笑)。

今回は、ローカルに配列を確保した場合について。

#include<stdio.h>

char* func1(){
  char array[] = {'a', 'b',  'c'};
  return array;   // Don't do this!
}

int main(){
  printf("array: %s\n", func1()); // not correct!
}

上のコードはやっちゃ駄目な例。配列は、ローカル変数でスタックに保持されて、関数から抜けたらそのメモリ領域の確保は解除されてしまいます。当たり前と言えば当たり前なんですけどね。このコード、実際には警告付きでコンパイルされて、(nil) とかで表記されたりする時もありますが、printf に渡されるのは実は何だか分かりません。

でも、この関数の中でしか使えない訳ではなく、そこから呼ぶ関数には渡せます。

#include<stdio.h>

void func_child(char array[]){
  printf("in child func: %s\n", array);  // OK! 'abc' is correctly shown
}

int main(){
  char array[] = {'a', 'b',  'c'};
  func_child(array);
}

関数が確保したローカル変数の領域は、関数を出るまで有効なので。

ですが、文字列のリテラル使用の場合、似てる様でいて似てません。上の様に配列の中に入れてやるとローカル保持ですが、以下の様に文字列をリテラルとし、ポインタ渡しで返した場合は動きが違うんですよね。こちらは関数から出てもオッケー。

#include<stdio.h>

char *func(){
  char *ptr = "abc";
  return ptr;  // OK!
}

void main(){
  printf("array: %s\n", func());  // "abc" is correctly shown
}

上の例は、文字列リテラルである"abc" が、静的領域に置かれて、そこのアドレスを ptr ポインタ変数が持つという意味で、そのポインタの中身をコピーして返してる。文字列リテラルは、ローカルのスタック領域に置かれるのではない。ptr ポインタはローカルのスタック領域に確保されるんですが、中身を値渡しなのでOK。

これを char array[] と配列型にして返すと動きません。

#include<stdio.h>

char *func(){
  char array[] = "abc";
  return array;   // Don't do this!
}

int main(){
  printf("array: %s\n", func());
}

配列型の場合は、関数ローカルのスタックに"abc"分の領域が保持されて、そのアドレスを array が指す様になります。なので関数から出ると使えません。上のコードは、char array[]; として、array[0] = 'a'; array[1] = 'b'; ... としているのと同じで、char *ptr = "abc" とは動きが全く別って事みたいなんですよね。

意外と深いと思う C 言語の配列の話。

Tags