Spatny vypocet v Cecku - datove typy?

Diskuze a poradna o programátorech a programování různých obvodů

Moderátor: Moderátoři

Zpráva
Autor
Uživatelský avatar
rnbw
Příspěvky: 37419
Registrován: 21 bře 2006, 00:00
Bydliště: Bratislava

#16 Příspěvek od rnbw »

Tento pripad (na rozdiel od toho povodneho) vie kompilator jednoducho odhalit, preto je vhodne mat zapnute vsetky varovania:

Kód: Vybrat vše

$ cc -Wall -Wextra test.c -o test
test.c: In function ‘main’:
test.c:3:5: warning: comparison of unsigned expression >= 0 is always true [-Wtype-limits]
Uživatelský avatar
masterboy
Příspěvky: 29
Registrován: 09 úno 2013, 00:00

#17 Příspěvek od masterboy »

OK ;) Dam tedy na Vase rady ;)

Jen bych mel jeste jeden dotaz. Kdyz mam definovane napr. dvourozmerne pole prvku, ktere je rozmeru napr. 16x21 a prochazim jej pomoci cyklu FOR, je rychlejsi pouzivat ukazatel na tuto promennou nebo mohu pracovat se samotnou promennou?
Uživatelský avatar
Panda38
Příspěvky: 725
Registrován: 21 lis 2012, 00:00
Bydliště: Most, Praha, Lanžhot
Kontaktovat uživatele:

#18 Příspěvek od Panda38 »

EDIT: na tenhle odstavec ses asi neptal (došlo mi později), spíš až na ten druhý ... S ukazatelem do dvourozměrného pole je potíž, že není jednoznačně dané, s jakou prioritou jsou indexy v paměti uložené (zda se mění nejdříve první nebo druhý index) - některé překladače to ukládají opačně (myslím že častější případ je měnit přednostně druhý index, ale nejsem si tím teď jistý). Když se vysleduje pořadí pro konkrétní překladač, tak se dá dobře pracovat s ukazatelem, ale při přechodu na jiný překladač se musí dát pozor zda to není jinak. Lépe se proto dělá s jednorozměrným polem a indexy slučovat sám, je to jednoznačnější. Na výkon procesoru je to stejné. Dá se pak snadno kopírovat blok pomocí memcpy.

Asi ses možná ptal spíš na ukazatel na celé pole. Pokud se jedná o globální proměnnou, tak přímý přístup k prvkům pole může být rychlejší když se použije přímo proměnná, protože program sahá přímo do paměti a nemusí počítat offset v poli. Ale i pro procházení cyklem je práce se jménem pole nepatrně rychlejší, nemusí se načítat z lokální proměnné ukazatel na pole. .... Ale většinou je rozdíl docela nepodstatný a je to celkem jedno.
Uživatelský avatar
masterboy
Příspěvky: 29
Registrován: 09 úno 2013, 00:00

#19 Příspěvek od masterboy »

Aha, tak to pak je asi fuk. Ja jsem si prave myslel, ze je rychlejsi pristupovat k vicerozmernemu poli pres ukazatel ;)

A kdy je nejvhodnejsi pouzit ukazatel? V jakych pripadech? Porad jen o tom ctu, ale zatim jsem se nikdy nedostal k tomu abych to pouzil.
Uživatelský avatar
Panda38
Příspěvky: 725
Registrován: 21 lis 2012, 00:00
Bydliště: Most, Praha, Lanžhot
Kontaktovat uživatele:

#20 Příspěvek od Panda38 »

Ukazatel je užitečný např. když se předává funkci adresa proměnné (nebo pole), se kterou má provést nějakou operaci nebo aby mohla vrátit více výsledků (do více proměnných). Nebo pro usnadnění přístupu do pole, aby se nemusel stále počítat index.

Např. když je obrazový buffer:
u8 canvas[DISPW*DISPH];

tak zápis horizontální linky lze udělat:
for (i = w; i > 0; i--) { canvas[y*DISPW+x] = col; x++; }

což vyžaduje opakovaný výpočet adresy, efektivnější je zapisovat s ukazatelem:
u8* d = &canvas[y*DISPW + x];
for (i = w; i > 0; i--) *d++ = col;
Uživatelský avatar
mtajovsky
Příspěvky: 3728
Registrován: 19 zář 2007, 00:00
Bydliště: Praha

#21 Příspěvek od mtajovsky »

Panda38 píše:S ukazatelem do dvourozměrného pole je potíž, že není jednoznačně dané, s jakou prioritou jsou indexy v paměti uložené (zda se mění nejdříve první nebo druhý index) - některé překladače to ukládají opačně (myslím že častější případ je měnit přednostně druhý index, ale nejsem si tím teď jistý).
K tomu bych se vyjádřil. Mluvíme o céčku, předpokládám.

1) pro vícerozměrné pole je jednoznačně dáno, který index patří ke kterému rozměru. Pro dvourozměrné pole indexuje první index 1-rozměrná pole a druhý pak samotné prvky. Analogicky pro vícerozměrná pole. Při procházení po prvcích se nejrychleji mění poslední index, pak předposlední a tak dále. Názorně je to vidět na výpisu z gdb:
Breakpoint 1, main (argc=1, argv=0x7fffffffe628) at prog.c:3
3 int P[2][2] = {{0,0},{0,0}};
(gdb) n
5 P[1][0] = 2;
(gdb) p P
$1 = {{0, 0}, {0, 0}}
(gdb) n
6 P[1][1] = 3;
(gdb) p P
$2 = {{0, 0}, {2, 0}}
(gdb) n
7 return 0;
(gdb) p P
$3 = {{0, 0}, {2, 3}}
(gdb)

Je to dáno normou již od K&R. :)

2) Co se týče použití proměnné pole versus pointery, tak uvažte následující:

Kód: Vybrat vše

int main(int argc, char** argv)
{
char p1[5] = {"ahoj"};
char p2[5] = {"    "};
char* ptr1 = p1;
char* ptr2 = p2;
int i = 0;

// --------- pointery -------
    while(*ptr1)
        *ptr2++ = *ptr1++;

// --------- pole ------------
    while(p1[i])
       p2[i] = p1[i++];
       
    return 0;
}

Verze kopírování s pointery bude rychlejší, protože odpadá sčítání indexu a adresy pole s použitím pointerové aritmetiky.
Uživatelský avatar
Panda38
Příspěvky: 725
Registrován: 21 lis 2012, 00:00
Bydliště: Most, Praha, Lanžhot
Kontaktovat uživatele:

#22 Příspěvek od Panda38 »

mtajovsky píše:Je to dáno normou již od K&R.
Sice jsem se setkal s opačným uspořádáním v paměti, ale je možné že šlo o předpotopní překladače a snad se tedy bude dát spolehnout na dodržování normy.
Uživatelský avatar
mtajovsky
Příspěvky: 3728
Registrován: 19 zář 2007, 00:00
Bydliště: Praha

#23 Příspěvek od mtajovsky »

Není to vyloučeno, ale prošel jsem už toho dost, co si pamatuju, tak DECUS C na PDP11/45, ATARI C, Borlandy od TC 2.0 až po BC++ 5.02, BCB 1.0 - 5.?, MS C od MSVC++ 5.0 až po VS2010, implementace gcc pro Linux, HPUX, Sun a Alpha, které mám "tu čest" na našich serverech používat a C pro Atmel AVR to tak mají. Tuhle konvenci dokonce používá i Java nebo C# a tak si myslím, že se na to dá spolehnout
Uživatelský avatar
masterboy
Příspěvky: 29
Registrován: 09 úno 2013, 00:00

#24 Příspěvek od masterboy »

Takze co z toho plyne? ;) Mam tedy pouzivat pointery na vicerozmerne pole?

PS.: Bavime se tady od C-ecku pro uP.
Uživatelský avatar
AB1
Příspěvky: 312
Registrován: 23 lis 2009, 00:00

#25 Příspěvek od AB1 »

Z toho myslím plyne, že můžeš používat buď indexy nebo ukazatele, co ti lépe vyhovuje.
Jestli tě zajímá co bude rychlejší, udělej si stejný příklad na oba způsoby a v simulátoru zjisti za jaký čas proběhnou.
Uživatelský avatar
Panda38
Příspěvky: 725
Registrován: 21 lis 2012, 00:00
Bydliště: Most, Praha, Lanžhot
Kontaktovat uživatele:

#26 Příspěvek od Panda38 »

Simulátor, nebo .... Na měření rychlosti používám LED - volám ve smyčce opakovaně funkci a po daném počtu překlopím LED. Podle intervalu blikání se dá docela dobře měřit doba zpracování funkce.
Uživatelský avatar
piitr
Příspěvky: 1003
Registrován: 19 říj 2007, 00:00
Kontaktovat uživatele:

#27 Příspěvek od piitr »

Ohledně dvourozměrného pole bych se přidal k mtajovskému.
Můžu navíc přidat příklad ze specifikace, tam je to pěkně vysvětleno:
Example

Consider the array object defined by the declaration

int x[3][5];

Here x is a 3x5 array of int s; more precisely, x is an array of three
member objects, each of which is an array of five int s. In the
expression x[i] , which is equivalent to (*(x+(i))) , x is first
converted to a pointer to the initial array of five int s. Then i is
adjusted according to the type of x , which conceptually entails
multiplying i by the size of the object to which the pointer points,
namely an array of five int objects. The results are added and
indirection is applied to yield an array of five int s. When used in
the expression x[i][j] , that in turn is converted to a pointer to the
first of the int s, so x[i][j] yields an int.

Možná budu trochu mimo, ale zahlédl jsem pár věcí, na které bych rád upozornil.
Ale jak říkám, možná plaším zbytečně.
mtajovsky píše:char p1[5] = {"ahoj"};
Tohle mi přijde trochu divné. Spíš bych to psal takhle:

Kód: Vybrat vše

char p1[5] = "ahoj";
Je ale možné, že to překladač zvládne, nevím.
mtajovsky píše:p2[i] = p1[i++];
Tohle mi přijde trochu nebezpečné. U operátoru přiřazení se výslovně píše:
The order of evaluation of the operands is unspecified.
Uživatelský avatar
mtajovsky
Příspěvky: 3728
Registrován: 19 zář 2007, 00:00
Bydliště: Praha

#28 Příspěvek od mtajovsky »

p2[i] = p1[i++]; - jasně, je to jen příklad na ilustraci ukazatelů versus indexů.

char p1[5] = {"ahoj"};
je ok, jinak by nemohlo fungovat:

char p[2][5] = {"ahoj", "cau!"};

masterboy píše:Takze co z toho plyne? ;) Mam tedy pouzivat pointery na vicerozmerne pole?
To je složitější. Ačkoliv projde:

char pole[2][5];
char** ptr = (char**)pole;

tak je problém. Pole si třeba gcc ukládá lineárně a konkrétní offset se počítá z rozsahu dimenzí. Ale v char** nejsou jednak nikde dimenze uchovány a za druhé je to pointer na vektor charů reprezentovaný opět pointerem. Proto není možno do pole přistupovat:
char c = ptr[1][3];

protože pointerová aritmetika vynásobí hned druhý index 4 (32 bit) nebo 8 (64 bit).

To by se muselo:
char c = ((char*)ptr)[1*5 + 3];

nebo
char c = *(((char*)ptr)+(1*5 + 3));

nebo nadefinovat jen:
char* ptr = (char*)pole;

takže pro procházení vícerozměrného pole můžete používat pointer, ale musíte si obsluhovat sám dimenze a jsme tam, kde jsme byli.
Uživatelský avatar
piitr
Příspěvky: 1003
Registrován: 19 říj 2007, 00:00
Kontaktovat uživatele:

#29 Příspěvek od piitr »

Asi se ty pointry používat dají a možná by to bylo rychlejší, ale ještě jsem to neviděl.
Vypadalo by to asi takhle:

Kód: Vybrat vše

#include <stdio.h>

int main(int argc, const char **argv)
{
   int pole[2][3] = {{1, 2, 3}, {4, 5, 6}};
   int i;
   int j;
   int (*radka)[3];
   int *prvek;
   
   for ( i = 2, radka = pole; i--; radka++ )
   {
      for ( j = 3, prvek = *radka; j--; prvek++ )
      {
         printf("%i\n", *prvek);
      }
   }
   
   return 0;
}
Odpovědět

Zpět na „Programování PIC, ATMEL, EEPROM a dalších obvodů“