web-gelistirme-sc.com

C kullanarak bir dizi döndürme

C için nispeten yeniyim ve dizilerle ilgili yöntemlerde biraz yardıma ihtiyacım var. Java programlamadan gelince, bir diziyi döndürmek için int [] method()in diyebilirim. Ancak, C ile bunları döndürdüğünüzde diziler için işaretçiler kullanmanız gerektiğini öğrendim. Yeni bir programcı olarak, gerçekten çok fazla anlamıyorum, hatta içine girdiğim birçok forumda bile.

Temel olarak, C'de bir char dizisi döndüren bir yöntem yazmaya çalışıyorum. Ben bir dizilimle birlikte yöntemi sunacağım (returnArray olarak adlandıralım). Önceki diziden yeni bir dizi oluşturur ve ona bir işaretçi döndürür. Sadece bunun nasıl başlatılacağı ve dizilimden çıktıktan sonra işaretçinin nasıl okunacağı konusunda yardıma ihtiyacım var. Bunu açıklayan herhangi bir yardım takdir edilmektedir.

Dizi İade Fonksiyonu için Önerilen Kod Formatı

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

İşlevin Arayan

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 derleyicim şu anda çalışmadığı için bunu henüz test etmedim, ancak bunu çözmek istiyorum

129
user1506919

C'deki işlevlerden dizileri döndüremezsiniz. Bunu da yapamazsınız (yapmamalısınız):

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, otomatik saklama süresi ile yaratılır ve beyan kapsamını terk ettiği zaman, yani fonksiyon geri döndüğünde referanslar geçersiz hale gelir.

Dinamik olarak işlevin içine bellek ayırmanız veya arayan tarafından sağlanan önceden ayrılan bir ara belleği doldurmanız gerekir.

Seçenek 1:

dinamik olarak fonksiyonun içine hafıza tahsis eder (ayrılmasından sorumlu olan arayan 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;
}

Öyle diyoruz:

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

    return 0;
}

Seçenek 2:

arayan tarafından sağlanan önceden ayrılan bir tamponu doldurma (arayan buf ayırır ve işleve geçer)

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

Ve öyle diyoruz:

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)
}
193
Ed S.

C dizileriyle ilgili davranışlar Java'dan çok farklıdır ve düşüncenizi buna göre ayarlamanız gerekir. C dizileri birinci sınıf nesneler değildir (yani, bir dizi ifadesi çoğu bağlamda "dizi-niteliğini" korumaz). C'de, "N eleman dizisi _ T" türündeki bir ifade, dizi ifadesinin bir "hariç" olduğu için "pointer" T "türüne dönüştürülür. sizeof veya unary & operatörünün operand'ı veya dizi ifadesi bir bildirgede başka bir dizi başlatmak için kullanılan bir dize değişmezi ise.

Diğer şeylerin yanı sıra, bu, bir dizi ifadesini bir işleve iletemeyeceğiniz ve dizi türü olarak aldığınız anlamına gelir ; işlev aslında bir işaretçi türü alır:

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

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

foo çağrısında, str ifadesi, char [6] türünden char * türüne dönüştürülür, bu nedenle foo öğesinin ilk parametresi char *a yerine char a[6]. sizeof str içinde, dizi ifadesi sizeof operatörünün bir işleneni olduğundan, işaretçi türüne dönüştürülmez, bu nedenle dizideki bayt sayısını (6) alırsınız.

Gerçekten ilgileniyorsanız ilgileniyorsanız, bu tedavinin nereden geldiğini anlamak için Dennis Ritchie'nin C Dilinin Gelişimi 'i okuyabilirsiniz. .

Sonuç, işlevlerin dizi türlerini döndürememesidir, çünkü dizi ifadeleri de bir ödevin hedefi olamaz.

En güvenli yöntem, arayanın diziyi tanımlaması ve adresini ve boyutunu, kendisine yazması gereken işleve iletmesidir:

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

Diğer bir yöntem, fonksiyonun diziyi dinamik olarak tahsis etmesi ve işaretçi ile boyutu döndürmesidir:

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

Bu durumda arayan, dizinin free library işlevi ile ayrılmasından sorumludur.

Yukarıdaki koddaki dst, char dizisine bir işaretçi değil, char öğesine basit bir işaretçidir. C'nin işaretçi ve dizi semantiği, [] alt işlem operatörünü dizi türündeki bir ifadeye veya işaretçi türüne uygulayabileceğiniz şekildedir; hem src[i] hem de dst[i], dizinin i 'öğesine erişecek (yalnızca src dizi türüne sahip olsa da).

Siz bir N-element dizisini T ile tanımlayabilir ve benzer bir şey yapabilirsiniz:

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

Yukarıdakilerle ilgili dezavantajlar. Her şeyden önce, C'nin eski sürümleri SOME_SIZE öğesinin bir derleme zamanı sabiti olmasını bekler, yani bu işlev yalnızca bir dizi büyüklüğüyle çalışır. İkincisi, kodu kesen bir alt dizini uygulamadan önce işaretçiyi serbest bırakmanız gerekir. Dizilere yönelik işaretçiler, çok boyutlu dizilerle ilgilenirken daha iyi çalışır.

24
John Bode

Bu lezzetli kötülük uygulamasına ne dersiniz?

array.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;
}
8
pyrospade

Bunun verilen soruna en iyi çözüm ya da tercih edilen bir çözüm olduğunu söylemiyorum. Bununla birlikte, fonksiyonların yapılar döndüğünü hatırlamakta fayda olabilir. İşlevler dizileri döndüremese de, diziler yapılara sarılabilir ve işlev yapıyı geri getirebilir, böylece diziyi de beraberinde taşıyabilir. Bu, sabit uzunluklu diziler için işe yarar.

    #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;
    }

Bu tekniğin güçlü ve zayıf yönlerini yorumluyorum. Bunu yapma zahmetinde bulunmadım.

6
Indinfer

Sizin durumunuzda, yığında bir dizi yaratıyorsunuz ve işlev kapsamından çıktıktan sonra dizi ayrılacak. Bunun yerine, dinamik olarak ayrılmış bir dizi oluşturun ve ona bir işaretçi getirin.

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

}
6
Man of One Way

Burada bildirilen diğer cevaplar gibi yığın belleği ((malloc () çağırma) aracılığıyla) yapabilirsiniz, ancak her aradığınızda her zaman belleği yönetmelisiniz (use free () işlevini kullanın). işleviniz). Ayrıca statik bir diziyle de yapabilirsiniz:

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

// do something in your array here

return array; 
}

Bellek yönetimi konusunda endişelenmeden kullanabilirsiniz.

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

Bu örnekte, dizi kullanım ömrüne ayarlamak için dizi tanımında statik anahtar sözcüğü kullanmalısınız, bu nedenle return ifadesinden sonra imha edilmeyecektir. Tabii ki, bu şekilde tüm uygulama ömrü boyunca hafızanızdaki SIZE baytını işgal edersiniz, bu nedenle doğru boyutlandırın!

4
mengo

Metodunuz kötü bir şekilde başarısız olacak bir yerel yığın değişkeni döndürecektir. Bir dizi döndürmek için, işlev dışında bir tane oluşturun, adrese göre işleve iletin, ardından değiştirin veya öbek üzerinde bir dizi oluşturun ve bu değişkeni döndürün. Her ikisi de işe yarar, ancak ilki düzgün çalışması için herhangi bir dinamik bellek ayırma gerektirmez.

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);
}
2
Michael Dorgan

Bu gibi bir kod kullanabilirsiniz:

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

Bunu yaptığınızda, adres serbest bırakılarak hafıza daha sonra serbest bırakılmalıdır.

Başka seçenekler var. Bir rutin bir göstericiyi mevcut bir yapının parçası olan bir diziye (veya dizinin bir kısmına) döndürebilir. Arayan bir diziyi geçebilir ve rutin yeni bir diziye alan ayırmak yerine yalnızca diziye yazar.

0
Eric Postpischil