Wskazówki dotyczące programowania

Write My PaperOrder your perfect paper right now!

Let professional writers deal with your paper, quickly and efficiently.

Write My Paper

Source: https://www.cs.uky.edu/~raphael/programming.html

Od uczniów młodszych i starszych klas CS oczekuje się znajomości podstawowych informacji na temat języków C , Unix i narzędzi do tworzenia oprogramowania . Na tej stronie szczegółowo opisano niektóre z tych zagadnień i zasugerowano kilka ćwiczeń .

Table of contents

C

  1. C pozwala deklarować zmienne poza jakąkolwiek procedurą. Zmienne te nazywane są zmiennymi globalnymi .
  • Zmienna globalna jest przydzielana jednorazowo podczas uruchamiania programu i pozostaje w pamięci aż do zakończenia programu.
  • Zmienna globalna jest widoczna dla wszystkich procedur w tym samym pliku.
  • Możesz sprawić, że zmienna globalna zadeklarowana w pliku Ac będzie widoczna dla wszystkich procedur w niektórych innych plikach Bc, Cc, Dc, ... deklarując ją z modyfikatorem extern w plikach Bc, Cc, Dc, ... jak w tym przykładzie:
    extern in theVariable
    Jeśli masz wiele plików współdzielących tę zmienną, powinieneś zadeklarować ją jako extern w pliku nagłówkowym foo.h (opisanym poniżej ) i użyć #include foo.h w plikach Bc, Cc, Dc, ... . Musisz zadeklarować zmienną w dokładnie jednym pliku bez modyfikatora extern , w przeciwnym razie w ogóle nie zostanie ona przydzielona.
  • Nie jest dobrym pomysłem używanie zbyt wielu zmiennych globalnych, ponieważ nie można zlokalizować miejsc, w których są one dostępne. Są jednak sytuacje, w których zmienne globalne pozwalają uniknąć przekazywania wielu parametrów do funkcji.
  1. Ciągi znaków są wskaźnikami do tablic znaków zakończonych znakiem null.
  • Deklarujesz ciąg jako char *variableName .
  • Jeśli Twój ciąg znaków jest stały, możesz po prostu przypisać do niego literał ciągu:
    char *myString = "To jest przykładowy ciąg znaków";
  • Pusty ciąg ma tylko terminator zerowy:
    mójCiąg = ""; // pusty ciąg znaków o długości 0, zawierający wartość null

Wskaźnik zerowy nie jest prawidłową wartością ciągu:
mójCiąg = NULL; // nieprawidłowy ciąg
Możesz użyć takiej wartości, aby wskazać koniec tablicy ciągów:
argv[0] = "nazwa programu";

argv[1] = "pierwszyparametr";

argv[2] = "drugiparametr";

  • argv[3] = NULL; //terminator

Jeśli ciąg znaków jest obliczany w czasie wykonywania, musisz zarezerwować wystarczająco dużo miejsca, aby go przechowywać. Pokój musi wystarczyć, aby pomieścić wartość null na końcu:
char *mójString;

mójString = (char *) malloc(strlen(jakiśCiąg) + 1); // przydziel miejsce

  • strcpy(myString, jakiśString); // skopiuj SomeString do myString
  • Aby uniknąć wycieków pamięci, powinieneś w końcu zwrócić miejsce przydzielone za pomocą malloc , używając free . Jego parametrem musi być początek spacji zwróconej przez malloc :
    free((void *) mójString);
    Aby zachować czystość i czytelność kodu, powinieneś wywołać free() w tej samej procedurze, w której wywołujesz malloc() ; możesz wywołać inne procedury między tymi dwoma punktami, aby manipulować ciągiem.
  • Jeśli kopiujesz ciągi znaków, powinieneś bardzo uważać, aby nigdy nie kopiować więcej bajtów, niż może pomieścić docelowa struktura danych. Przepełnienia bufora są najczęstszą przyczyną luk w zabezpieczeniach programów. W szczególności rozważ użycie strncpy() i strncat() zamiast strcpy() i strcat() .

Jeśli używasz C++, musisz przekonwertować obiekty łańcuchowe na ciągi w stylu C przed przekazaniem ich w wywołaniu systemowym.
string myString // Ta deklaracja działa tylko w C++

...

  • SomeCall(myString.c_str())
    Niestety, c_str() zwraca niezmienny ciąg znaków. Jeśli potrzebujesz modyfikowalnego ciągu, możesz skopiować dane za pomocą strcpy() (jak powyżej) lub możesz rzutować typ:
    SomeCall(const_cast(myString.c_str()))
    Rzutowanie nie jest tak bezpieczne jak kopiowanie, ponieważ funkcja SomeCall() może w rzeczywistości zmodyfikować ciąg znaków, co mogłoby zmylić każdą część programu, która zakłada, że ​​myString jest stała, co jest typowym zachowaniem ciągów C++.
  1. Bufor to obszar pamięci pełniący rolę pojemnika na dane . Mimo że dane mogą mieć interpretację (taką jak tablica struktur z wieloma polami), programy, które buforują odczyt i zapis, często traktują je jako tablice bajtów. Tablica bajtów to nie to samo, co ciąg znaków, mimo że oba są zadeklarowane jako char * lub char [] .
  • Mogą nie zawierać znaków ASCII i nie mogą być zakończone znakiem null.
  • Nie można użyć funkcji strlen() do znalezienia długości danych w buforze (ponieważ bufor może zawierać bajty zerowe). Zamiast tego musisz obliczyć długość danych na podstawie wartości zwracanej przez wywołanie systemowe (zwykle read ), które wygenerowało dane.
  • Nie możesz używać strcpy() , strcat() ani podobnych procedur w buforach bajtowych; zamiast tego musisz użyć memcpy() lub bcopy() .

Zapisujesz bufor o wielkości 123 bajtów do pliku, używając kodu takiego jak ten:
char *nazwa pliku = "/tmp/foo"

#zdefiniuj ROZMIAR BUF 4096

char buf[BUFROZMIAR]; // bufor zawierający maksymalnie bajtów BUFSIZE

...

int plik wyjściowy; // deskryptor pliku, mała liczba całkowita

int bajtówToWrite; // liczba bajtów pozostałych do zapisania

char *outPtr = buf;

...

if ((outFile = creat(fileName, 0660)) < 0) [ // błąd

// zobaczuprawnienia do plików , aby zrozumieć 0660

perror(nazwa pliku); // wypisz przyczynę

wyjście(1); // i wyjdź

[

bajtówToWrite = 123; // inicjalizacja; 123 to tylko przykład

while ((bytesWritten = write(outFile, outPtr, bytesToWrite)) < bytesToWrite) [

// nie wszystkie bajty zostały jeszcze zapisane

if (bytesWritten < 0) [ // błąd

perror("zapisz");

wyjście(1);

[

outPtr += bajtyZapisane;

bytesToWrite -= bajty zapisane;

  • [

Aby kompilator przydzielił miejsce na bufory, musisz zadeklarować bufor o rozmiarze, który kompilator może obliczyć, jak w
#zdefiniuj ROZMIAR BUF 1024

  • char buf[BUFROZMIAR];
    Jeśli po prostu zadeklarujesz bufor bez rozmiaru:
    char buf[];
    wtedy ma nieznany rozmiar i C nie przydziela żadnego miejsca. Jest to dopuszczalne, jeśli buf jest parametrem formalnym (to znaczy pojawia się w nagłówku procedury); rzeczywisty parametr (dostarczony przez osobę wywołującą) ma rozmiar. Ale jest to niedopuszczalne, jeśli buf jest zmienną. Jeśli nie znasz rozmiaru bufora w czasie kompilacji, powinieneś użyć takiego kodu:
    char *buf = (char *) malloc(rozmiarbufora);
    gdzie buforSize jest wynikiem niektórych obliczeń w czasie wykonywania.
  1. Można dynamicznie przydzielać i zwalniać pamięć.

Poszczególne instancje dowolnego typu:
typedef ... mójTyp;

mójTyp *mojaZmienna = (mójTyp *) malloc(rozmiar(myTyp));

// możesz teraz uzyskać dostęp do *myVariable.

...

  • free((void *) mojaZmienna);
    Ponownie, dobrą praktyką programistyczną jest wywoływanie free() w tej samej procedurze, w której wywołujesz malloc() .

Tablice jednowymiarowe dowolnego typu:
mójTyp *myArray = (myTyp *) malloc(Długość tablicy * rozmiar(myTyp));

// myArray[0] .. myArray[arrayLength - 1] są teraz przydzielone.

...

  • free((void *) mojaArray);

Tablice dwuwymiarowe są reprezentowane przez tablicę wskaźników, z których każdy wskazuje na tablicę:
myType **myArray = (myType **) malloc(numRows * sizeof(myType *));

int indeks wiersza;

for (rowIndex = 0; rowIndex < numRows; rowIndex += 1) [

myArray[rowIndex] = (myType *) malloc(numColumns * sizeof(myType));

[

// myArray[0][0] .. myArray[0][numColumns-1] .. myArray[numRows-1][numColumns-1]

// są teraz przydzielone. Być może zechcesz je zainicjować.

...

for (rowIndex = 0; rowIndex < numRows; rowIndex += 1) [

free((void *) mojaArray[rowIndex]);

[

free((void *) mojaArray);

  • Jeśli używasz C++, nie mieszaj new/delete z malloc/free dla tej samej struktury danych. Zaletą new/delete dla instancji klas jest to, że automatycznie wywołują konstruktory, które mogą inicjować dane, oraz destruktory, które mogą finalizować dane. Kiedy używasz malloc/free , musisz jawnie zainicjować i sfinalizować.
  1. Liczby całkowite
  • C zwykle reprezentuje liczby całkowite w 4 bajtach. Na przykład liczba 254235 jest reprezentowana jako liczba binarna 00000000,00000011,11100001,00011011.
  • Z drugiej strony tekst ASCII reprezentuje liczby jak każdy inny znak, z jednym bajtem na cyfrę przy standardowym kodowaniu. W kodzie ASCII liczba 254235 jest reprezentowana jako 00110010, 00110101, 00110110, 00110010, 00110011, 00110101.
  • Jeśli chcesz napisać plik liczb całkowitych, ogólnie rzecz biorąc, zarówno pod względem przestrzennym, jak i czasowym, bardziej efektywne jest zapisanie wersji 4-bajtowych niż konwersja ich na ciągi ASCII i zapisanie ich. Oto jak zapisać pojedynczą liczbę całkowitą do otwartego pliku:
    write(outFile, &myInteger, sizeof(moja liczba całkowita))

Możesz przyjrzeć się poszczególnym bajtom liczby całkowitej, rzucając ją jako strukturę czterech bajtów:
int adres IP; // przechowywana jako liczba całkowita, rozumiana jako 4 bajty

struktura typedef [

char bajt1, bajt2, bajt3, bajt4;

[ IPSzczegóły_t;

IPDetails_t *details = (IPDetails_t *) (&Adres IP);

printf("bajt 1 to %o, bajt 2 to %o, bajt 3 to %o, bajt 4 to %o\n",

  • szczegóły->bajt1, szczegóły->bajt2, szczegóły->bajt3, szczegóły->bajt4);
  • Wielobajtowe liczby całkowite mogą być reprezentowane w różny sposób na różnych maszynach. Niektóre (jak Sun SparcStation) umieszczają najbardziej znaczący bajt na pierwszym miejscu; inne (takie jak Intel i80x86 i jego potomkowie) umieszczają najmniej znaczący bajt na pierwszym miejscu. Jeśli piszesz dane w postaci liczb całkowitych, które mogą być odczytywane na innych komputerach, przekonwertuj dane na „sieciową” kolejność bajtów za pomocą funkcji htons() lub htonl() . Jeśli czytasz dane w postaci liczb całkowitych, które mogły zostać zapisane na innych komputerach, przekonwertuj dane z kolejności „sieciowej” na lokalną kolejność bajtów za pomocą ntohs() lub ntohl() .

Możesz przewidzieć układ pamięci struktur i wartość zwróconą przez sizeof() . Na przykład,
struktura foo [

znak a; // wykorzystuje 1 bajt

// C wstawia tutaj 3-bajtową dokładkę, więc b może zaczynać się od 4-bajtowej granicy

int b; // wykorzystuje 4 bajty

bez znaku krótkie c; // wykorzystuje 2 bajty

bez znaku znak d[2]; // wykorzystuje 2 bajty

  1. [;
    Dlatego sizeof(struct foo ) zwraca 12. Ta przewidywalność (dla danej architektury) jest powodem, dla którego niektórzy nazywają C „przenośnym językiem asemblera”. Należy przewidzieć układ struktury podczas generowania danych, które muszą mieć określony format, taki jak nagłówek pakietu sieciowego.
  2. Możesz zadeklarować wskaźniki w C do dowolnego typu i przypisać im wartości wskazujące na obiekty tego typu.

W szczególności C pozwala budować wskaźniki do liczb całkowitych:
int pewna liczba całkowita;

int *intPtr = &pewna liczba całkowita; // deklaruje zmienną o wartości wskaźnika i przypisuje odpowiednią wartość wskaźnika

jakieśWywołanie(intPtr); // przekazuje wskaźnik jako rzeczywisty parametr

  • SomeCall(&someInteger); // ma taki sam efekt jak powyżej
  • Procedura biblioteki AC, która pobiera wskaźnik do wartości, najprawdopodobniej modyfikuje tę wartość (staje się parametrem „out” lub „in out”). W powyższym przykładzie jest bardzo prawdopodobne, że SomeCall modyfikuje wartość liczby całkowitej SomeInteger .

Można zbudować wskaźnik do tablicy liczb całkowitych i użyć go do przechodzenia przez tę tablicę.
#zdefiniuj ARRAY_LENGTH 100

int intArray[ARRAY_LENGTH];

int *intArrayPtr;

...

suma całkowita = 0;

for (intArrayPtr = intArray; intArrayPtr < intArray+ARRAY_LENGTH; intArrayPtr += 1) [

suma += *intArrayPtr;

  • [

Można zbudować wskaźnik do tablicy struktur i użyć go do przechodzenia przez tę tablicę.
#zdefiniuj ARRAY_LENGTH 100

typedef struct [int foo, bar;[ pair_t; // pair_t to nowy typ

para_t strukturaArray[ARRAY_LENGTH]; // structArray jest tablicą elementów ARRAY_LENGTH pair_t

para_t *structArrayPtr; // structArrayPtr wskazuje na element pair_t

...

suma całkowita = 0;

for (structArrayPtr = structArray; structArrayPtr < structArray+ARRAY_LENGTH; structArrayPtr += 1) [

suma += structArrayPtr->foo + structArrayPtr->bar;

  • [
  • Po dodaniu liczby całkowitej do wskaźnika wskaźnik zostanie przesunięty o określoną liczbę elementów, niezależnie od tego, jak duże są te elementy. Kompilator zna rozmiar i postępuje właściwie.
  1. Wyjście
  • Formatujesz dane wyjściowe za pomocą printf lub jego wariantu, fprintf .
  • Ciąg formatujący używa %d , %s , %f do wskazania, że ​​na wyjściu ma zostać umieszczona liczba całkowita, ciąg znaków lub wartość rzeczywista.
  • Ciąg formatujący używa \t i \n do wskazania tabulacji i nowej linii.
  • Przykład:
    printf("Myślę, że liczba %d to %s\n", 13, "szczęśliwa");
  • Mieszanie printf() , fprintf() i cout może nie spowodować wydrukowania elementów w oczekiwanej kolejności. Używają niezależnych obszarów przejściowych („buforów”), które drukują, gdy są pełne.
  1. Procedura main() pobiera parametry funkcji, które reprezentują parametry wiersza poleceń .
  • Jednym z powszechnych sposobów pisania głównej procedury jest:
    int main(int argc; char *argv[]);
    Tutaj argc to liczba parametrów, a argv to tablica ciągów znaków, czyli tablica wskaźników do tablic znaków zakończonych znakiem null.

Zgodnie z konwencją pierwszym elementem argv jest nazwa samego programu.
int main(int argc; char *argv[]);

printf("Mam %d parametrów; nazywam się %s, a mój pierwszy parametr to %s\n",

  • argc, argv[0], argv[1]);
  1. Przydatne funkcje językowe
  • Możesz zwiększyć liczbę całkowitą lub ustawić wskaźnik na następny obiekt, używając operatora ++ . Zwykle najlepiej jest umieścić ten operator po zmiennej: myInt++ . Jeśli umieścisz ++ przed zmienną, zmienna będzie zwiększana przed jej oceną, a rzadko jest to, czego chcesz.

Możesz zbudować przypisanie, w którym zmienna po lewej stronie będzie pierwszą częścią wyrażenia po prawej stronie:
mojaInt -= 3; // odpowiednik myInt = myInt - 3

mojaInt *= 42; // odpowiednik myInt = myInt * 42

  • mojaInt += 1; // odpowiednik i być może lepszy niż myInt++
  • Liczby można wyrażać w systemie dziesiętnym, ósemkowym (poprzedzając cyfrą 0 , jak w przypadku 0453 ) lub szesnastkowo (poprzedzając cyfrą 0x , jak w przypadku 0xffaa ).

Możesz traktować liczbę całkowitą jako zbiór bitów i wykonywać operacje bitowe:
mojaInt = mojaInt | 0444; // bitowy LUB; 0444 jest w formacie ósemkowym

mojaInt &= 0444; // bitowe AND ze skrótem przypisania

  • myInt = coś ^ cokolwiek; //bitowy XOR

C i C++ mają wyrażenia warunkowe. Zamiast pisać
jeśli (a < 7)

a = pewna wartość

w przeciwnym razie

  • a = inna wartość;
    Możesz pisać
    a = a < 7? jakaśWartość: jakaśInnaWartość;
  • Przypisania zwracają wartość po lewej stronie, dzięki czemu można uwzględnić przypisanie w większych wyrażeniach, takich jak wyrażenia warunkowe. Powinieneś jednak przestrzegać konwencji, zgodnie z którą takie przypisania są zawsze otoczone nawiasami, aby wskazać zarówno osobie czytającej Twój kod, jak i kompilatorowi, że tak naprawdę masz na myśli przypisanie, a nie test równości. Na przykład napisz
    if ((s = gniazdo(...)) == -1)
    nie
    if (s = gniazdo(...) == -1)
    Druga wersja jest zarówno trudniejsza do odczytania, jak i w tym przypadku niepoprawna, ponieważ operator równości == ma wyższy priorytet niż operator przypisania = .
  1. Programy, które nie są trywialnie krótkie, powinny być zwykle rozłożone na wiele plików źródłowych , każdy o nazwie kończącej się na .c (w przypadku programów w języku C) lub .cpp (w przypadku programów w języku C++).
  • Spróbuj zgrupować funkcje, które manipulują tymi samymi strukturami danych lub mają powiązane cele, w tym samym pliku.
  • Wszystkie typy, funkcje, zmienne globalne i stałe manifestu, które są potrzebne więcej niż jednemu plikowi źródłowemu, należy również zadeklarować w pliku nagłówkowym , którego nazwa kończy się na .h .
  • Z wyjątkiem funkcji wbudowanych, nie deklaruj treści funkcji (ani czegokolwiek, co powoduje, że kompilator generuje kod lub przydziela miejsce) w pliku nagłówkowym.
  • Każdy plik źródłowy powinien odnosić się do potrzebnych plików nagłówkowych za pomocą linii #include .
  • Nigdy #nie dołączaj pliku .c .
  1. Jeśli masz wiele plików źródłowych, musisz połączyć wszystkie skompilowane pliki obiektowe wraz z bibliotekami, których potrzebuje Twój program.
  • Najłatwiejszą metodą jest użycie kompilatora C, który zna biblioteki C:
    gcc *.o -o mójProgram
    To polecenie prosi kompilator o połączenie wszystkich plików obiektowych z biblioteką C (która jest domyślnie dołączona) i umieszczenie wyniku w pliku myProgram , który staje się wykonywalny.
  • Jeśli Twój program potrzebuje innych bibliotek, powinieneś podać je po plikach obiektowych, ponieważ linker zbiera tylko procedury z bibliotek, o których już wie, że są potrzebne, i łączy pliki w określonej przez Ciebie kolejności. Jeśli więc potrzebujesz biblioteki takiej jak libxml2 , polecenie łączenia powinno wyglądać mniej więcej tak:
    gcc *.o -lxml2 -o mójProgram
    Kompilator wie, jak przeszukiwać różne standardowe katalogi w poszukiwaniu bieżącej wersji libxml2 .
  1. Debugowanie programów w języku C
  • Jeśli wystąpi błąd segmentacji, najprawdopodobniej masz indeks poza zakresem, niezainicjowany wskaźnik lub wskaźnik zerowy.
  • Możesz umieścić instrukcje print w swoim programie, aby pomóc Ci zlokalizować błąd.
  • Debugowanie będzie prawdopodobnie najskuteczniejsze, jeśli użyjesz gdb (opisanego poniżej ), aby dowiedzieć się, gdzie leży błąd.
  • Programy działające przez długi czas muszą uważać, aby zwolnić całą przydzieloną pamięć, w przeciwnym razie w przeciwnym razie zabraknie im pamięci. Aby debugować wycieki pamięci, możesz zapoznać się z tymi artykułami na temat debugowania wycieków pamięci w C i C++ .

Uniksa

pliki standardowe , polecenia , wywołania systemowe , uprawnienia do plików

  1. Zgodnie z konwencją każdy proces rozpoczyna się od otwarcia trzech standardowych plików : standardowego wejścia, standardowego wyjścia i standardowego błędu, powiązanych z deskryptorami plików 0, 1 i 2.
  • Standardowe wejście jest zwykle podłączone do klawiatury. Cokolwiek wpiszesz, trafia do programu.
  • Standardowe wyjście jest zwykle podłączone do ekranu. Niezależnie od tego, co wyjdzie z programu, stanie się widoczne.
  • Błąd standardowy jest również zwykle podłączony do ekranu.
  • Możesz użyć powłoki do wywoływania programów, tak aby standardowe wyjście jednego programu było bezpośrednio połączone („potokowane”) ze standardowym wejściem innego programu:
    jest | toaleta

Możesz użyć powłoki do wywoływania programów, aby standardowe wejście i/lub wyjście zostało połączone z plikiem:
ls > lsOutFile

wc < lsOutFile

  • sort -u posortowanyPlik
  • Ogólnie rzecz biorąc, programy nie wiedzą lub nie przejmują się tym, czy powłoka zmieniła znaczenie ich standardowych plików.
  1. Polecenia Uniksa
  • Polecenia to po prostu nazwy plików wykonywalnych. Zmienna środowiskowa PATH mówi powłoce , gdzie ich szukać. Zazwyczaj ta zmienna ma wartość taką jak /bin:/usr/bin:/usr/local/bin:. .
  • Aby zobaczyć, gdzie powłoka znajduje konkretny program, na przykład vim , powiedz, gdzie vim .
  1. Wywołania systemowe i wywołania bibliotek podlegają pewnym ważnym konwencjom.
  • Wartość zwracana przez wywołanie zwykle wskazuje, czy wywołanie powiodło się (zwykle wartość wynosi 0 lub jest dodatnia), czy też nie powiodło się (zwykle wartość wynosi -1).

Zawsze sprawdzaj wartość zwracaną przez wywołania biblioteki. Gdy wywołanie systemowe nie powiedzie się, funkcja perror() może wyświetlić informację o błędzie (do standardowego błędu):
int fd;

char *nazwa pliku = "mójplik";

if ((fd = open(nazwa pliku, O_RDONLY)) < 0) [

perror(nazwa pliku); // może wyświetlić „mójplik: Nie ma takiego pliku ani katalogu”

  • [
  • Strona podręcznika dotycząca wywołania systemowego lub procedury bibliotecznej może zawierać listę typów danych, których nie definiuje, na przykład size_t lub time_t lub O_RDONLY . Typy te są zazwyczaj zdefiniowane w plikach nagłówkowych wymienionych na stronie podręcznika; musisz dołączyć wszystkie te pliki nagłówkowe do swojego programu C.
  1. Uprawnienia do plików w systemie Unix są zwykle wyrażane liczbami ósemkowymi.
  • W powyższym przykładzie funkcji creat() 0660 jest liczbą ósemkową (to właśnie oznacza początkowe 0), reprezentującą binarną liczbę 110 110 000. Ta liczba ósemkowa przyznaje uprawnienia do odczytu i zapisu, ale nie uprawnienia do wykonywania, właścicielowi pliku i grupie pliku, ale nie ma uprawnień innym użytkownikom.
  • Uprawnienia ustawiasz podczas tworzenia pliku za pomocą parametru wywołania creat() .
  • Polecenie ls -l pokazuje uprawnienia do plików.
  • Możesz zmienić uprawnienia do posiadanego pliku za pomocą programu chmod .
  • Wszystkie Twoje procesy mają cechę zwaną umask, zwykle przedstawianą jako liczba ósemkowa. Kiedy proces tworzy plik, bity umaski są usuwane z uprawnień określonych w wywołaniu creat() . Jeśli więc Twój umask ma wartość 066, inni nie będą mogli czytać ani zapisywać utworzonych przez Ciebie plików, ponieważ 066 reprezentuje uprawnienia do odczytu i zapisu dla Twojej grupy i innych osób. Możesz sprawdzić i zmodyfikować swój umask za pomocą programu umask , który zwykle wywołujesz w skrypcie startowym powłoki (w zależności od powłoki, ~/.login lub ~/.profile ).

Narzędzia do tworzenia oprogramowania

edytor tekstu , debugger , kompilator , strony podręcznika , tworzenie , wyszukiwanie ,

  1. Użyj edytora tekstu, aby tworzyć, modyfikować i sprawdzać swój program. Dostępnych jest kilka rozsądnych edytorów tekstu.
  • Nauczenie się edytora vima i jego interfejsu graficznego gvim wymaga trochę wysiłku, ale zapewnia bardzo wysokiej jakości zestaw narzędzi do edycji plików programów, w tym podświetlanie składni, dopasowywanie nawiasów, uzupełnianie słów, automatyczne wcięcia, wyszukiwanie według znaczników (które przesuwają szybkie przejście z miejsca, w którym program wywołuje funkcję do miejsca, w którym funkcja jest zdefiniowana) oraz zintegrowane wyszukiwanie stron podręcznika. Vim jest przeznaczony do obsługi klawiatury; nie musisz nigdy używać myszy, jeśli nie chcesz. Jest swobodnie dostępny dla systemów operacyjnych Unix, Win32 i Microsoft. Jest to najbardziej rozwinięta wersja serii edytorów, która obejmuje ed , ex , vi iElvisa . Możesz przeczytać dokumentację online vima i uzyskać natychmiastową pomoc za pomocą polecenia : help vima .
  • Edytor emacsa jest, jeśli w ogóle, bardziej funkcjonalny niż vim . Nauka wymaga również dużego wysiłku. Jest również swobodnie dostępny dla systemów operacyjnych Unix i Microsoft. Dokumentację znajdziesz tutaj .
  • Dostępnych jest wiele innych edytorów tekstu, ale generalnie nie zapewniają one dwóch najbardziej przydatnych funkcji potrzebnych do tworzenia programów: automatycznego wcięcia i podświetlania składni. Jednak te edytory tekstu często mają tę zaletę, że są łatwiejsze do nauczenia, zgodnie z ich ograniczonymi możliwościami. Do tych edytorów tekstu o niższej jakości należą (dla Uniksa) pico , gedit i joe oraz (dla Microsoftu) notatnik i word .
  • Być może znasz zintegrowane środowisko programistyczne (IDE), takie jak Eclipse, Code Warrior lub .NET. Środowiska te zazwyczaj mają edytory tekstu zintegrowane z debugerami i kompilatorami. Jeśli używasz takiego IDE, warto skorzystać z powiązanych edytorów tekstu.
  1. gdb to debuger , który rozumie zmienne i strukturę programu.
  • Dokumentację znajdziesz tutaj .
  • Aby efektywnie używać gdb , musisz przekazać flagę -g do kompilatora C lub C++.
  • Jeśli Twój program myProgram nie pozostawił pliku o nazwie core , spróbuj gdb myProgram core .
  • Możesz także uruchomić swój program od początku pod kontrolą gdb : gdb myProgram .
  • Wszystkie polecenia gdb można skrócić do unikalnego przedrostka.
  • Polecenie pomocy jest bardzo przydatne.
  • Polecenie Where pokazuje stos wywołań, w tym numery linii pokazujące, gdzie znajduje się każda procedura. Jest to pierwsze polecenie, które powinieneś wypróbować podczas debugowania pliku podstawowego.
  • Aby wydrukować wartość jakiegoś wyrażenia (możesz dołączyć swoje zmienne i zwykłe operatory C), wpisz print wyrażenie , jak w
    drukuj (myInt + 59) & 0444;
  • Aby zobaczyć swój program, spróbuj wyświetlić listę myFunction lub listę myFile.c:38 .
  • Aby ustawić inny rekord aktywacji jako bieżący, użyj polecenia w górę (dla nowszego) lub w dół (dla nowszego).
  • Możesz ustawić punkt przerwania w dowolnej linii dowolnego pliku. Na przykład możesz powiedzieć break foo.p:38 , aby ustawić punkt przerwania w linii 38 w pliku foo.p. Za każdym razem, gdy program podczas wykonywania natrafi na tę linię, zatrzyma się, a gdb wyświetli monit o podanie poleceń. Możesz na przykład przyjrzeć się zmiennym lub przejść dalej przez program.
  • Następne polecenie powoduje przejście o jedną instrukcję do przodu (wywołanie i powrót z dowolnej procedury, jeśli to konieczne) .
  • Polecenie step powoduje przejście do przodu o jedną instrukcję, ale jeśli instrukcja zawiera wywołanie procedury, wchodzi do procedury i zatrzymuje się na pierwszej instrukcji.
  • Jeśli wpiszesz polecenie set follow-fork-mode child , to kiedy program wykona wywołanie fork() , gdb będzie kontynuował debugowanie dziecka, a nie rodzica.
  • Opuść gdb , wprowadzając polecenie Quit .
  • Możesz preferować użycie graficznego interfejsu ddd do gdb .
  1. Zawsze dawaj programom kompilacyjnym gcc lub g++ flagę -Wall , aby włączyć wysoki poziom ostrzeżeń. Podobnie nadaj javacowi flagę -Xlint:all . Nie włączaj programu, który generuje ostrzeżenia w czasie kompilacji.
  2. Możesz przeczytać podręcznik , aby uzyskać szczegółowe informacje o programach, procedurach biblioteki C i wywołaniach systemowych Uniksa, używając programu man , na przykład man printf lub man gcc .
  • Czasami żądana funkcja znajduje się w określonej sekcji podręcznika Uniksa i musisz wyraźnie o nią poprosić: man 2 open lub man 3 printf . Sekcja 1 dotyczy programów, sekcja 2 dotyczy wywołań systemowych, sekcja 3 dotyczy biblioteki C, a sekcja 8 dotyczy administracji systemem. Najprawdopodobniej nie potrzebujesz innych sekcji.
  • Możesz sprawdzić, czy jakiś program, procedura biblioteki C lub wywołanie systemowe Uniksa jest istotne dla jakiegoś tematu, używając flagi -k , jak w przypadku man -k print .
  1. Użyj programu make , aby uporządkować przepisy dotyczące rekompilacji i ponownego łączenia programu po zmianie pliku źródłowego.

Jeśli Twój program składa się z kilku plików, możesz skompilować je osobno, a następnie połączyć ze sobą. Kompilujesz z flagą -c i używasz flagi -o do wskazania pliku wyjściowego. Rozsądny plik makefile może wyglądać następująco:
ŹRÓDŁA = sterownik.c wejście.c wyjście.c

OBIEKTÓW = sterownik.o wejście.o wyjście.o

NAGŁÓWKI = wspólne.h

CFLAGS = -g -Ściana

program: $(OBIEKTÓW)

$(CC) $(CFLAGS) $(OBIEKTÓW) -o program

$(OBIEKTÓW): $(NAGŁÓWKI)

testRun: program

  • program Ten plik makefile wykorzystuje wbudowaną definicję CC i wbudowaną regułę do konwersji plików źródłowych C, takich jak sterownik.c, na ich plik obiektowy. Jeśli zmodyfikujesz tylko input.c , to make testRun spowoduje, że kompilator odbuduje input.o , następnie kompilator ponownie połączy obiekty, utworzy program , a następnie uruchomi program ze standardowym wejściem przekierowanym z pliku testData .
  • Jeśli masz wiele plików źródłowych i wiele plików nagłówkowych, możesz użyć programu makedependent do automatycznego zbudowania reguł Makefile , które określają, w jaki sposób pliki źródłowe zależą od plików nagłówkowych. W powyższym przykładzie założono, że wszystkie pliki źródłowe zależą od wszystkich plików nagłówkowych, co często nie ma miejsca.
  1. Program grep może szybko wyszukać definicję lub zmienną, szczególnie w plikach dołączanych:
    grep "struct timeval [" /usr/include/*/*.h

Ćwiczenia

Wykonaj te ćwiczenia w języku C.

  1. Napisz program o nazwie atoi , który otwiera plik danych nazwany w wierszu poleceń i wczytuje z niego pojedynczy wiersz wejściowy, który powinien zawierać liczbę całkowitą przedstawioną w znakach ASCII. Program konwertuje ten ciąg na liczbę całkowitą, mnoży liczbę całkowitą przez 3 i wypisuje wynik w formacie standardowym. Program nie może używać funkcji atoi() . Powinieneś użyć programu make . Twój plik Makefile powinien mieć trzy reguły: atoi , run (który uruchamia program na standardowych danych testowych i przekierowuje dane wyjściowe do nowego pliku) i clean(który usuwa pliki tymczasowe). Upewnij się, że program działa poprawnie na złych danych i kończy działanie z pomocnym komunikatem, jeśli brakuje pliku danych lub jest on nieczytelny. Przejdź przez program, uruchamiając go za pomocą gdb , umieszczając punkt przerwania w funkcji main() i wielokrotnie używając polecenia step .
  2. Wyszukaj stronę podręcznika programu cat . Zakoduj własną wersję cat . Twoja wersja musi akceptować wiele parametrów nazw plików (lub nie akceptować ich wcale). Nie musi akceptować żadnych parametrów opcji.
  3. Napisz program usuwaniaSuffix , który przyjmuje pojedynczy parametr: nazwę pliku sufiksu. Plik przyrostków zawiera jedną linię na wpis. Wpis to niepusty ciąg znaków, który nazywamy przyrostkiem , po którym następuje znak >, po którym następuje inny (prawdopodobnie pusty) ciąg znaków, który nazywamy zamianą . Twój program powinien przechowywać wszystkie przyrostki i ich zamienniki w tablicy mieszającej. Użyj zewnętrznego łańcucha. Twój program powinien następnie czytać standardowe wejście. Dla każdego słowa w oddzielonego spacjami w danych wejściowych znajdź najdłuższy przyrostek s występujący w w i zmodyfikuj w , usuwając słowo s i wstawiajączamiana s , utworzenie w' . Wypisz jedną linię na każde zmodyfikowane słowo w postaci w>w' . Nie pisz żadnych słów, które nie zostały zmodyfikowane.
Write My PaperOrder your perfect paper right now!

Let professional writers deal with your paper, quickly and efficiently.

Write My Paper
Article posted on:Sep 6, 2024
Article updated on:Sep 6, 2024