Wstęp

Wstęp

Doszliśmy do kluczowego momentu w naszej serii artykułów dotyczących rozwoju C. To także, nie przypadkowo, ta część C daje wiele bólu głowy dla początkujących. W tym miejscu wchodzimy, a celem tego artykułu (jednym z nich) jest obalenie mitów o wskazówkach i C jako języka trudnego/niemożliwego do nauczenia się i czytania. Niemniej jednak zalecamy zwiększoną uwagę i odrobinę cierpliwości, a zobaczysz, że wskazówki nie są tak zadziwiające, jak mówią legendy.

Definicje i ostrzeżenia

Wydaje się naturalne i zdrowy rozsądek, że powinniśmy zacząć od ostrzeżeń, i serdecznie polecamy je zapamiętać: podczas gdy wskaźniki ułatwiają swoje życie jako programista, one również one również one Móc Wprowadź trudne do znalezienia błędy i niezrozumiały kod. Zobaczysz, jeśli będziesz czytać, o czym mówimy i powaga wspomnianych błędów, ale dolna linia jest, jak powiedziano wcześniej, bądź bardzo ostrożny.

Prosta definicja wskaźnika byłaby „zmienną, której wartością jest adres innej zmiennej”. Prawdopodobnie wiesz, że systemy operacyjne zajmują się adresami przy przechowywaniu wartości, tak jak oznaczasz rzeczy w magazynie, dzięki czemu możesz je znaleźć w razie potrzeby. Z drugiej strony tablicę można zdefiniować jako zbiór elementów zidentyfikowanych przez indeksy. Zobaczysz później, dlaczego wskaźniki i tablice są zwykle prezentowane razem, i jak stać się wydajnym w ich użyciu. Jeśli masz tło w innych językach wyższego poziomu, znasz String Ratatype. W C tablice są odpowiednikiem zmiennych typu string i argumentuje się, że takie podejście jest bardziej wydajne.



Wskaźniki

Widziałeś definicję wskaźnika, teraz zacznijmy od szczegółowych wyjaśnień i, oczywiście, przykłady. Pierwsze pytanie, które możesz sobie zadać, brzmi: „Dlaczego miałbym używać wskazówek?". Chociaż mogę płakać nad tym porównanie, ryzykuję: czy używasz symlinów w swoim systemie Linux? Nawet jeśli sam nie stworzyłeś, twój system się udaje i sprawia, że ​​praca jest bardziej wydajna. Słyszałem kilka horrorów o starszych programistach C, które przysięgają, że nigdy nie używali wskazówek, ponieważ są „trudne”, ale to oznacza, że ​​programista jest niekompetentny, nic więcej. Ponadto są sytuacje, w których będziesz musiał użyć wskazówek, więc nie należy ich traktować jako opcjonalne, ponieważ nie są one. Tak jak poprzednio, wierzę w naukę według przykładu, więc oto idzie:

int x, y, z; x = 1; y = 2; int *ptoi; /* PTOI jest i oznacza wskaźnik do liczby całkowitej*/ ptoi = & x; / * PTOI wskazuje na x */ Z = *Ptoi; / * z jest teraz 1, wartość x, według której wskazuje PTOI */ ptoi = & y; / *PTOI wskazuje teraz na y */

Jeśli porysujesz głową w zamieszaniu, nie uciekaj: to tylko boli za pierwszym razem, wiesz. Chodźmy linii i zobaczmy, co tu zrobiliśmy. Najpierw ogłosiliśmy trzy liczby całkowite, czyli X, Y i Z, i daliśmy odpowiednio wartości x i y 1 i 2. To jest prosta część. Nowy element ma wraz z deklaracją zmiennej PTOI, czyli wskaźnik do liczby całkowitej, więc to zwrotnica w kierunku liczby całkowitej. Odbywa się to za pomocą gwiazdki przed nazwą zmiennej i mówi się, że jest operatorem przekierowania. Linia „ptoi = & x;” oznacza „PTOI wskazuje teraz na X, który musi być liczbą całkowitą, zgodnie z powyższą deklaracją PTOI”. Możesz teraz pracować z PTOI, tak jak w przypadku X (cóż, prawie). Wiedząc o tym, następny wiersz jest odpowiednikiem „z = x;”. Następnie my Dereference PTOI, co oznacza, że ​​mówimy „przestań wskazywać na X i zaczynamy wskazywać na Y”. Konieczna jest tutaj jedna ważna obserwacja: i operator może być używany tylko w obiektach rezydenta pamięci, które są zmiennymi (z wyjątkiem rejestru [1]) i elementów tablicy.

[1] Zmienne typu rejestru są jednym z elementów C, które istnieją, ale większość programistów je unika. Zmienna z tym załączonym słowem kluczowym sugeruje kompilatorowi, że będzie on często używany i powinna być przechowywana w rejestrze procesora w celu szybszego dostępu. Większość współczesnych kompilatorów ignoruje tę wskazówkę i i tak decyduje się, więc jeśli nie jesteś pewien, czy potrzebujesz rejestracji, nie masz.

Powiedzieliśmy, że PTOI musi wskazywać na liczbę całkowitą. Jak powinniśmy kontynuować, jeśli chcieliśmy ogólnego wskaźnika, więc nie będziemy musieli martwić się typami danych? Wprowadź wskaźnik do nieważności. To wszystko, co ci powiemy, a pierwszym zadaniem jest dowiedzieć się, jakie zastosowania może mieć wskaźnik do nieważności i jakie są „ograniczenia.



Tablice

W tym pod komapie zobaczysz, dlaczego nalegaliśmy na przedstawienie wskazówek i tablic w jednym artykule, pomimo ryzyka przeciążenia mózgu czytelnika. Dobrze jest wiedzieć, że pracując z tablicami, nie musisz używać wskazówek, ale miło jest to zrobić, ponieważ operacje będą szybsze, z wadą mniej zrozumiałego kodu. Deklaracja tablicy ma wynik ogłoszenia szeregu kolejnych elementów dostępnych za pośrednictwem indeksów, na przykład:

int a [5]; int x; a [2] = 2; x = a [2];

A jest tablicą 5-elementową, przy czym trzeci element to 2 (numerowanie indeksu zaczyna się od zera!), a x jest zdefiniowane jako 2 to 2. Wiele błędów i błędów podczas pierwszego radzenia sobie z tablicami jest to, że zapomina o problemie 0. Kiedy powiedzieliśmy „kolejne elementy”, mieliśmy na myśli, że gwarantowano, że elementy tablicy mają kolejne lokalizacje w pamięci, a nie, jeśli A [2] to 2, to [3] ma 3. Istnieje struktura danych w C o nazwie ENUM, która to robi, ale jeszcze się z tym nie poradzimy. Znalazłem stary program, który napisałem podczas nauki C, z pomocą mojego przyjaciela Google, który odwraca postacie w ciągu. Oto jest:

#Include #include int main () char Stringy [30]; int i; Char C; printf („Wpisz ciąg .\ n "); warta (String, 30, stdin); printf (" \ n "); Do(i = 0; i < strlen(stringy); i++) printf("%c", stringy[i]); printf("\n"); Do(i = strlen (String); i> = 0; i--) printf („%c”, String [i]); printf („\ n”); powrót 0;  

To jeden ze sposobów na zrobienie tego bez użycia wskaźników. Ma pod wieloma względami wady, ale ilustruje związek między strunami i tablicami. String to 30-znakowa tablica, która zostanie użyta do przechowywania danych wejściowych, będę indeksem tablicy, a C będzie indywidualną postacią, nad którą można je opracować. Prosimy więc o ciąg, zapisujemy go w tablicy za pomocą spłok, drukuje oryginalny ciąg, zaczynając od String [0] i kontynuując, przy użyciu pętli, aż ciąg się nie zakończy. Operacja odwrotna daje pożądany wynik: Ponownie otrzymujemy długość sznurka za pomocą strlen () i rozpoczynamy odliczanie zero, a następnie wydrukuj znak strunowy według znaku. Kolejnym ważnym aspektem jest to, że każda tablica znaków w C kończy się znakiem zerowym, reprezentowana graficznie przez „\ 0”.

Jak byśmy to zrobili za pomocą wskazówek? Nie kusi się, aby zastąpić tablicę na wskaźnik na char, który nie zadziała. Zamiast tego użyj odpowiedniego narzędzia do pracy. Do interaktywnych programów, takich jak powyższe, użyj tablicy znaków o stałej długości, w połączeniu z bezpiecznymi funkcjami, takimi jak fgets (), abyś nie został ugryziony przez przepełny bufora. Jednak w przypadku stałych sznurków możesz użyć

char * myName = "David";

a następnie, korzystając z funkcji dostarczonych w ciągu.h, manipuluj danymi, jak uważasz za stosowne. Mówiąc o tym, jaką funkcję zdecydowałbyś dodać MyName do stringów, które dotyczą użytkownika? Na przykład zamiast „Wprowadź numer”, powinieneś mieć „David, wprowadź numer”.



Wskaźniki i tablice

Możesz i zachęcamy do używania tablic w połączeniu z wskaźnikami, chociaż na początku możesz być zaskoczony z powodu składni. Ogólnie rzecz biorąc, możesz zrobić wszystko związane z tablicą z wskaźnikami, z zaletą prędkości u boku. Możesz pomyśleć, że dzięki dzisiejszemu sprzętowi użycie wskazówek z tablicami tylko po to, aby uzyskać trochę prędkości, nie jest tego warte. Jednak w miarę wzrostu programów i złożoności, wspomniana różnica zacznie być bardziej oczywista, a jeśli kiedykolwiek pomyślisz o przeniesieniu aplikacji na jakąś wbudowaną platformę, pogratulujesz sobie. W rzeczywistości, jeśli zrozumiałeś, co zostało powiedziane do tego momentu, nie będziesz mieć powodów, aby się zaskoczyć. Powiedzmy, że mamy szereg liczb całkowitych i chcemy ogłosić wskaźnik do jednego z elementów tablicy. Kod wyglądałby tak:

int myarray [10]; int *myptr; int x; myPtr = & myarray [0]; x = *myptr;

Mamy więc tablicę o imieniu MyArray, składającą się z dziesięciu liczb całkowitych, wskaźnika do liczby całkowity przez wskaźnik. Teraz możesz zrobić wszelkiego rodzaju sprytne sztuczki, aby poruszać się po tablicy, jak

*(MyPtr + 1);

który wskazuje na następny element MyArray, a mianowicie Myarray [1].

Jedną ważną rzeczą, którą należy wiedzieć, a jednocześnie, która doskonale ilustruje związek między wskaźnikami i tablicami, jest to, że wartością obiektu typu tablicy jest adres jego elementu „Zero), więc jeśli myPtr = i myarray [[ 0], potem myptr = myarray. Jako ćwiczenie, zapraszamy do nieco przestudiowania tego związku i zakodowania niektórych sytuacji, w których uważasz, że/może być przydatne. To właśnie napotkasz jako arytmetyka wskaźnika.

Rozważania dotyczące strun w C i połączeniach

Zanim zobaczyliśmy, że możesz to zrobić

Char *MyString; MyString = "To jest ciąg."

lub możesz zrobić to samo, używając

Char MyString [] = "To jest ciąg.";

W drugim przypadku, jak mogłeś wywnioskować, MyString jest wystarczająco dużą tablicą, aby zachować przypisane mu dane. Różnica polega na tym, że używając tablic, możesz działać na poszczególnych znakach wewnątrz ciągu, podczas gdy przy użyciu podejścia wskaźnika nie możesz. Jest to bardzo ważna kwestia, o której należy pamiętać, że uratuje cię przed kompilatorem, gdy duży mężczyźni przychodzą do twojego domu i robią straszne rzeczy dla babci. Idąc trochę dalej, kolejnym problemem, o którym powinieneś pamiętać, jest to, że jeśli zapomnisz o wskazówkach, wykonane są połączenia w C według wartości. Kiedy więc funkcja potrzebuje czegoś ze zmiennej, wykonana jest kopia lokalna i wykonuje się prace nad tym. Ale jeśli funkcja zmienia zmienną, zmiany nie są odzwierciedlone, ponieważ oryginał pozostaje nienaruszony. Korzystając z wskaźników, możesz użyć połączeń przez odniesienie, Jak zobaczysz w naszym przykładzie poniżej. Ponadto wywołanie wartości może stać się wymagające zasobów, jeśli obiekty pracujące są duże. Technicznie jest również połączenie pointera, ale na razie zachowajmy to proste.

Powiedzmy, że chcemy napisać funkcję, która wymaga liczby całkowitej jako argument i zwiększa ją z pewną wartością. Prawdopodobnie będziesz miał ochotę napisać coś takiego:

void inclu (int a) a+= 20; 

Teraz, jeśli spróbujesz, zobaczysz, że liczba całkowita nie zostanie zwiększona, ponieważ tylko lokalna kopia będzie. Jeśli napisałeś

void inclu (int & a) a+= 20; 

Twój argument liczb całkowitych zostanie zwiększony o dwadzieścia, czego chcesz. Więc jeśli nadal masz wątpliwości co do przydatności wskaźników, oto jeden prosty, ale znaczący przykład.



Nieco zaawansowane tematy

Myśleliśmy o umieszczeniu tych tematów w specjalnej sekcji, ponieważ są one trochę trudniejsze do zrozumienia dla początkujących, ale są przydatne, konieczne wiedzą w programowaniu C. Więc…

Wskazówki do wskazówek

Tak, wskaźniki są zmiennymi tak jak każda inna, więc mogą mieć inne zmienne wskazujące na nie. Podczas gdy proste wskaźniki, jak widać powyżej, mają jeden poziom „wskazywania”, wskazówki do wskazówek mają dwa, więc taki zmienny wskazuje na inny, który wskazuje na inny. Myślisz, że to jest szalone? Możesz mieć wskazówki do wskazówek do wskazówek do wskaźników do… .ad infinitum, ale już przekroczyłeś próg psychiczny i użyteczność, jeśli masz takie deklaracje. Zalecamy użycie CDECL, który jest małym programem zwykle dostępnym w większości dystrybucji Linux, który „tłumaczy” między C i C ++ i angielskim i na odwrót. Tak więc wskaźnik do wskaźnika może zostać ogłoszony jako

int ** ptrtoptr;

Teraz, zgodnie z tym, jak użyte są wskaźniki wielokrotnego poziomu, istnieją sytuacje, w których masz funkcje, takie jak porównanie powyżej, i chcesz uzyskać od nich wskaźnik jako wartość zwrotu. Możesz także chcieć szeregu ciągów, co jest bardzo przydatną funkcją, jak zobaczysz w kaprysu.

Wielowymiarowe tablice

Tablice, które do tej pory widziałeś, są jednoznaczne, ale to nie znaczy, że jesteś do tego ograniczony. Na przykład można wyobrazić sobie tablicę dwukierunkową jako szereg tablic. Radzę użyć wielowymiarowych tablic, jeśli czujesz potrzebę, ale jeśli jesteś dobry z prostym, dobrym ole „jednorazowym, użyj tego, aby twoje życie jako kodera będzie prostsze. Aby zadeklarować tablicę dwukierunkową (używamy tutaj dwóch wymiarów, ale nie ograniczasz się do tej liczby), zrobisz to

 int bidimarray [4] [2];

który będzie miał wpływ na ogłoszenie tablicy liczb całkowitych 4 na 2. Aby uzyskać dostęp do drugiego elementu pionowo (pomyśl o krzyżówki, jeśli to pomoże!) i pierwszy poziomy, możesz to zrobić

Bidimarray [2] [1];

Pamiętaj, że te wymiary są tylko dla naszych oczu: kompilator przydziela pamięć i działa z tablicą w ten sam sposób, więc jeśli nie widzisz użyteczności, nie używaj go. Ergo, nasza tablica powyżej można ogłosić jako

int bidimarray [8]; / * 4 na 2, jak wspomniano */


Argumenty wiersza poleceń

W naszej poprzedniej odsłonie serii rozmawialiśmy o Main i o tym, jak można ją używać z argumentami lub bez. Kiedy Twój program go potrzebuje i masz argumenty, są one char Argc i char *argv []. Teraz, gdy wiesz, czym są tablice i wskaźniki, rzeczy zaczynają mieć znacznie większy sens. Myślimy jednak o tym, aby dowiedzieć się tutaj. char *argv [] można również napisać jako char ** argv. Jak uważasz, że jako trochę jedzenia do przemyślenia? Pamiętaj, że ARGV oznacza „wektor argumentów” i jest szeregiem stringów. Zawsze możesz polegać na fakcie, że Argv [0] jest nazwą samego programu, podczas gdy Argv [1] jest pierwszym argumentem i tak dalej. Tak więc krótki program zobaczenia jego nazwy i argumentów wyglądałby tak:

#include #include int main (int argc, char ** argv)  chwila(argc--) printf („%s \ n”, *argv ++); powrót 0; 

Wniosek

Wybraliśmy części, które wyglądały najważniejsze do zrozumienia wskaźników i tablic, i celowo pominęli niektóre tematy, takie jak wskaźniki do funkcji. Niemniej jednak, jeśli pracujesz z przedstawionymi tutaj informacjami i rozwiążesz ćwiczenia, będziesz miał całkiem niezły początek tej części C, która jest uważana za główne źródło skomplikowanego i niezrozumiałego kodu.

Oto doskonałe odniesienie do wskaźników C ++. Chociaż nie jest to C, języki są powiązane, więc artykuł pomoże ci lepiej zrozumieć wskaźniki.

Oto, czego możesz się spodziewać następnego:

  • I. C Opracowanie w Linux - Wprowadzenie
  • Ii. Porównanie C i innych języków programowania
  • Iii. Typy, operatorzy, zmienne
  • Iv. Kontrola przepływu
  • V. Funkcje
  • Vi. Wskaźniki i tablice
  • VII. Struktury
  • VIII. Podstawowe I/O
  • IX. Styl kodowania i zalecenia
  • X. Budowanie programu
  • Xi. Opakowanie dla Debiana i Fedory
  • XII. Uzyskanie pakietu w oficjalnych repozytoriach Debiana

Powiązane samouczki Linux:

  • Wprowadzenie do automatyzacji, narzędzi i technik Linuksa
  • Rzeczy do zainstalowania na Ubuntu 20.04
  • Mastering Bash Script Loops
  • Zagnieżdżone pętle w skryptach Bash
  • Mint 20: Lepsze niż Ubuntu i Microsoft Windows?
  • Big Data Manipulacja dla zabawy i zysku Część 1
  • Rzeczy do zrobienia po zainstalowaniu Ubuntu 20.04 Focal Fossa Linux
  • Samouczek debugowania GDB dla początkujących
  • Zaawansowane rejestrowanie i audyt w systemie Linux
  • Jak często musisz ponownie uruchomić serwer Linux?