Cを使って配列を返す


Answers

Cの配列の扱いはJavaのものとは非常に異なっており、それに応じて考え方を調整する必要があります。 Cの配列はファーストクラスのオブジェクトではありません(つまり、配列式はほとんどのコンテキストで配列のままです)。 Cでは、配列式がsizeofまたは単項演算子のオペランドである場合を除き、 " T N要素配列"型の式が " Tへのポインタ"型の式に暗黙的に変換( "減衰")されます、または配列式が、宣言内の別の配列を初期化するために使用されている文字列リテラルである場合。

とりわけ、配列式を関数に渡して配列型として受け取ることができないことを意味します。 関数は実際にポインタ型を受け取ります:

void foo(char *a, size_t asize)
{
  // do something with a
}

int bar(void)
{
  char str[6] = "Hello";
  foo(str, sizeof str);
}

fooの呼び出しでは、式strchar [6]からchar *に変換されstr 。これは、 fooの最初のパラメータがchar a[6]代わりにchar a[6] char *aと宣言される理由です。 sizeof strでは、配列式はsizeof演算子のオペランドなので、ポインタ型に変換されないので、配列(6)のバイト数を取得します。

あなたが本当に興味があるなら、Dennis RitchieのC言語の開発を読んで、この治療がどこから来るのか理解することができます。

結論として、関数は配列型を返すことができません。配列式は代入の対象にすることはできません。

最も安全な方法は、呼び出し元が配列を定義し、そのアドレスとサイズを関数に書き出すことです。

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に配列型があっても)。

T N要素配列へのポインタを宣言して、同様のことすることができます:

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がコンパイル時定数であるとSOME_SIZEしています。つまり、関数は1つの配列サイズでしか動作しません。 第2に、コードを混乱させる下付き文字を適用する前にポインタを間接参照する必要があります。 多次元配列を扱うときは、配列へのポインタがうまく動作します。

Question

私はC言語では比較的新しいので、配列を扱うメソッドについては助けが必要です。 Javaプログラミングから来て、私はint [] method()を使って配列を返すことに慣れています。 しかし、C言語では配列を返すときにポインタを使う必要があることが分かりました。 新しいプログラマーであり、私が見てきた多くのフォーラムでさえ、私は本当にこれを理解していません。

基本的には、C言語でchar配列を返すメソッドを記述しようとしています。配列を使ってメソッド(returnArrayと呼ぶ)を提供します。 前の配列から新しい配列を作成し、その配列へのポインタを返します。 私はこれを始める方法と、配列から一度送信されたポインタをどのように読むのかについて助けが必要です。 これを説明する助けに感謝します。

アレイ返却機能のための提案コードフォーマット

char *returnArray(char array []){
 char returned [10];
 //methods to pull values from array, interpret them, and then create new array
 return &(returned[0]); //is this correct?
} 

関数の呼び出し元

int main(){
 int i=0;
 char array []={1,0,0,0,0,1,1};
 char arrayCount=0;
 char* returnedArray = returnArray(&arrayCount); ///is this correct?
 for (i=0; i<10;i++)
  printf(%d, ",", returnedArray[i]);  //is this correctly formatted?
}

私はこれをまだテストしていませんが、私のCコンパイラは現時点では動作していませんが、これを理解したいと思います




あなたのメソッドは、ひどく失敗するローカルスタック変数を返します。 配列を返すには、関数の外に関数を作成し、関数にアドレスで渡してから変更するか、ヒープ上に配列を作成してその変数を返します。 どちらもうまくいくが、最初は動的メモリ割り当てを必要とせず、正しく動作させる。

void returnArray(int size, char *retArray)
{
  // work directly with retArray or memcpy into it from elsewhere like
  // memcpy(retArray, localArray, size); 
}

#define ARRAY_SIZE 20

int main(void)
{
  char foo[ARRAY_SIZE];
  returnArray(ARRAY_SIZE, foo);
}



あなたのケースでは、スタック上に配列を作成していて、関数スコープを離れると配列の割り当てが解除されます。 代わりに、動的に割り当てられた配列を作成し、そのポインタを返します。

char * returnArray(char *arr, int size) {
    char *new_arr = malloc(sizeof(char) * size);
    for(int i = 0; i < size; ++i) {
        new_arr[i] = arr[i];
    }
    return new_arr;
}

int main() {

    char arr[7]= {1,0,0,0,0,1,1};
    char *new_arr = returnArray(arr, 7);

    // don't forget to free the memory after you're done with the array
    free(new_arr);

}



次のようなコードを使用することができます:

char *MyFunction(some arguments...)
{
    char *pointer = malloc(size for the new array);
    if (!pointer)
        An error occurred, abort or do something about the error.
    return pointer; // Return address of memory to the caller.
}

これを行うと、アドレスを解放してメモリを解放する必要があります。

他にもオプションがあります。 ルーチンは、既存の構造体の一部である配列(または配列の一部)へのポインタを返すことがあります。 呼び出し元は配列を渡すことがあり、ルーチンは新しい配列のためのスペースを割り当てるのではなく、単に配列に書き込むだけです。




配列をグローバル変数として宣言し、すべての関数がそれを編集できるようにします。 次に例を示します。

#include<stdio.h>
#include<conio.h> 


char a[1000];
void edit(char b);
main()
{
 edit(a);
 printf("%s", a);
}

void edit(char b)
{
 for(int i=0;i<sizeof(b);i++)
 a[i]=i;
}





Links