Tipuri de date structurale în C. Inițializarea unei structuri în C (c struct)

Ultima actualizare: 02.10.2018

Alături de clase, structurile reprezintă o altă modalitate de a crea tipuri de date proprietare în C#. Mai mult, multe tipuri primitive, de exemplu, int, double etc., sunt în esență structuri.

De exemplu, să definim o structură care reprezintă o persoană:

Struct User (nume public șir; public int vârsta; public void DisplayInfo() ( Console.WriteLine($"Nume: (nume) Vârstă: (vârstă)"); ) )

Ca și clasele, structurile pot stoca starea ca variabile și pot defini comportamentul ca metode. Deci, în acest caz, sunt definite două variabile - numele și vârsta pentru a stoca numele și, respectiv, vârsta unei persoane și metoda DisplayInfo pentru a afișa informații despre persoană.

Utilizăm această structură în program:

Utilizarea sistemului; namespace HelloApp ( struct User (nume public șir; public int vârsta; public void DisplayInfo() ( Console.WriteLine($"Nume: (nume) Vârsta: (vârstă)"); ) ) clasă Program ( static void Main(string args) ) ( Utilizator tom; tom.name = „Tom”; tom.age = 34; tom.DisplayInfo(); Console.ReadKey(); ) ) )

În acest caz, este creat un obiect tom. Setează valorile variabilelor globale și apoi afișează informații despre acestea.

Constructori de structură

La fel ca o clasă, o structură poate defini constructori. Dar, spre deosebire de o clasă, nu trebuie să apelăm un constructor pentru a crea un obiect de structură:

Utilizator tom;

Cu toate acestea, dacă creăm un obiect de structură în acest fel, atunci trebuie să inițializam toate câmpurile (variabilele globale) ale structurii înainte de a obține valorile acestora sau înainte de a apela metodele structurii. Adică, de exemplu, în următorul caz vom primi o eroare, deoarece câmpurile și metodele sunt accesate înainte de a li se atribui valori inițiale:

Utilizator tom; int x = tom.age; // Eroare tom.DisplayInfo(); // Eroare

De asemenea, putem folosi un constructor implicit pentru a crea o structură, atunci când sunt apelate, câmpurilor structurii li se va atribui o valoare implicită (de exemplu, pentru tipuri numerice acest număr este 0):

Utilizator tom = utilizator nou(); tom.DisplayInfo(); // Nume: Vârsta: 0

Ne putem defini și propriii constructori. De exemplu, să schimbăm structura utilizatorului:

Utilizarea sistemului; folosind System.Reflection; namespace HelloApp ( struct User (nume public șir; public int vârsta; public User (nume șir, int vârsta) ( this.name = nume; this.age = age; ) public void DisplayInfo() ( Console.WriteLine($"Name) : (nume) Vârstă: (vârstă)"); ) ) class Program ( static void Main(string args) ( User tom = new User ("Tom", 34); tom.DisplayInfo(); User bob = new User( ); bob.DisplayInfo(); Console.ReadKey();

Este important să luăm în considerare că, dacă definim un constructor într-o structură, atunci acesta trebuie să inițializeze toate câmpurile structurii, deoarece în acest caz sunt setate valorile pentru variabilele nume și vârstă.

La fel ca pentru o clasă, puteți folosi un inițializator pentru a crea o structură:

Persoană utilizator = utilizator nou (nume = „Sam”, vârsta = 31);

Dar, spre deosebire de o clasă, nu puteți inițializa câmpurile unei structuri direct atunci când le declarați, de exemplu, astfel:

Struct User ( nume public șir = "Sam"; // ! Eroare public int vârsta = 23; // ! Eroare public void DisplayInfo() ( Console.WriteLine($"Name: (nume) Vârsta: (vârstă)"); ) )

Înainte de a începe să studiem clasele în C++, ne vom uita la un tip de date similar cu o clasă - structs. Structurile sunt utile atunci când trebuie să combinăm mai multe variabile cu tipuri diferite sub un singur nume. Acest lucru face ca programul să fie mai compact și mai flexibil pentru a face modificări. Structurile sunt de asemenea indispensabile atunci când este necesară gruparea unor date, de exemplu, o înregistrare dintr-o bază de date sau un contact dintr-o agendă. În acest din urmă caz, structura va conține date de contact precum numele, adresa, numărul de telefon etc.

Sintaxă

În timpul scrierii unui program, poate fi necesar să grupați date diferite. De exemplu, este posibil să doriți să stocați coordonatele unor obiecte și numele acestora. Puteți face acest lucru cu:

Int x_coor; int y_coor; nume de șiruri;

Dar, deoarece fiecare element al unei matrice este conectat la altul, atunci când schimbați unul, va trebui să schimbați și restul. Și cu cât trebuie să combinați mai multe date, cu atât un astfel de program va fi mai complex. Prin urmare, pentru a combina diferite date, folosim structurilor .

Format de anunț structura arata asa:

Struct Car (int x_coor; int y_coor; nume șir; );

Prin declararea unei structuri, vă prezentăm tip propriu date pe care le putem folosi, precum și tipuri standard, adică Declarația unei variabile de tipul nostru va fi astfel:

StructName variableName;

structName este numele structurii, variableName este numele variabilei.

x_coor, y_coor și nume - câmpuri structura noastră. Când declarăm o structură, creăm un tip de date compus care poate fi folosit pentru a crea variabile care combină mai multe valori (de exemplu, coordonate și un nume). În interiorul structurii, dăm fiecărui câmp un nume, astfel încât apoi să ne putem referi la această valoare prin numele ei.

Pentru a accesa câmpurile structurii, utilizați un punct:

// declar variabila Car myCar; // și folosește-l myCar.x_coor = 40; myCar.y_coor = 40; myCar.name = "Porche";

După cum puteți vedea, puteți stoca câte câmpuri doriți în structură și pot avea diferite tipuri.

Să ne uităm la un exemplu care demonstrează combinația de matrice și structuri.

#include folosind namespace std; struct PlayerInfo (int skill_level; nume șir; ); folosind namespace std; int main() ( // ca și în cazul tipurilor obișnuite, puteți declara o matrice de structuri PlayerInfo players; for (int i = 0; i< 5; i++) { cout << "Please enter the name for player: " << i << "\n"; // сперва получим доступ к элементу массива, используя // обычный синтаксис для массивов, затем обратимся к полю структуры // с помощью точки cin >> jucători[ i ].nume; cout<< "Please enter the skill level for " << players[ i ].name << "\n"; cin >> jucători[ i ].skill_level; ) pentru (int i = 0; i< 5; ++i) { cout << players[ i ].name << " is at skill level " << players[i].skill_level << "\n"; } }

La fel ca în cazul tipurilor simple (int, de exemplu), puteți crea matrice de structuri. Și lucrați cu fiecare element al acestei matrice în același mod ca și cu o variabilă separată. Pentru a accesa câmpul de nume al primului element al unei matrice de structuri, scrieți pur și simplu:

Jucători[ 0 ].nume

Structuri și funcții

Foarte des trebuie să scrieți funcții care iau structuri ca argumente sau returnează o structură. De exemplu, dacă trebuie să scrieți un joc arcade în spațiu mic, este posibil să aveți nevoie de o funcție pentru a inițializa un nou inamic:

Struct EnemySpaceShip ( int x_coordinate; int y_coordinate; int weapon_power; ); EnemySpaceShip getNewEnemy();

Funcția getNewEnemy ar trebui să returneze o structură cu câmpuri inițializate:

EnemySpaceShip getNewEnemy() ( nava EnemySpaceShip; ship.x_coordinate = 0; ship.y_coordinate = 0; ship.weapon_power = 20; retur nava; )

Această funcție va returna de fapt o copie a navei variabilei locale create. Aceasta înseamnă că fiecare câmp al structurii va fi copiat într-o nouă variabilă. În cazul nostru, copierea unui număr mic de câmpuri nu este vizibilă, dar atunci când lucrați cu cantități mari de date, trebuie să evitați acțiunile inutile, vom vorbi mai multe despre acest lucru în articolul despre indicatori;

Astfel, pentru a obține o nouă variabilă vom folosi următorul cod:

nava EnemySpaceShip = getNewEnemy();

Acum această variabilă poate fi folosită ca structură obișnuită.

Puteți trece structuri la o funcție ca aceasta:

Upgrade EnemySpaceShipWeapons (nava EnemySpaceShip) ( ship.weapon_power += 10; return ship; )

Când trecem o structură unei funcții, aceasta este copiată, la fel ca atunci când returnăm o structură. Prin urmare, orice modificări făcute în interiorul funcției se vor pierde, așa că returnăm structura după modificare.

Folosind funcția:

Navă = upgradeWeapons(navă);

Când o funcție este apelată, variabila ship este copiată și modificată în funcție, iar când variabila este returnată, este copiată din nou și suprascrie câmpurile variabilei originale.

Și, în sfârșit, un program pentru crearea și îmbunătățirea unei nave:

Struct EnemySpaceShip ( int x_coordinate; int y_coordinate; int weapon_power; ); ENEMYSPACSESHIP GetNeWeNemy () (EnemySspaceship Ship; Ship.x_Coordinat = 0; Ship.Y_Coordinat = 0; Ship.Weapon_Power = 20; Return Ship;) EnemySspaceship UpgradeWeapons (EnemySspaceship Ship) (Ship.weapon_Power += 10; () ( EnemySpaceShip enemy = getNewEnemy(); enemy = upgradeWeapons(inamic); )

Indicatoare

Dacă lucrați cu o structură, atunci pentru a accesa variabile trebuie să utilizați operatorul „->” în loc de un punct. Toate proprietățile pointerului sunt neschimbate. Exemplu:

#include folosind namespace std; struct xampl(int x;); int main() ( structura xampl; xampl *ptr; structura.x = 12; ptr = cout<< ptr->X; cin.get(); )

Recent m-am familiarizat cu structurile C/C++ - struct. Doamne, „de ce să-i cunoști”, spui? Astfel, vei face două greșeli deodată: în primul rând, nu sunt Dumnezeu și, în al doilea rând, am crezut că și structurile sunt structuri în Africa. Dar după cum sa dovedit, nu. Vă voi spune despre câteva detalii vitale care îi vor salva pe unii dintre cititori de o depanare de o oră...

Alinierea câmpurilor în memorie

Acordați atenție structurii:

Struct Foo ( char ch; int value; );
Ei bine, în primul rând, care este dimensiunea acestei structuri în memorie? sizeof(Foo) ?
Mărimea acestei structuri în memorie depinde de setările compilatorului și de directivele din codul dvs....

În general, câmpurile sunt aliniate în memorie de-a lungul unei granițe care este un multiplu al propriei dimensiuni. Adică, câmpurile de 1 octet nu sunt aliniate, câmpurile de 2 octeți sunt aliniate la poziții pare, câmpurile de 4 octeți sunt aliniate la poziții care sunt multipli de patru etc. În majoritatea cazurilor (sau să presupunem că acesta este cazul astăzi) dimensiunea de aliniere a structurii în memorie este de 4 octeți. Deci sizeof(Foo) == 8 . Unde și cum vor fi adăugați cei 3 octeți suplimentari? Dacă nu știi, nu vei ghici niciodată...

  • 1 octet: ch
  • 2 octeți: gol
  • 3 octeți: gol
  • 4 octeți: gol
  • 5 octeți: valoare
  • 6 octeți: valoare
  • 7 octeți: valoare
  • 8 octeți: valoare
Să ne uităm acum la plasarea în memorie a următoarei structuri:

Struct Foo ( char ch; short id; int value; );
Arata cam asa:

  • 1 octet: ch
  • 2 octeți: gol
  • 3 octeți: id
  • 4 octeți: id
  • 5 octeți: valoare
  • 6 octeți: valoare
  • 7 octeți: valoare
  • 8 octeți: valoare
Adică, ceea ce poate fi înghesuit într-o aliniere de 4 octeți este înghesuit cu un bang (fără a crește dimensiunea structurii din memorie), să mai adăugăm un câmp:

Struct Foo ( char ch; short id; short opt; int value; );
Să ne uităm la plasarea câmpurilor în memorie:

  • 1 octet: ch
  • 2 octeți: gol
  • 3 octeți: id
  • 4 octeți: id
  • 5 octeți: opt
  • 6 octeți: opt
  • 7 octeți: gol
  • 8 octeți: gol
  • 9 octeți: valoare
  • 10 octeți: valoare
  • 11 octeți: valoare
  • 12 octeți: valoare
Toate acestea sunt atât de trist, dar există o modalitate de a rezolva asta direct din cod:

#pragma pack(push, 1) struct Foo ( // ... ); #pragma pack(pop)
Am setat dimensiunea de aliniere la 1 octet, am descris structura și am returnat setarea anterioară. Recomand cu caldura revenirea setarii anterioare. Altfel, totul s-ar putea termina foarte prost. Mi s-a întâmplat asta odată - Qt s-a prăbușit. Undeva am inclus porecla lor.h-sub numele meu.h-porecla...

Câmpuri de biți

În comentarii mi s-a subliniat că câmpurile de biți din structurile conform standardului sunt „implementare definită”- prin urmare este mai bine să evit să le folosesc, dar pentru mine tentația este prea mare...

Nu numai că mă simt neliniștit în suflet, dar în general mă simt rău când văd în cod umplerea câmpurilor de biți folosind măști și schimburi, de exemplu, astfel:

Câmp nesemnat = 0x00530000; // ... câmp &= 0xFFFF00FF; câmp |= (id)<< 8; // ... field &= 0xFFFFFF83; field |= (proto) << 2;
Toate astea miroase a atâta tristețe și așa erori și a depanării lor încât îmi iese imediat o migrenă! Și apoi ies din culise - Câmpurile Bit. Cel mai surprinzător este că erau încă în limbajul C, dar nu întreb pe nimeni - toată lumea aude despre ele pentru prima dată. Această mizerie trebuie corectată. Acum le voi da tuturor un link, sau cel puțin un link către acest articol.

Cum vă place această bucată de cod:

#pragma pack(push,1) struct IpHeader ( uint8_t header_length:4; uint8_t version:4; uint8_t type_of_service; uint16_t total_length; uint16_t identifier; // Semnalează uint8_t _reserved:1; uintg8_t_t;_tframent8_1;_t fragment_off set_part1 :5 uint8_t fragment_offset_part2 uint8_t time_to_live; #pragma pack(pop)
Și apoi, în cod, putem lucra cu câmpuri, așa cum lucrăm întotdeauna cu câmpuri în C/C++. Toate lucrează în ture etc. Compilatorul preia controlul. Desigur, există unele restricții... Când enumerați mai multe câmpuri de biți la rând legate de același câmp fizic (mă refer la tipul care apare în stânga numelui câmpului de biți) - specificați nume pentru toți biții până la sfârșitul câmpului, altfel nu veți avea acces la acești biți va fi, cu alte cuvinte, codul:

#pragma pack(push,1) stuct MyBitStruct ( uint16_t a:4; uint16_t b:4; uint16_t c; ); #pragma pack(pop)
Rezultatul este o structură de 4 octeți! Cele două jumătăți ale primului octet sunt câmpurile a și b. Al doilea octet nu este accesibil prin nume, iar ultimii 2 octeți sunt accesibili după numele c. Acesta este un moment foarte periculos. După ce ați descris structura cu câmpuri de biți, asigurați-vă că îi verificați dimensiunea !

De asemenea, ordinea în care biții sunt plasați într-un octet depinde de ordinea octetilor. Cu ordinea LITTLE_ENDIAN, câmpurile de biți sunt distribuite începând de la primii octeți, cu BIG_ENDIAN - invers...

Ordinea octetilor

De asemenea, sunt întristat de apelurile la funcțiile htons() , ntohs() , htonl() , nthol() în codul C++. În C acest lucru este încă posibil, dar nu în C++. Nu mă voi împăca niciodată cu asta! Vă rugăm să rețineți că toate următoarele se aplică pentru C++!

Ei bine, voi fi scurt aici. Într-unul dintre articolele mele anterioare am scris deja ce trebuie făcut cu ordinele de octeți. Este posibil să descriem structuri care funcționează extern ca numere, dar în interior ele însele determină ordinea de stocare în octeți. Deci, structura antetului nostru IP va arăta astfel:

#PRAGMA PACK (Push, 1) Struct IPHEADER (Uint8_t Header_LENGTH: 4; UINT8_T VERSION: 4; UINT8_T TYPE_OF_SERVICE; U16BE TOTAL_LENGTH; U16BE IDENTIC T _Reserved: 1; :5; uint6_t ; #pragma pack(pop)
Acordați atenție tipurilor de câmpuri de 2 octeți - u16be. Acum, câmpurile de structură nu au nevoie de conversii în ordinea octeților. Există încă probleme cu fragment_offset , dar cei care nu le au au probleme. Cu toate acestea, puteți, de asemenea, să veniți cu un șablon care ascunde această rușine, să-l testați o dată și să vă simțiți liber să îl utilizați pe tot parcursul codului.

„Limbajul C++ este suficient de complex pentru a ne permite să scriem în el simplu.” Destul de ciudat - eu

ZYÎntr-unul dintre următoarele articole plănuiesc să prezint structurile ideale, din punctul meu de vedere, pentru lucrul cu anteturile de protocol ale stivei TCP/IP. Vorbește înapoi - înainte să fie prea târziu!

Structuri în C (c)- Acestea sunt date combinate care au o relație logică. Spre deosebire de matrice, structurile pot conține date de diferite tipuri. Iată câteva exemple de structuri în C (c): structura clasei (numele elevului, litera clasei, scorul mediu); structura unei echipe de fotbal (antrenor, numele echipei, locul în clasament). Acestea. structura pe care o vei folosi destul de des. Acum să vedem cum sunt descrise structurile în C:

clasa struct (
nume char;
char class_name;
float bal;
};

struct

Orice structură în limbajul C (c) trebuie să înceapă cu cuvântul cheie - struct, care îi spune compilatorului că vom avea o structură aici. Toate datele din structură (struct) sunt scrise între acolade, iar la sfârșit există o virgulă cu punct (;). Vă sfătuiesc să puneți imediat o virgulă cu punct ca să nu existe greșeli.

După cum puteți vedea, în structura (struct) Avem date de diferite tipuri, dar sunt combinate într-o conexiune logică, deoarece în exemplul meu sunt o anumită clasă de școală. Datele dintr-o structură trebuie să aibă nume unice, dar aceleași nume pot fi folosite în structuri diferite.

Structura care a fost creată mai sus nu ocupă spațiu în memoria computerului, deoarece noi, de fapt, ne-am creat pur și simplu propriul tip de date. Anunţ structurilor nu diferă de declararea oricărui tip de date în limbajul C (C). Iată un exemplu:

struct clasa a, b, *c;

Am declarat o variabilă a de tip struct klass, o matrice b formată din 5 elemente de tip struct klass și un pointer către variabila struct klass.

De asemenea, puteți declara variabile imediat după declararea structurii:

clasa struct (
nume char;
char class_name;
float bal;
) a, b, *c;

Ce operațiuni se pot face cu structurile? Este mai bine să enumerați răspunsul la această întrebare punct cu punct:

  1. atribuind unui câmp de structură o valoare de același tip
  2. puteți obține adresa structurii. Nu uitați de operațiunea de preluare a adresei (&)
  3. puteți accesa orice câmp al structurii
  4. pentru a determina dimensiunea unei structuri, puteți utiliza operația sizeof().

Inițializarea structurii

Inițializarea unei structuri în limbaj C (c) apare în același mod ca la inițializarea unui tablou. Iată un exemplu de inițializare a unei structuri:

struct class a = ("Sergey", "B", 4,5);

Acestea. Creăm o variabilă de tip struct class și atribuim valori tuturor celor trei câmpuri pe care le-am definit în structură. Ordinea este foarte importantă când inițializarea structurii, deoarece computerul în sine nu poate sorta datele. Dacă orice câmp este lăsat necompletat, acesta va fi automat completat cu 0 - pentru tipurile întregi; NULL - pentru pointeri; \0 (terminator nul) - pentru tipuri de șir.

O structură este un tip de date agregate, deoarece poate conține elemente de diferite tipuri. Sintaxa pentru declararea unei structuri în C++ este diferită de C. Deși versiunea C rămâne corectă pentru C++. Se pare că în C++ poți folosi două stiluri de declarare a structurilor, dar în C poți folosi doar unul. Să ne uităm la sintaxa pentru declararea unei structuri în C++:

Nume Struct (tip atrib; // alte elemente ale structurii) structVar1, structVar2, ...;

  • struct este cuvântul cheie care începe definirea structurii
  • Nume — numele structurii
  • tip — tipul de date al elementului de structură
  • atrib - element de structură
  • structVar1-2 - variabile structurale

O declarație de structură trebuie să înceapă întotdeauna cu cuvântul cheie struct. Nu este necesar ca structura să aibă un nume, dar atunci o astfel de structură trebuie să aibă variabile de structură declarate între acolada de închidere și punct și virgulă, rândul 5. Declarația de structură trebuie să conțină acolade încadrează corpul structurii; atributele sale sunt declarate ( elemente), linia 3. Variabilele structurale, la declararea unei structuri, sunt optionale, linia 5.

Deoarece o structură este un tip de date, pentru a utiliza acest tip de date, trebuie să declarați o variabilă de structură și, în loc de tipul de date, să specificați numele structurii.

struct_name structVariable;

Sintaxă pentru declararea unei structuri în C:

Typedef struct name ( tip atrib1; tip atrib2; // alte elemente ale structurii... ) newStructName structVar;

Sintaxa pentru declararea unei structuri în limbajul C are două opțiuni. P În primul rând, omiteți cuvântul cheie typedef , cu numele newStructName de asemenea, nu este folosit și numele structurii, atunci este necesar să folosiți variabilele de structură atunci când declarați structura - structVar , rândul 6. Priviți exemplul:

Numele structurii structVar;

Sau puteți folosi un typedef pentru a declara un alias pentru structura newStructName, un alias:

NewStructName structVar;

În orice caz, dacă doriți să declarați un pointer către o structură din interiorul unei structuri, trebuie să utilizați prima sintaxă:

Numele structurii *struct_instance; // pointer către structură

Declararea unui pointer către o structură

Sintaxa pentru declararea unui pointer către o structură în C este ambiguă. În C, dacă nu utilizați un typedef atunci când definiți o structură, atunci este obligatoriu să utilizați variabile de structură între acolada de închidere și punct și virgulă.
În C++, acest lucru nu este necesar. Pentru a declara un pointer către o structură, în C++ pur și simplu prefixați numele variabilei structurii cu un caracter pointer - * .

StructName *structVar; // pointer către structura structName

NewStructName *structVar; // newStructName trebuie declarat cu typedef

sau cam asa ceva, si pentru SI:

Nume structura *structVar;

Accesarea elementelor de structură

Accesarea elementelor de structură este la fel de simplă ca și utilizarea simbolului punct. Sa presupunem. că avem o variabilă de structură numită car și are un element numit viteză, pe care acum îl vom accesa:

Auto.viteza;

Notă: Această metodă de accesare a elementelor unei structuri funcționează numai dacă structura nu este un pointer către o structură.

Accesarea elementelor indicatorului de structură

Pentru a accesa elementele unei structuri printr-un pointer către structură, în loc de operatorul punct, utilizați operatorul săgeată ->:

CarPtr->viteza;

P.S.: Tuturor posesorilor de smartphone-uri Android le prezint o selecție bună de programe de navigare GPS pentru Android. Lista conține aproximativ 20 de produse software, puteți descărca și instala oricare dintre ele pe dispozitiv. Toate programele sunt absolut gratuite.