188

私は 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コンパイラは現時点では動作していないため、まだテストしていませんが、これを理解したいと思います

4

8 に答える 8

273

C の関数から配列を返すことはできません。これもできません (すべきではありません)。

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?
} 

returnedは自動保存期間で作成され、宣言スコープを離れると、つまり関数が戻ると、それへの参照は無効になります。

関数内でメモリを動的に割り当てるか、呼び出し元によって事前に割り当てられたバッファを埋める必要があります。

オプション1:

関数内で動的にメモリを割り当てます (割り当て解除を担当する呼び出し元ret)

char *foo(int count) {
    char *ret = malloc(count);
    if(!ret)
        return NULL;

    for(int i = 0; i < count; ++i) 
        ret[i] = i;

    return ret;
}

次のように呼び出します。

int main() {
    char *p = foo(10);
    if(p) {
        // do stuff with p
        free(p);
    }

    return 0;
}

オプション 2:

呼び出し元によって提供された事前に割り当てられたバッファーを埋める (呼び出し元が割り当てbufて関数に渡す)

void foo(char *buf, int count) {
    for(int i = 0; i < count; ++i)
        buf[i] = i;
}

そして、次のように呼び出します。

int main() {
    char arr[10] = {0};
    foo(arr, 10);
    // No need to deallocate because we allocated 
    // arr with automatic storage duration.
    // If we had dynamically allocated it
    // (i.e. malloc or some variant) then we 
    // would need to call free(arr)
}
于 2012-07-25T18:51:01.920 に答える
36

C の配列の扱いは Java とは大きく異なるため、それに応じて考え方を調整する必要があります。C の配列は第一級のオブジェクトではありません (つまり、配列式はほとんどのコンテキストでその「配列性」を保持しません)。C では、配列式がまたは 単項演算TTのオペランドである場合、または配列式は、宣言で別の配列を初期化するために使用される文字列リテラルです。 sizeof&

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

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

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

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

本当に興味がある場合は、Dennis Ritchie のThe Development of the C Languageを読んで、この処理がどこから来たのかを理解してください。

要するに、関数は配列型を返すことができないということです。これは、配列式も代入の対象にならないため問題ありません。

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

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);
  ...
}

もう 1 つの方法は、関数が動的に配列を割り当て、ポインターとサイズを返すことです。

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]アクセスします(配列型しかありませんが)。isrc

の N 要素配列へのポインターを宣言し、同様のことを行うことができます。T

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]);
  ...
}

上記のいくつかの欠点。まず第一に、古いバージョンの CSOME_SIZEはコンパイル時の定数であると想定しています。つまり、関数は 1 つの配列サイズでしか機能しません。次に、添字を適用する前にポインターを逆参照する必要があり、コードが乱雑になります。多次元配列を扱う場合は、配列へのポインターがより適切に機能します。

于 2012-07-25T19:59:13.897 に答える
18

これが最善の解決策であるとか、特定の問題に対する好ましい解決策であると言っているのではありません。ただし、関数は構造体を返すことができることを覚えておくと便利です。関数は配列を返すことはできませんが、配列を構造体でラップすることはでき、関数は構造体を返すことができるため、配列を一緒に運ぶことができます。これは、固定長配列で機能します。

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>

    typedef
    struct 
    {
        char v[10];
    } CHAR_ARRAY;



    CHAR_ARRAY returnArray(CHAR_ARRAY array_in, int size)
    {
        CHAR_ARRAY returned;

        /*
        . . . methods to pull values from array, interpret them, and then create new array
        */

        for (int i = 0;  i < size; i++ )
            returned.v[i] = array_in.v[i] + 1;

        return returned; // Works!
    } 




    int main(int argc, char * argv[])
    {
        CHAR_ARRAY array = {1,0,0,0,0,1,1};

        char arrayCount = 7;

        CHAR_ARRAY returnedArray = returnArray(array, arrayCount); 

        for (int i = 0; i < arrayCount; i++)
            printf("%d, ", returnedArray.v[i]);  //is this correctly formatted?

        getchar();
        return 0;
    }

この手法の長所と短所についてコメントをお待ちしています。私はそうする気にはなりませんでした。

于 2015-03-10T17:57:27.567 に答える
10

この美味しくて邪悪な実装はどうですか?

配列.h

#define IMPORT_ARRAY(TYPE)    \
    \
struct TYPE##Array {    \
    TYPE* contents;    \
    size_t size;    \
};    \
    \
struct TYPE##Array new_##TYPE##Array() {    \
    struct TYPE##Array a;    \
    a.contents = NULL;    \
    a.size = 0;    \
    return a;    \
}    \
    \
void array_add(struct TYPE##Array* o, TYPE value) {    \
    TYPE* a = malloc((o->size + 1) * sizeof(TYPE));    \
    TYPE i;    \
    for(i = 0; i < o->size; ++i) {    \
        a[i] = o->contents[i];    \
    }    \
    ++(o->size);    \
    a[o->size - 1] = value;    \
    free(o->contents);    \
    o->contents = a;    \
}    \
void array_destroy(struct TYPE##Array* o) {    \
    free(o->contents);    \
}    \
TYPE* array_begin(struct TYPE##Array* o) {    \
    return o->contents;    \
}    \
TYPE* array_end(struct TYPE##Array* o) {    \
    return o->contents + o->size;    \
}

main.c

#include <stdlib.h>
#include "array.h"

IMPORT_ARRAY(int);

struct intArray return_an_array() {
    struct intArray a;
    a = new_intArray();
    array_add(&a, 1);
    array_add(&a, 2);
    array_add(&a, 3);
    return a;
}

int main() {
    struct intArray a;
    int* it;
    int* begin;
    int* end;
    a = return_an_array();
    begin = array_begin(&a);
    end = array_end(&a);
    for(it = begin; it != end; ++it) {
        printf("%d ", *it);
    }
    array_destroy(&a);
    getchar();
    return 0;
}
于 2012-07-26T02:34:41.770 に答える
9

ここで報告されている他の回答と同様に、 (malloc()呼び出しを介して) ヒープ メモリを使用して実行できますが、常にメモリを管理する必要があります (関数を呼び出すたびにfree()関数を使用します)。静的配列でも実行できます。

char* returnArrayPointer() 
{
static char array[SIZE];

// do something in your array here

return array; 
}

メモリ管理を気にせずに使用できます。

int main() 
{
char* myArray = returnArrayPointer();
/* use your array here */
/* don't worry to free memory here */
}

この例では、配列定義で static キーワードを使用して、配列の有効期間をアプリケーション全体に設定する必要があるため、return ステートメントの後に破棄されません。もちろん、この方法では、アプリケーションの全期間にわたってメモリ内で SIZE バイトを占有するため、適切なサイズに設定してください。

于 2014-12-01T21:40:15.353 に答える
7

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

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);

}
于 2012-07-25T18:49:38.860 に答える
2

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

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);
}
于 2012-07-25T18:50:56.450 に答える
0

次のようなコードを使用できます。

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.
}

これを行うと、アドレスを free に渡すことによって、後でメモリを解放する必要があります。

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

于 2012-07-25T18:54:20.000 に答える