Sida 1 av 1

Ett annat C-kods problem

Postat: 17 april 2007, 02:51:19
av TomasL
Funderar på om detta är tillåtet i ANSI-C, har ej hittat nått vettit svar i K&R:

Kod: Markera allt

void func(void)
    {
       char a[20];
       a[]=.................................; // bry er int om denna rad, är lat bara :) 
       if (a[4])
                DWORD *p=dworddata;
       else
                FLOAT *p=floatdata;
       .....Övrig kod
    }
Gissar att det borde vara ok.
Vad jag vill göra är föjande:
Via serieporten tar jag emot ett datagram, en viss byte i datagrammet talar om längden på datagrammet, 8 eller 16 byte.
Jag använder 2 olika strukturer, ett för det långa och ett för det korta datagrammet.
Det min serierutin tar emot datgrammet och placerar det i en temporär matris "a" i exemplet ovan
sedan, beroende på längden skall datagrammet kopieras över till resp struktur så jag kan använda det sedan.

Frågan är om ANSI-C accepterar att jag deklarerar och samtidigt tilldelar pekaren "p" i en if-sats?

Det borde fungera tycker jag, men?

Nån som har nån kommentar.

Postat: 17 april 2007, 03:03:59
av bearing
Jag är ganska säker på att man inte kan deklarera variabler alls "mitt i" programmet i ANSI-C.

Du får antagligen deklarera två olika varibler i början av programmet.

Postat: 17 april 2007, 07:15:10
av Icecap
En av sakerna som blev adderat i C++ var möjligheten att deklarera variabler mitt i. Dessutom är det en annan sak som gör att det inte fungerar även i C++:

Kod: Markera allt

void func(void)
    {
       char a[20];
       a[]=.................................; // bry er int om denna rad, är lat bara :)
       if (a[4])
                DWORD *p=dworddata;
       else
                FLOAT *p=floatdata;
  // Precis när if/else lämnas försvinner det lokala scopet för dessa variabler
       .....Övrig kod
    }

Alltså kan du istället deklarera:
union
  {
  DWORD *p_dw;
  FLOAT *p_fl;
  } Common;
Då kan du komma åt 2 variabler som är exakt lika:
Common.p_dw som DWORD-pekare
Common.p_fl som FLOAT-pekare
De delar minnesplats och om du sätter den ena sätter du per definition den andra också.

Men egentligen kan det kvitta totalt med denna uppdelning, en pekare är en pekare!
Deklarera alltså
void * p;
och använd den, när du senare ska räkna och kompilern gnäller får du cast'a den, FLOAT-pekare = (FLOAT*)p och DWORD-pekare = (DWORD*)p

Postat: 17 april 2007, 12:53:59
av Ulf
Så som koden kan tolkas så har Icecap helt rätt, du löser det med en union.
Det slulle iofs gå med void* p, men då blir det jobbigt att typecasta hela tiden.

Säger inte ansi-c att deklaration av en variabel ska ske i början av ett block?

Tex

void func(void)
{
char a[20];
...
if( a[0] == '1' )
{
int A;
/* gör något med A */
...

/* Aslutar existera när man är ute ur blocket */
}
}

Sen har vi ju POSIX också...

Postat: 17 april 2007, 14:54:22
av TomasL
Har funderat på en union, men, iofs var exemplet lite dåligt, eftersom pekaren skall peka på en av två olika strukturer med olika antal medlemmar.
Med en union blir jag fortfarande tvungen att adressera resp struktur individuellt.

Tror lösningen med att typecasta en void pekare är en bättre lösning.

Postat: 17 april 2007, 15:31:39
av Millox
Deklareringssättet är ANSI C endast i standarden C99, inte i den gamla standarden. GNU CC använder dock inte C99 som standard, men andra kan göra det.

Som Icecap säger är det dock scopet för variablerna som gör att det inte kommer funka. Använd istället void* och håll reda på hur du använder variabeln genom att casta den rätt istället; om du prompt måste lösa det med variabler med samma namn.

Alltså:

void *p = NULL;

(DOUBLE*)p = apa;
(FLOAT *)p = bepa;

Postat: 17 april 2007, 18:39:14
av TomasL
Orsaken är att jag vill spara kod-utrymme.

Känns lite onödigt att dubblera funktionen, därför vill jag göra på detta sättet.

Postat: 17 april 2007, 19:23:13
av Icecap
Inte sparar du någon kodplats på det vis, du sparar bara lite RAM-minne (en pekare).

Postat: 17 april 2007, 20:03:17
av TomasL
Borde göra det.


I princip bör det bli så här:

Kod: Markera allt

void *p;
if (a[4]=8)
    (shortstruct*) p=shorstruct;
else (longstruct*) p=longstruct;

strcopy(p, &a);


Annars blir det så här:
shortstruct *p;
lonstruct *p1;
if (a[4]=8)
    strcopy(p, &a)
else
    strcopy(p1,&a)

Det borde rimligtvis spara lite tycker jag.
eftersom pekaren och a[] är lokala tar de inte upp nån plats i ram, skillnaden blir att jag sparar in ett hopp till strcopy och därmed en del kod som annars går åt för att skapa hoppet, stackhantering mm.

Postat: 17 april 2007, 20:21:39
av Mupp
Kompilatorer är fantastiska saker. Vilket innebär att dom inte alltid gör som man tänkt. Har du testat så att du vet att det spar det du tänkt spara? Det känns som att -Os (eller motsvarande) kommer påverka mängden programminne mer är "fulhack", om du ursäktar. Optimering brukar gå sämre om man försöker förvirra kompilatorn, vilket inte är alltför svårt, för att inte tala om hur förvirrad man själv blir när man läser det en vecka efter man skrivit det. Mitt råd är att testa båda och avgöra om det är värt det, jag är beredd att sätta din nästa månadslön på att det inte lönar sig alltför mycket.

Edit, syftningsfel.

Postat: 18 april 2007, 00:48:14
av TomasL

Kod: Markera allt

	char tempdata[21];
	char *p;
	char test=tempdata[3];
	LONGSTRUCT *pl=&longdata;
	SHORTSTRUCT *ps=&shortdata;
	
	if (test==15)
		p= &shortdata.v[0];
	else  p= &longdata->v[0];
	vstrcpy(p, tempdata);
	
	//den andra varianten
	if (test==15)
		strcpy(ps->v, tempdata);
	else strcpy(pl->v, tempdata);
Jag blev tvingad att deklarera typerna som en union mellan de egentilga strukturerna och en char v[].

Den första varianten tar 60 byte totalt och den andra 68 byte totalt. (11%) besparing av kodminne, vilket är rätt mycket, med tanke på PICarnas begränsade utrymme

Edit: Det skiljer 12 byte mellan de olika metoderna.