shakur Skrevet 25. mai 2009 Del Skrevet 25. mai 2009 (endret) 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 25. mai 2009 av shakur Lenke til kommentar
GeirGrusom Skrevet 25. mai 2009 Del Skrevet 25. mai 2009 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. Lenke til kommentar
Kagee Skrevet 25. mai 2009 Del Skrevet 25. mai 2009 (endret) Heller int doit(double **b) enn int doit(double **data) regner jeg med? Endret 25. mai 2009 av Kagee Lenke til kommentar
shakur Skrevet 28. mai 2009 Forfatter Del Skrevet 28. mai 2009 (endret) 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 28. mai 2009 av shakur Lenke til kommentar
Giddion Skrevet 29. mai 2009 Del Skrevet 29. mai 2009 <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 Lenke til kommentar
Dead_Rabbit Skrevet 30. mai 2009 Del Skrevet 30. mai 2009 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
shakur Skrevet 1. juni 2009 Forfatter Del Skrevet 1. juni 2009 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
GeirGrusom Skrevet 1. juni 2009 Del Skrevet 1. juni 2009 *b går ut av scope ja, men jeg gir verdien til den og legger det der **data peker til. **data vil jo også gå ut av scope, men ikke minnet som **data peker (som er en double*) Lenke til kommentar
kernel Skrevet 2. juni 2009 Del Skrevet 2. juni 2009 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
kernel Skrevet 2. juni 2009 Del Skrevet 2. juni 2009 (endret) 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 2. juni 2009 av kernel Lenke til kommentar
zotbar1234 Skrevet 2. juni 2009 Del Skrevet 2. juni 2009 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
kernel Skrevet 2. juni 2009 Del Skrevet 2. juni 2009 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
shakur Skrevet 3. juni 2009 Forfatter Del Skrevet 3. juni 2009 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
kernel Skrevet 3. juni 2009 Del Skrevet 3. juni 2009 (endret) 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 3. juni 2009 av kernel Lenke til kommentar
GeirGrusom Skrevet 3. juni 2009 Del Skrevet 3. juni 2009 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
shakur Skrevet 3. juni 2009 Forfatter Del Skrevet 3. juni 2009 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
zotbar1234 Skrevet 4. juni 2009 Del Skrevet 4. juni 2009 (endret) 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 4. juni 2009 av zotbar1234 Lenke til kommentar
kernel Skrevet 5. juni 2009 Del Skrevet 5. juni 2009 (endret) 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 5. juni 2009 av kernel Lenke til kommentar
GeirGrusom Skrevet 5. juni 2009 Del Skrevet 5. juni 2009 (endret) "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 5. juni 2009 av GeirGrusom Lenke til kommentar
kernel Skrevet 5. juni 2009 Del Skrevet 5. juni 2009 "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
Anbefalte innlegg
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 kontoLogg inn
Har du allerede en konto? Logg inn her.
Logg inn nå