Utilizator:
Parola:
Am uitat parola... | Cont nou!


Articole Resurse Echipe Competiții Proiecte Forum DevBlogs Locuri de muncă GDROMag Issue#1 GDROCon 2007

 
Forum » Articole » Programare » Numarul de elemente dintr-un array static

Articol:
Numarul de elemente dintr-un array static
Autor: Dark, postat pe 23 Apr 2008 15:06:03
Versiune printabilã
Nota despre cod: ce misto era daca forumul nu taia codul dintre mai mic mai mare. Pina se rezolva cu paranoia am pus $ unde urmau sa vina caracterele respective.

Presupun ca mai toata lumea stie cum se determina cite elemente are un array pre-alocat:

Cod sursă:

#include $stdio.h$

struct Ceva
{
    int x;
    char y[19];
    short z;
};

Ceva g_theArray[42];

#define NUM_ELEMS(a) ((sizeof(a) / sizeof(a[0]))

int main()
{
    int numElems = NUM_ELEMS(g_theArray); // returneaza 42
    printf("%dn", numElems);
}
 


Toate bune si frumoase pe vremea lui C, dar exista o problema:

Cod sursă:

Ceva* altArray = new Ceva[42];
int numElems1 = NUM_ELEMS(altArray); // Returneaza 0.
int* arrayDeInt = new int[42];
int numElems2 = NUM_ELEMS(arrayDeInt); // In general returneaza 1 sau 2.
 


Problema aici e ca sizeof() ne da dimensiunea pointerului, ca n-are de unde sa stie dimensiunea array-ului alocat dinamic. Un pointer are 4 sau 8 bytes pe majoritatea platformelor, insa Ceva e mai mare, asa ca atunci cind le impartim ca intregi ne da 0.

Nu se poate obtine marimea unui array alocat dinamic (in C++ chior, ca daca ne apucam sa facem chestii platform-specific se poate). Se poate insa sa evitam folosirea gresita a lui NUM_ELEMS, adica sa dea eroare de compilare cind argumentul e un pointer, nu un array alocat static. Header-ul stdlib.h din CRT-ul de la VS 2005 are un macro _countof definit (aproape) in felul urmator:

Cod sursă:

#if !defined(__cplusplus)
#define _countof(_Array) (sizeof(_Array) / sizeof(_Array[0]))
#else
extern "C++"
{
template $ typename _CountofType, size_t _SizeOfArray $
char (*__countof_helper(_CountofType (&_Array)[_SizeOfArray]))[_SizeOfArray];
#define _countof(_Array) sizeof(*__countof_helper(_Array))
}
#endif
 


In C, face aia cu impartirea si gata. In C++ insa, face o smecherie cu template-uri astfel incit:

Cod sursă:

int a[19];
int b* = new b[20];
int x = _countof(a); // 20
int y = _countof(b); // compiler error
 


Codul nu foloseste nici o extensie de VC, deci merge in orice compilator intreg la cap. Evident, daca va decideti sa-l bagati in codul propriu trebuie sa redenumiti __countof_helper, _CountofType, _SizeOfArray, _Array si _countof pentru ca toti sint identificatori rezervati (incep cu underscore). Eu l-am bagat la mine in felul urmator:

Cod sursă:

template $ typename Type, size_t SizeOfArray $
char (*StaticArrayNumElemsHelper(Type (&arr)[SizeOfArray]))[SizeOfArray];
#define STATIC_ARRAY_NUM_ELEMS(x) sizeof(*StaticArrayNumElemsHelper(x))
 


Bonus points pentru cine se prinde de ce functioneaza. Hint: sintaxa pentru declarat array-uri nu-i intotdeauna ceea ce pare a fi. Wink

PS: definitia originala are si un calificator UNALIGNED pe-acolo, il gasiti si singuri cu go to definition la _countof. In VC, macro-ul UNALIGNED se expandeaza in nimic pe 32 de biti, iar pe 64 de biti se expandeaza in keyword-ul __unaligned. Nu pricep de ce e nevoie de el, am testat si merge si fara pe 64 de biti.

Comentarii pentru acest articol:



Pagina 1 din 1 [ 1 ]

Mesaj Info autor
    Postat la 23 Apr 2008 15:45:24    Subiect: < fara subiect >
Rimio info:

Rimio:

Care e metoda platform specific, ca nu gasesc pe net. Doar oameni care spun sizeof(array)/sizeof(type).

PS: Cica exista o solutie after all, documentata de un tip pe un forum, si anume:
keep accessing array elements from 0 until the program segfaults, then back up one and that's your limit.

Smile)

If at first you don't succeed, you fail.



Status:
Înregistrat pe:
24 Mar 2007 21:50:44
Vârsta: 23 ani
Mesaje: 794
Locatie: Pitesti, Arges
Programator

 
    Postat la 23 Apr 2008 16:45:49    Subiect: < fara subiect >
Dark info:

Dark:

Pina si solutia aia e gresita, pentru ca n-o sa afli dimensiunea array-ului ci dimensiunea zonei de memorie din care face parte. Cu alte cuvinte, daca aloci 2 array-uri si incerci sa afli cit de mare e primul cu metoda aia, o sa afli cit de mari sint amindoua, plus inca un numar random de bytes. Page fault iei doar cind treci de marginea unei pagini alocate, nu a unui buffer returnat de alocatorul din CRT.

In general e o prostie sa vrei sa stii cit de mare e un buffer spre care ai doar un pointer. Daca te intereseaza asta trebuie sa primesti de la caller si dimensiunea, deoarece caller-ul stie. E o chestie de design al codului. O functie care are nevoie sa stie dimensiunea unui buffer si n-o primeste e prost facuta. Exemple notorii sint strcat(), strcpy(), sprintf() si asa mai departe, care nu stiu cit de mare e bufferul destinatie si sint sursa numarul unu de gauri de securitate din diversele softuri (in special alea micromoi mai vechi, cum ar fi IIS de acum 2-3 ani). Aia care au facut functiile astea au fost niste imbecili si din pacate si-au dat seama mult prea tirziu.

Daca buffer-ul e alocat dinamic, tine minte odata cu el cit ai alocat sau foloseste un container care face asta pentru tine, cum ar fi std::vector. Daca e alocat static (pe stiva, global etc.) stii din declaratie, asa ca zi-le si functiilor pe care le apelezi.

Dupa tot disclaimer-ul asta, exista metode platform-specific de a afla cit de mare e un buffer alocat dinamic. Trebuie sa stii cum merge alocatorul de memorie, sau sa iti dea niste functii care sa-ti expuna matele lui. In CRT-ul lui VC exista _msize(), in GLIBC exista malloc_usable_size(). Mare atentie insa, deoarece de exemplu malloc_usable_size() iti returneaza dimensiunea reala a buffer-ului, care nu-i intotdeauna egala cu dimensiunea pe care ai cerut-o tu (uneori e mai mare).

Sint putine lucruri mai abominabile decit sa folosesti functiile astea. De exemplu:

Cod sursă:

void CopyString(char* dest,  const char* src)
{
    int size = _msize(dest);
    // Un programator experimentat cu multiple participari la Galaciuc ar
    // putea concluziona ca acum stie cit de mare e "dest" si copiaza doar
    // "size" bytes, deci a scapat de probleme pentru tot restul vietii.
}
 


Dupa aia vine cineva si face:

Cod sursă:

char str[12];
CopyString(str, "ana are mere si se mindreste cu ele");

struct Ceva
{
    int x;
    char str[5];
};

Ceva* a = new Ceva;
CopyString(a->str, "inca o gogorita");
 


Surpriza, "str" nu e alocat dinamic, deci _msize() nu stie nimic despre el. In al doilea exemplu, "a" este alocat dinamic, dar "str" e un membru al sau, deci din nou _msize() nu stie nimic despre el. N-ai de unde sa stii daca un pointer vine de pe stiva, de la malloc(), dintr-o structura mai mare etc. Din acest motiv _msize() si prietenii lui sint inutili. Nu-i folosi. Functia aia se poate scrie intr-un singur fel:

Cod sursă:

void CopyString(char* dest, size_t destSize, const char* src)
{
    // ...
}
 


In felul asta il obligi pe caller sa-ti spuna explicit cit de mare e buffer-ul. Daca nu stie, inseamna ca si caller-ul a fost facut prost si trebuia sa primeasca si el un size in lista de parametri, si tot asa pina la virf unde e declarata sau alocata variabila.

Deci: nu exista metode de a afla cit de mare e un buffer spre care ai doar un pointer.

Acum sa revenim, cine se prinde cum functioneaza _countof()?

"Am crezut ca esti ceva mai avansat" - Nekitu, 2008 A.D.
Autobaza


Status:
Înregistrat pe:
12 May 2007 20:12:30
Vârsta: ? ani
Mesaje: 729
Locatie:
Programator

 
    Postat la 24 Apr 2008 13:54:27    Subiect: < fara subiect >
xelanoimis info:

xelanoimis:

Parerea mea e sa tii si countul de elemente intr-un intreg, ca de-aia esti in C.
Eu n-as folosi chestii de tip countof, care incearca la runtime sa se uite prin cine stie ce locatii interne, dupa marimea zonei de memorie. Indiferent cine zice ca merg.
Iar daca vrei sa fii mai C++, fa-ti o clasa de array si tii countul ca membru acolo.


Status:
Înregistrat pe:
30 Sep 2006 02:07:32
Vârsta: ? ani
Mesaje: 52
Locatie: Bucuresti
Programator

 
    Postat la 24 Apr 2008 14:13:09    Subiect: < fara subiect >
Dark info:

Dark:

_countof nu incearca sa faca nimic la runtime si nici nu se uita prin nimic intern. Totul e compile-time si nu merge decit pe array-uri statice, in rest iti da compile error (in C++). E pentru chestii d-astea:

Cod sursă:

int params[32];

for(int i = 0; i < _countof(params); ++i)
  DoSomething(param[i]);
 


Este o idee deosebit de proasta sa scrii:

Cod sursă:

for(int i = 0; i < 32; ++i)
  DoSomething(param[i]);
 


pentru ca cineva o sa vina si o sa schimbe numarul de elemente din params si n-o sa se uite in toate for-urile din program. E de asemenea pentru d-astea:

Cod sursă:

char something[64];
snprintf(something, _countof(something), si asa mai departe);
 


La fel ca si mai sus, e o prostie sa scrii direct 64, ca o sa ai probleme cind cineva schimba dimensiunea buffer-ului. Unii folosesc sizeof() aici, dar si ala e o prostie, pentru ca intr-o zi o sa descoperi ca vrei unicode, si atunci:

Cod sursă:

wchar_t something[64];
swprintf(something, sizeof(something), si asa mai departe);
 


o sa te loveasca si n-o sa stii de unde. swprintf() doreste numarul de caractere disponibile in buffer, nu numarul de bytes. sizeof() e numarul de caractere doar cind caracterele au un byte. Daca scrii cu _countof() de la inceput scapi de dureri de cap.

Un neam si mai ascuns de probleme e in WINAPI, unde un define ascuns iti face toate string-urile unicode. Daca folosesti sizeof() acolo, cind cineva iti baga use Unicode o sa ai o groaza de cosmaruri despre buffer overflow-uri.

Concluzia: foloseste _countof(). Daca nu vrei sa te legi de CRT-ul Microsoft, copiaza definitia customizata de mai sus in PCH la tine si foloseste-o p-aia. Nu e negociabil.

Ultima editare efectuată de Dark pe 24 Apr 2008 14:15:34; 1 editări în total

"Am crezut ca esti ceva mai avansat" - Nekitu, 2008 A.D.
Autobaza


Status:
Înregistrat pe:
12 May 2007 20:12:30
Vârsta: ? ani
Mesaje: 729
Locatie:
Programator

 
    Postat la 25 Apr 2008 19:13:51    Subiect: < fara subiect >
raicuandi info:

raicuandi:

Tocmai cand imi luasem si eu cateva zile libere... Smile

Ah, iubesc sintaxa C++ Smile Geniala faza, good laugh Smile

Desigur, e doar in aparente, defapt e destul de simpla treaba:

Template-ul ul defineste o functie ce returneaza un pointer catre un array de char-uri. (De ce un pointer? Pt ca e ilegal sa returnezi un array direct!)

Template-ul se rezolva din tipul variabilei care il dai ca argument:

int a[19];

StaticArrayNumElemsHelper(a) rezolva la predeclaratia functiei:
StaticArrayNumElemsHelper(int un_array[19]);

'Type' e acolo in template pt ca nu are importanta care este tipul unui element din array, poate sa fie chiar si alt array (de ex daca 'a' ar fi fost int a[3][2], atunci _countof ar fi returnat 3, iar tipul lui a[0] este int[2]), dar trebuie sa fie acolo, pt ca variabila aia un_array trebuie sa aibe un tip, si cum nu conteaza care e, Type e in template; dar 19... aici e partea desteapta, trece in SizeOfArray, care este mai apoi folosit ca numarul de elemente din array-ul returnat de pointerul returnat de functia respectiva.

Type (&arr)[SizeOfArray] Asta e toata jmecheria. Daca argumentul, arr, nu e un array, template-ul nu se poate rezolva... pt ca arr trebuie sa fie un array de orice numar de elemente, de orice tip de element. Pt ca daca nu e, template-ul nu poate fi rezolvat. So yeah, un pic "wtf?".

/edit: un pic mai clar spus: arr este declarat ca o referinta la un array de un numar oarecare de elemente de un tip oarecare. Functia e un template care depinde de acele 2 oarecare-uri, asa ca o sa fie o functie (implementare) diferita pt orice combinatie diferita de acele oarecare-uri. Oricare ar fi acele oarecare-uri, arr ramane un array, asa ca nu poti sa ii dai argument altceva decat un array. Um, idea e ca totul e mai simplu decat pare. Pur si simplu 'este', nu-i nici-un voodoo shit la mijloc.

Apoi, partea cu sizeof din #define:
sizeof(*StaticArrayNumElemsHelper(x))

StaticArrayNumElemsHelper(x) aparent cheama functia respectiva. Defapt nu. Asta e doar sintaxa la sizeof pt a afla dimensiunea tipului returnat de o functie. (cum asta e compile time shit, se rezolva la compile time, deci no call)

Iar steluta aia * e pt a face derefencing la pointerul acela al tipului returnat, rezultand ca ramai cu ceva de genul sizeof(char[n]), care e egal cu n. E mereu char, pt ca nu conteaza ce e defapt. Ar fi putut returna Type in loc de char, dar chiar nu are nici o diferenta. Sper.

Toata treaba e facuta de compilator, static, la compile time...



Now where's my bonus points?!

Ultima editare efectuată de raicuandi pe 25 Apr 2008 19:19:21; 1 editări în total

Method 2: Move Your Mouse Pointer
If you move your mouse pointer continuously while the data is being returned to Microsoft Excel, the query may not fail. Do not stop moving the mouse until all the data has been returned to Microsoft Excel.


Status:
Înregistrat pe:
24 Mar 2007 21:02:40
Vârsta: 22 ani
Mesaje: 514
Locatie: Adelaide, Australia
Programator

 
    Postat la 25 Apr 2008 19:26:23    Subiect: < fara subiect >
Dark info:

Dark:

Ok, primesti bonus points (pe care deocamdata le poti preschimba doar in stima si mindria Partidului Smile ), cu o singura mentiune: tipul returnat trebuia sa fie array de char, nu putea fi array de T. Daca era T te intorceai de unde ai plecat. Trebuie sa fie array de char pentru ca in standard e specificat ca sizeof(char) = 1, si atunci sizeof(char[SizeOfArray]) = SizeOfArray, care e ce cauti. Daca era int[SizeOfArray] nu mai obtineai SizeOfArray.

"Am crezut ca esti ceva mai avansat" - Nekitu, 2008 A.D.
Autobaza


Status:
Înregistrat pe:
12 May 2007 20:12:30
Vârsta: ? ani
Mesaje: 729
Locatie:
Programator

 
    Postat la 25 Apr 2008 19:41:59    Subiect: Re:
raicuandi info:

raicuandi:

Dark a scris:

Ok, primesti bonus points (pe care deocamdata le poti preschimba doar in stima si mindria Partidului Smile )


Ah, in seara asta am mai aflat si de la ce vine litera aia "C" din C++... Smile (in fundal: muzica din telenovele de atunci cand aflii Adevarul Socant™)


Dark a scris:

cu o singura mentiune:

Pfft, ca de obicei, iar a intrat in efect talentul meu nemarginit de a o da in bara la cele mai marunte detalii ... Because I can!

Method 2: Move Your Mouse Pointer
If you move your mouse pointer continuously while the data is being returned to Microsoft Excel, the query may not fail. Do not stop moving the mouse until all the data has been returned to Microsoft Excel.


Status:
Înregistrat pe:
24 Mar 2007 21:02:40
Vârsta: 22 ani
Mesaje: 514
Locatie: Adelaide, Australia
Programator

 
    Postat la 05 May 2008 13:03:50    Subiect: < fara subiect >
Black_Knight info:

Black_Knight:

bun articolu chiar o sa incerc Very Happy


Status:
Înregistrat pe:
07 May 2007 19:49:43
Vârsta: 29 ani
Mesaje: 711
Locatie: Bucuresti
Programator

 

Pagina 1 din 1 [ 1 ]


Server time: 14:35:05 11.02.2012



[ Termeni si conditii | Contact | F.A.Q. | Funny Pictures ]

© 2011 Copyright 7thFACTOR Entertainment - All rights reserved