Mutatók C-ben: Mi a mutató a C programozásban? Típusok

⚡ Okos összefoglaló

A C nyelvben a pointerek olyan változók, amelyek egy másik változó memóriacímét tárolják. Lehetővé teszik a dinamikus memóriafoglalást, a hatékony tömb- és karakterlánc-kezelést, valamint az összetett adatszerkezeteket, az address-of és dereference operátorokat használva az értékek olvasására vagy módosítására.

  • 📌 Meghatározás: Egy mutató egy másik változó memóriacímét tárolja.
  • 🔧 Operators: Az & operátor egy címet ad vissza; a * operátor dereferenciálja azt.
  • 🧷 Nyilatkozat: Írd ki a data_type *name értéket, majd inicializáld egy változó címével.
  • 🏷️ Típusok: A null, void és wild mutatók eltérően viselkednek, és figyelmet igényelnek.
  • Számtan: Egy mutató növelése az alaptípus méretével mozgatja azt.
  • 🤖 AI segítség: A mesterséges intelligencia eszközei hibakeresést végeznek a szegmentálási hibákon és jelzik a lelógó mutatókat az ellenőrzés során.

Mutatók C-ben

Mi a mutató C-ben?

A mutató A C nyelvben a egy változó, amely egy másik változó címét tárolja. Egy mutató hivatkozhat egy másik mutatóra vagy egy függvényre is. Egy mutató növelhető vagy csökkenthető, hogy a következő vagy előző memóriahelyre mutasson. A mutató célja a memóriaterület megtakarítása és a gyorsabb végrehajtási idő elérése.

A mutatók használata C-ben

Ha egy int típusú v változót deklarálunk, a v egy értéket tárol. Minden változónak, az értékén kívül, van egy címe is, vagyis az a hely, ahol a memóriában található. A címet úgy kérhetjük le, hogy a változó neve elé egy és-jelet (&) teszünk. Ha egy változó címét kiíratjuk, az egy véletlenszámnak tűnik, amely futtatásról futtatásra változhat.

Érték tárolása helyett a mutató egy változó címét tárolja. Például:

int *y = &v;
VÁLTOZÓ MUTATÓ
A érték tárolt a nevezett tárolási/memóriacím. A változó hogy mutat valahová a tárhely/memória címe másik változót.

Mutató deklarálása

Mint a változók, a mutatók C programozás használat előtt deklarálni kell őket. A pointer deklarációjának formája a következő:

data_type *pointer_variable_name;

Itt, adattípus a mutató alaptípusa, és a mutató által jelzett változó típusát jelzi. A csillag (*), ugyanaz a szimbólum, amelyet a szorzáshoz is használnak, az indirekt operátor, amely egy mutatót deklarál. Néhány érvényes mutatódeklaráció:

int    *ptr_thing;       /* pointer to an integer */
int    *ptr1, thing;     /* ptr1 points to an int; thing is an int */
double *ptr2;           /* pointer to a double */
float  *ptr3;           /* pointer to a float */
char   *ch1;            /* pointer to a character */

Mutató inicializálása

Egy mutató deklarálása után inicializáljuk azt egy változó címével. Ha egy mutatót nem inicializálunk, és mégis használunk, az eredmények kiszámíthatatlanok és akár katasztrofálisak is lehetnek. Egy változó címének lekéréséhez használjuk az és-jel (&) operátort a változó neve előtt:

pointer = &variable;

Egy egyszerű program, amely egy mutatót illusztrál, az alábbiakban látható:

#include <stdio.h>
int main()
{
   int a = 10;     // variable declaration
   int *p;         // pointer variable declaration
   p = &a;        // store address of a in pointer p
   printf("Address stored in p is: %x\n", p);  // access the address
   printf("Value stored in p is: %d\n", *p);    // access the value
   return 0;
}

output:

Address stored in p is: 60ff08
Value stored in p is: 10
Operator Jelentés
* Két célt szolgál: deklarál egy mutatót, és visszaadja a hivatkozott változó értékét (dereferencia).
& Egy célt szolgál: visszaadja egy változó címét.

A mutatók típusai C-ben

Null mutató

Egy null pointert úgy hozunk létre, hogy a deklaráció során NULL értéket rendelünk hozzá. Ez akkor hasznos, ha a pointerhez még nincs cím rendelve. Egy null pointer mindig 0 értéket tartalmaz:

#include <stdio.h>
int main()
{
   int *p = NULL;     // null pointer
   printf("The value inside variable p is: %x", p);
   return 0;
}

Üres mutató

A void pointer, más néven generikus pointer, nem rendelkezik szabványos adattípussal. A void kulcsszóval hozható létre, és bármely változó címét tárolhatja:

#include <stdio.h>
int main()
{
   void *p = NULL;    // void pointer
   printf("The size of pointer is: %d\n", sizeof(p));
   return 0;
}

Vad mutató

Egy mutatót vad mutatónak nevezünk, ha nincs semmire inicializálva. Ezek a mutatók nem hatékonyak, mert ismeretlen memóriahelyre mutathatnak, ami problémákat okozhat és a program összeomlását okozhatja. A vad mutatókkal mindig legyünk óvatosak:

#include <stdio.h>
int main()
{
   int *p;          // wild pointer
   printf("%d", *p);
   return 0;
}

A C nyelvben található egyéb mutatótípusok közé tartozik a függő mutató, az összetett mutató, a közeli mutató, a távoli mutató és a hatalmas mutató.

Közvetlen és közvetett hozzáférési mutatók

C nyelven két egyenértékű módja van egy változó tartalmának elérésére és manipulálására. A közvetlen hozzáférés közvetlenül a változó nevét használja, míg a közvetett hozzáférés egy mutatót a változóra:

#include <stdio.h>
int var = 1;        /* declare and initialize an int variable */
int *ptr;           /* declare a pointer to int */
int main(void)
{
   ptr = &var;       /* point ptr to var */
   printf("\nDirect access, var = %d", var);
   printf("\nIndirect access, var = %d", *ptr);
   *ptr = 48;        /* change var through the pointer */
   printf("\nIndirect access, var = %d", *ptr);
   return 0;
}

Mutatós aritmetika C-ben

Amikor mutatókkal dolgozunk, tartsuk be a következő prioritási szabályokat. A * és & operátorok prioritása megegyezik az unáris operátorok prioritásával (tagadás !, növelés ++, csökkentés –). Ugyanebben a kifejezésben az unáris operátorok *, &, !, ++ és — jobbról balra értékelődnek ki. Ha egy P mutató egy X változóra mutat, akkor a *P bárhol használható, ahol X írható. A következő kifejezések ekvivalensek:

Kifejezés Egyenértékű kifejezés
Y = *P + 1 Y = X + 1
*P = *P + 10 X = X + 10
*P + = 2 X + = 2
++*P ++X
(*P)++ X++

Az utolsó esetben zárójelek szükségesek: mivel a * és ++ egyváltozós operátorok jobbról balra értékelődnek ki, zárójelek nélkül a P mutató növekedne, nem pedig az objektum, amelyre mutat. A mutatókon elérhető aritmetikai műveletek az értékadás, a növelés és a csökkentés, valamint az összeadás vagy a sub.tracegy egészértékű eltolást ad meg, amely ennyi elemmel mozgatja el a mutatót (minden lépés egyenlő az alaptípus méretével bájtokban).

C mutatók és tömbök példákkal

Hagyományosan index segítségével érjük el a tömb elemeit, de a mutatók ezt is megkönnyítik. A tömb nevére beállított mutató az első elemre mutat:

#include <stdio.h>
int main()
{
   int a[5] = {1, 2, 3, 4, 5};   // array initialization
   int *p;                       // pointer declaration
   p = a;                       // p points to the first element; same as &a[0]
   for (int i = 0; i < 5; i++)
   {
      printf("%d\n", *p);            // print element
      p++;                      // move to the next element
   }
   return 0;
}

Egy szám hozzáadása egy mutatóhoz annyi elemet mozdít előre, amennyit csak lehetséges. Ha p a 0. memóriahelyre mutat, akkor p + 1 hatására a mutató az 1. memóriahelyre mutat.

C Mutatók és karakterláncok példákkal

A karakterlánc (string) karakter objektumok tömbje, melyek null karakterrel ('\0') végződnek. A karakterláncokat mutatók segítségével manipulálhatjuk:

#include <stdio.h>
#include <string.h>
int main()
{
   char str[] = "Hello Guru99!";
   char *p;
   p = str;
   printf("First character is: %c\n", *p);
   p = p + 1;
   printf("Next character is: %c\n", *p);
   p = str;                     // reset the pointer
   for (int i = 0; i < strlen(str); i++)
   {
      printf("%c\n", *p);
      p++;
   }
   return 0;
}

A karakterláncok kezelésének egy másik módja a mutatók tömbje:

#include <stdio.h>
int main()
{
   char *materials[] = {"iron", "copper", "gold"};
   for (int i = 0; i < 3; i++)
      printf("%s\n", materials[i]);
   return 0;
}

A mutatók előnyei C-ben

  • A mutatók hasznosak a memóriahelyek közvetlen eléréséhez.
  • Hatékony módot biztosítanak egy tömb elemeinek elérésére.
  • Dinamikus memóriaelosztásra és -felszabadításra használják őket.
  • Komplex adatszerkezetek, például láncolt listák, gráfok és fák létrehozására használják őket.

A mutatók hátrányai C-ben

  • A mutatók megértése kissé bonyolult.
  • Olyan hibákat okozhatnak, mint a szegmentálási hibák vagy a nem kívánt memóriahelyhez való hozzáférés.
  • A mutatónak adott helytelen érték memória-sérülést okozhat.
  • A mutatók a memóriaszivárgásért is felelősek, ha a memória nincs felszabadítva.
  • A programozó felelőssége, hogy gondosan kezelje a mutatókat.

GYIK

A lelógó mutató felszabadult vagy hatókörön kívül eső memóriára mutat. Ezt úgy kerülheted el, hogy a free függvény meghívása után azonnal NULL értékre állítod a mutatót, és használat előtt ellenőrized, hogy az NULL-e.

Egy NULL mutató dereferenciálása szegmentációs hibát okoz, és futásidőben összeomlik a program. Mielőtt a * operátort használná olvasásra vagy írásra, mindig ellenőrizze, hogy a mutató nem NULL-e.

Egy int **pp formátumban deklarált dupla mutató egy másik mutató címét tárolja. Dinamikus 2D tömbökhöz és egy függvényen belüli mutatóargumentum módosítására használják.

Egy mutató egy memóriacímet tárol, és a címek szélessége egy adott platformon azonos, jellemzően 4 bájt 32 biten és 8 bájt 64 biten, függetlenül attól, hogy milyen típusra mutat.

A függvénymutató egy függvény címét tárolja, így az dinamikusan meghívható. Széles körben használják visszahívásokhoz és futásidőben kiválasztott függvényekből álló táblázatok létrehozásához.

Az olyan függvények, mint a malloc és a calloc, egy mutatót adnak vissza a futásidőben a heap-en lefoglalt memóriára. Ezt a memóriát a mutatón keresztül érheted el, és a free paranccsal felszabadíthatod a szivárgások elkerülése érdekében.

Igen. A mesterséges intelligencia asszisztensek beolvassák a szegmentációs hibát és a környező kódot, majd elmagyarázzák a valószínűsíthető okot, például egy inicializálatlan vagy felszabadult mutatót, és javaslatot tesznek egy javításra, amelyet áttekinthet.

A mesterséges intelligenciával működő analizátorok, olyan eszközökkel együtt, mint a Valgrind, jelzik a soha fel nem szabadított memóriát és a felszabadítás után használt mutatókat. Automatizálják az ellenőrzést és kiemelik a kockázatos mintákat, mielőtt a kód kiküldésre kerülne.

Foglald össze ezt a bejegyzést a következőképpen: