Stránka 2 z 2

Napsal: 11 úno 2013, 12:36
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]

Napsal: 11 úno 2013, 16:47
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?

Napsal: 11 úno 2013, 16:55
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.

Napsal: 11 úno 2013, 17:44
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.

Napsal: 11 úno 2013, 17:55
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;

Napsal: 11 úno 2013, 18:04
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.

Napsal: 11 úno 2013, 18:11
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.

Napsal: 11 úno 2013, 18:24
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

Napsal: 11 úno 2013, 18:42
od masterboy
Takze co z toho plyne? ;) Mam tedy pouzivat pointery na vicerozmerne pole?

PS.: Bavime se tady od C-ecku pro uP.

Napsal: 12 úno 2013, 07:33
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.

Napsal: 12 úno 2013, 09:36
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.

Napsal: 12 úno 2013, 09:39
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.

Napsal: 12 úno 2013, 09:59
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.

Napsal: 12 úno 2013, 15:00
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;
}