Gå til innhold

[Løst]Returnere array med allokert minne


Anbefalte innlegg

Prøver å allokere minne til en double* peker inni en funksjon og returnere dette til main(), men

uansett hva jeg prøver så har jeg ikke lenger tilgang til dette minneområdet i main() og ender med segmentation

fault. Aner at jeg kan få til dette med doble pekere, men er egentlig usiker på om det i det hele tatt er mulig.

Alltså:

 

main()

{

double *x;

n = doit(&x);

}

 

doit(**b)

{

n = 3;

*b = malloc(sizeof(double)*n);

*b[0] = 1;

*b[1] = 2;

*b[2] = 3;

return n;

}

 

 

Hva gjør jeg feil, og er det mulig å få til dette på måten jeg har tenkt?

 

Edit: Løst med temporært array og memcpy() i doit().

Endret av shakur
Lenke til kommentar
Videoannonse
Annonse
int doit(double **data)
{
 int n = 3;
 double *b = calloc(n, sizeof(double));
 b[0] = 1;
 b[1] = 2;
 b[2] = 3;
 *data = b;
 return n;
}

 

Skal fungere.

 

Takk, dette ser også ut til å fungere. Kunne du forklart meg hva som blir forskjellen av

å bruke *data = b, kontra memcpy(*data,b, sizeof(double)*n)?

Begge deler ser ut til å fungere.

 

Og er det lovlig å kalle free(b) rett etter *data = b?

Endret av shakur
Lenke til kommentar
<snip>

 

Takk, dette ser også ut til å fungere. Kunne du forklart meg hva som blir forskjellen av

å bruke *data = b, kontra memcpy(*data,b, sizeof(double)*n)?

Begge deler ser ut til å fungere.

 

Når du bruker *data = b sier du bare hvor dataene kan hentes, ved bruk av memcpy kopierer dataene.

f. eks. ved å ikke bruke memcpy vil en endring av b dataene også gi endring i *data dataene.... siden endringene skjer på samme sted

 

ps:

Ditt eksempel vil krasje med mindre *data peker til et området som kan ta imot alle dataene som memcpy "sender"

 

Og er det lovlig å kalle free(b) rett etter *data = b?

 

Ja lovlig, men ikke lurt (hvis du vil bruke dataene som b peker til)... Det er mye som er lov i C som ikke er så lurt :D

Lenke til kommentar

Hvis du frigjør b frigjør du også det *data peker på. Da vil du returnere en peker til uinitialisert minne, og hvis jeg husker helt feil så vil det gi "undefined behaviour" hvis du på et senere tidspunkt prøver å dereference denne pekeren. Av samme grunn skal man aldri referere en peker/referanse til en lokal variabel pga. at denne vil peke til uinitialisert minne når funksjonen returnerer. (De lokale variablene går out of scope og destrueres.)

Lenke til kommentar
Hvis du frigjør b frigjør du også det *data peker på. Da vil du returnere en peker til uinitialisert minne, og hvis jeg husker helt feil så vil det gi "undefined behaviour" hvis du på et senere tidspunkt prøver å dereference denne pekeren. Av samme grunn skal man aldri referere en peker/referanse til en lokal variabel pga. at denne vil peke til uinitialisert minne når funksjonen returnerer. (De lokale variablene går out of scope og destrueres.)

 

Men vil ikke *b i eksempelet til GeirGrusom være en lokal variabel, og hvorfor

vil ikke denne gå out of scope?

Og hva er egentlig forskjellen med at jeg sender **data som en dobbel-peker, kontra

hvis jeg ikke hadde gjort det?

Lenke til kommentar
main()

{

double *x;

n = doit(&x);

}

 

doit(**b)

{

n = 3;

*b = malloc(sizeof(double)*n);

*b[0] = 1;

*b[1] = 2;

*b[2] = 3;

return n;

}

 

Hva gjør jeg feil, og er det mulig å få til dette på måten jeg har tenkt?

 

Edit: Løst med temporært array og memcpy() i doit().

 

Det er mange feil over, og det er forunderlig om du i det hele tatt fikk kompilert den koden. Jeg ville løst problemet på følgende måte:

 

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

double *dbl_alloc(int n)
{
       int i;
       double *array;

       assert(n > 0);

       array = malloc(n * sizeof *array);
       if (NULL == array) {
               fprintf(stderr, "dbl_alloc: malloc fail of %d items\n", n);
               exit(EXIT_FAILURE);
       }

       for (i=0; i<n; i++) {
               array[i] = (double) i+1;
       }
       return array;
}

int main(void)
{
       int i, n = 3;
       double *x;

       x = dbl_alloc(n);
       for (i=0; i<n; i++) {
               printf("x[%d] = %f\n", i, x[i]);
       }
       return 0;
}

 

og når jeg kompilere og kjører denne koden, gir det følgende resultat

 

$ gcc -ansi -pedantic -Wall dyn_array.c

$ a.out

x[0] = 1.000000

x[1] = 2.000000

x[2] = 3.000000

Lenke til kommentar
main()

{

double *x;

n = doit(&x);

}

 

Feil 1: main() er K&R C, I standard C er det int main(void)

Feil 2: kaller funksjonen doit() uten at den er deklarert.

Feil 3: int funksjonen main() mangler retur verdi

 

doit(**b)

{

n = 3;

*b = malloc(sizeof(double)*n);

*b[0] = 1;

*b[1] = 2;

*b[2] = 3;

return n;

}

 

Feil 4: doit(**b) mangler type, riktig er int doit(double **b)

Feil 5: n = 3; mangler type, riktig er int n = 3;

Feil 6: #include <stdlib.h> mangler, trengs for malloc()

 

Hvis jeg retter alt dette, så blir resultatet

 

#include <stdlib.h>

int doit(double **b);

int main(void)
{
       double *x;
       int n;

       n = doit(&x);

       return 0;
}

int doit(double **b)
{
       int n = 3;
       *b = malloc(sizeof(double)*n);
       *b[0] = 1.0;
       *b[1] = 2.0;
       *b[2] = 3.0;
       return n;
}

Endret av kernel
Lenke til kommentar
main()

{

double *x;

n = doit(&x);

}

 

Feil 1: main() er K&R C, I standard C er det int main(void)

Feil 2: kaller funksjonen doit() uten at den er deklarert.

 

Kun prototypen til variadic functions er påkrevd ved kallstedet. Man kan kalle ikke-deklarerte funksjoner ellers.

 

Feil 3: int funksjonen main() mangler retur verdi

 

main() er særskilt, da manglende returverdi er ekvivalent med "return 0" (eller var det return EXIT_SUCCESS? Husker ikke standardformuleringen).

 

Feil 6: #include <stdlib.h> mangler, trengs for malloc()

 

Hvor krever standarden at prototypen til malloc skal være synlig på kallstedet?

Lenke til kommentar
Kun prototypen til variadic functions er påkrevd ved kallstedet. Man kan kalle ikke-deklarerte funksjoner ellers.

 

Ikke riktig.

 

Feil 3: int funksjonen main() mangler retur verdi

 

main() er særskilt, da manglende returverdi er ekvivalent med "return 0" (eller var det return EXIT_SUCCESS? Husker ikke standardformuleringen).

 

return 0 og return EXIT_SUCCESS er ekvivalente. Selv om main() i C90 ikke trenger return, så var ikke dette lenger tilfelle under C99.

 

Feil 6: #include <stdlib.h> mangler, trengs for malloc()

 

Hvor krever standarden at prototypen til malloc skal være synlig på kallstedet?

 

Det er Undefined Behaviour å glemme å inkludere riktig header, derfor anbefaler mange eksperter at man ikke bør caste retur verdi fra malloc() i C, selv om C++ krever en slik cast.

Lenke til kommentar

 

Selvfølgelig kompilerer ikke pseudo-koden jeg postet i starten, det kan

jo alle se. Var egentlig bare interessert i å finne ut av hvordan jeg kunne returnere

array-innholdet fra en metode. Ser ut som eksempelet til GeirGrusom funker bra her.

 

Kernel, eksempelet ditt er ikke helt ekvivalent med mitt. Du forutsetter at array-lengden

er definert før metodekallet og gis som parameter. Det har ikke jeg muligheten til i

prosjektet mitt.

Lenke til kommentar
Selvfølgelig kompilerer ikke pseudo-koden jeg postet i starten, det kan

jo alle se. Var egentlig bare interessert i å finne ut av hvordan jeg kunne returnere

array-innholdet fra en metode.

 

Hallo, metode er Java, i C har vi kun funksjoner!!

 

Det er en fordel at du poster minimal kode som lar seg kompilere og illustrerer problemet, ellers drukner man i feil og ser ikke skogen for bare trær. Når jeg ser på program snutten nå, så gikk jeg glipp av den feilen du antagelig lurte på, for bruk av temporær double peker er jo unødvendig. Problemet var operator presidens mellom * og [] i doit(), prøv med:

 

int doit(double **b)
{
       int n = 3;        

       *b = malloc(n * sizeof(double));        
       (*b)[0] = 1.0; 
       (*b)[1] = 2.0;
       (*b)[2] = 3.0;        
       return n;
}

 

 

Kernel, eksempelet ditt er ikke helt ekvivalent med mitt. Du forutsetter at array-lengden

er definert før metodekallet og gis som parameter. Det har ikke jeg muligheten til i

prosjektet mitt.

 

Huh? Når du allokerer array, så må du vite antall elementer det skal ha. Det å hardkode array lende inni en funksjon, er ikke standard måte å gjøre dette på, så

 

double *doit(int n)

 

eller

 

int doit(double **b, int n)

 

er adskillig bedre API design enn

 

int doit(double **b)

 

og du har definitivt mulighet til å gjøre det slik jeg anbefaler. Dessuten, bør du alltid sjekke retur verdi fra malloc(). Hvis exit() ikke er akseptabel i tilfelle malloc feiler, så kan fortsatt

 

double *doit(int n)

 

benyttes, ved å returnere NULL.

Endret av kernel
Lenke til kommentar

Veldig rett. Jeg pleier sjeldent å allokere minne inne i funksjoner for deretter å returnere en peker eller lignende, fordi jeg synes det er dårlig design.

Da er det bedre å allokere minne, og dereter å få funksjonen til å fylle det (dersom det er mulig selvsagt)

Det burde være opp til funksjonen som allokerer minnet å frigjøre det etterpå (igjen, kun hvis det er mulig)

Lenke til kommentar

Takker for hjelp og innspill. Feilen med operator-presdensen hadde jeg ikke tenkt på i

det hele tatt, godt sett!

Helt enig i det dere poengterer med hvor minnet skal allokeres, men dette var et spesielt tilfelle

hvor funksjonen gjør noen I/O operasjoner og må allokere minne på grunnlag av dette. Kan jo sikkert

deles opp i flere funksjoner osv, men, men.. Det funker nå, og lærte noe nytt i dag også.

Lenke til kommentar
Kun prototypen til variadic functions er påkrevd ved kallstedet. Man kan kalle ikke-deklarerte funksjoner ellers.

 

Ikke riktig.

 

Vel, jo, det er riktig:

 

6.2.1 (...) A function prototype is a declaration of a function that declares the types of its parameters.

 

6.5.2.2 (...) If the expression that denotes the called function has a type that does not include a

prototype, the integer promotions are performed on each argument, and arguments that have type float are promoted to double. (...)

 

Standarden åpner eksplisitt for muligheten av funksjonskall der prototypen er ukjent. Med mindre du har et annet sitat fra standarden, holder jeg fremdeles på at kallet av funksjoner hvis prototype er ukjent er tillatt. Your move.

 

Feil 3: int funksjonen main() mangler retur verdi

 

main() er særskilt, da manglende returverdi er ekvivalent med "return 0" (eller var det return EXIT_SUCCESS? Husker ikke standardformuleringen).

 

return 0 og return EXIT_SUCCESS er ekvivalente. Selv om main() i C90 ikke trenger return, så var ikke dette lenger tilfelle under C99.

 

Jo:

 

5.1.2.2.3 Program termination (...) reaching the } that terminates the

main function returns a value of 0. (...)

 

(Dette er fra 9899:1999 (E), for ordens skyld).

 

Feil 6: #include <stdlib.h> mangler, trengs for malloc()

 

Hvor krever standarden at prototypen til malloc skal være synlig på kallstedet?

 

Det er Undefined Behaviour å glemme å inkludere riktig header, (...)

 

Nei, det har jeg allerede poengtert. Jeg spør igjen, hvor krever standarden at prototypen til malloc (eller en vilkårlig annen ikke-variadic funksjon) er påkrevd på kallstedet?

 

edit: trykkfeil

Endret av zotbar1234
Lenke til kommentar
Kun prototypen til variadic functions er påkrevd ved kallstedet. Man kan kalle ikke-deklarerte funksjoner ellers.

 

Ikke riktig.

 

Vel, jo, det er riktig:

 

6.2.1 (...) A function prototype is a declaration of a function that declares the types of its parameters.

 

6.5.2.2 (...) If the expression that denotes the called function has a type that does not include a

prototype, the integer promotions are performed on each argument, and arguments that have type float are promoted to double. (...)

 

Standarden åpner eksplisitt for muligheten av funksjonskall der prototypen er ukjent. Med mindre du har et annet sitat fra standarden, holder jeg fremdeles på at kallet av funksjoner hvis prototype er ukjent er tillatt. Your move.

 

Tror nok du misforstår her, under C90 så kunne man droppe deklarasjon av funksjoner,

 

3.3.2.2 Function calls

[...]

If the expression that precedes the parenthesized argument list in

a function call consists solely of an identifier, and if no

declaration is visible for this identifier, the identifier is

implicitly declared exactly as if, in the innermost block containing

the function call, the declaration

 

extern int identifier();

men merk her at dette ikke er kompatibelt med prototypen til malloc()! Under C99 er denne implisitte deklarasjonen droppet, fordi det gjør det enklere å finne bugs, C99 Rationale sier

 

6.5.2.2 Function calls

[...]

A new feature of C99: The rule for implicit declaration of functions has been removed in C99.

The effect is to guarantee the production of a diagnostic that will catch an additional category of

programming errors. After issuing the diagnostic, an implementation may choose to assume an

implicit declaration and continue translation in order to support existing programs that exploited

this feature.

 

poenget med funksjons deklarasjoner og prototyper, er altså å unngå kjipe bugs og få bedre feilmeldinger under kompilering.

 

Feil 3: int funksjonen main() mangler retur verdi

 

main() er særskilt, da manglende returverdi er ekvivalent med "return 0" (eller var det return EXIT_SUCCESS? Husker ikke standardformuleringen).

 

return 0 og return EXIT_SUCCESS er ekvivalente. Selv om main() i C90 ikke trenger return, så var ikke dette lenger tilfelle under C99.

 

Jo:

 

5.1.2.2.3 Program termination (...) reaching the } that terminates the

main function returns a value of 0. (...)

 

(Dette er fra 9899:1999 (E), for ordens skyld).

 

OK, jeg husket ikke riktig der, og blandet noe om på C90 og C99. Uansett, hvis return eller exit mangler under C90, så

er exit status til programmet udefinert. Dette er en SW defekt, så lenge verden består stort sett av C90 kompilatorer og man ikke bedriver embedded. At en implisitt

return 0;

nå gjøres under C99, er ikke en unnskyldning for å droppe return, når man enkelt kan skrive portabel kode/program. Forøvrig, inntil Microsoft og GNU GCC har implementert C99, så bør vi skrive kode/program som virker likt under C90 og C99.

Endret av kernel
Lenke til kommentar

"return 0;" er ihvertfall ekvivalent med "xor eax, eax"

Men er dette spesifikt for main? altså at void main kaller "return 0;" selv om returverdien er void?

For void funksjoner ville jo verdien i EAX være irrelevant, men main funksjonen må jo egentlig returnere en verdi enten en vil eller ikke.

Endret av GeirGrusom
Lenke til kommentar
"return 0;" er ihvertfall ekvivalent med "xor eax, eax"

 

Den abstrakte C maskinen som er definert i standarden, sier ingenting om hvilke CPU registre eller hvordan returverdi overføres på. En C implementasjon, står fritt til å gjøre dette via stack for eksempel.

 

Men er dette spesifikt for main? altså at void main kaller "return 0;" selv om returverdien er void?

 

Så lenge du ikke bedriver embedded programmering, så er det ikke noe som heter void main(), standard C har

 

int main(void)

eller

int main(int argc, char *argv[])

 

C90 standarden sier følgende:

 

2.1.2.2 Hosted environment

 

A hosted environment need not be provided, but shall conform to the

following specifications if present.

 

"Program startup"

 

The function called at program startup is named main . The

implementation declares no prototype for this function. It can be

defined with no parameters:

 

int main(void) { /*...*/ }

 

or with two parameters (referred to here as argc and argv , though any

names may be used, as they are local to the function in which they are

declared):

 

int main(int argc, char *argv[]) { /*...*/ }

 

Det er hosted implementasjoner som støtter void main(), spesielt ser man slike uvaner under Windows med VC++ kompilatoren. For å skru av implementasjon spesifikke språk utvidelser fra MS, husk å bruke ANSI modus via /Za opsjonen:

 

cl /Za main.c

Lenke til kommentar

Opprett en konto eller logg inn for å kommentere

Du må være et medlem for å kunne skrive en kommentar

Opprett konto

Det er enkelt å melde seg inn for å starte en ny konto!

Start en konto

Logg inn

Har du allerede en konto? Logg inn her.

Logg inn nå
×
×
  • Opprett ny...