CLion

CLion jest programem do edycji kodu w językach C i C++. Aby mieć na niego licencję, wystarczy założyć na stronie https://account.jetbrains.com/login stworzyć sobie nowe konto używając emaila studenckiego (na pewno działają maile `@students.mimuw.edu.pl`).

Prace domowe i projekt zaliczeniowy możecie pisać w dowolnym programie (np VisualStudio, CodeBlocks, Notepad++), jednak ja polecam właśnie CLiona, zwłaszcza, że mamy na niego studencką licencję.

Pierwsza instalacja na swoim komputerze wymaga trochę pracy. Cały proces wygląda zgrubsza tak:

  1. Zakładamy konto na stronie JetBrains i odbieramy darmową licencję studencką.
  2. Pobieramy CLiona z tej strony link.
  3. Instalujemy go (można zostawić domyślne ustawienia).
  4. Logujemy się na swoje konto JetBrains w CLionie.

Teraz, żeby stworzyć nowy projekt musimy:

  1. Wybrać kafelek New Project.
  2. Wybrać z menu po lewej strone C Executable.
  3. Opcjonalnie zmienić nazwę folderu z untitled na coś sensownego.
  4. Kliknąć Create.
  5. Powinien nam się pojawić projekt oraz okno wyboru kompilatora - na Windowsie wystarczy nam domyślny MinGW (w laboratorium używaliśmy gcc, który nie jest dostępny na Windowsie). W tym oknie nie trzeba nic wybierać, wystarczy kliknąć OK.

Cały proces jest też opisany na stronie twórców CLiona - tutaj.

Z lewej strony mamy widok na wszystkie pliki projektu, natomiast na górnym pasku z prawej strony mamy dwie szczególnie ciekawe ikony - zielony guzik PLAY (trójkąt) i na prawo od niego zielonego robala DEBUG. Wciśnięcie guzika PLAY kompiluje i uruchamia nasz program, natomiast DEBUG kompiluje i uruchamia nasz program w trybie debugowania - o tym za jakiś czas.

Po wciśnięciu guzika PLAY na dole ekranu pojawia się terminal z od razu uruchomionym programem. Tutaj można wpisywać dane, które nasz program ma przeczytać. Po każdej wpisanej linii konieczne jest wciśnięcie ENTER, inaczej to, co napisaliśmy nie zostanie przekazane do programu.

Po dwukrotnym kliknięciu pliku z lewej strony, pojawia się on w oknie edycji - w centrum ekranu. Na tych zajęciach będziemy pisali jedynie programy składające się z jednego pliku źródłowego, więc całą treść kodu będzie można po prostu wklejać do pliku o nazwie main.c. W pliku CMakeLists.txt są przechowywane informacje dla kompilatora, na razie dla nas nieistotne.

Przykładowy program w C

Za przykład posłuży nam to, co napisaliśmy na labach. Żeby lepiej zrozumieć, co się tutaj dzieje, trzy uwagi na start:

  1. Komentarze w kodzie to takie fragmenty, które są pomijane przez kompilator - służą jedynie przekazaniu informacji pomiędzy osobami piszącymi i czytającymi kod. W C mamy komentarze wielolinijkowe, które zaczynają się /* i kończą się */ oraz komentarze jednolinijkowe (od danego miejsca do końca linii), które zaczynają się od //.
  2. Kompilator C "czyta" kod od góry do dołu i jeśli natrafi na jakąś nazwę, której nie zna, to oznajmi to błędem. Przykładowo:
int main() {
    f(); // w tym miejscu kompilator nie wie czym jest `f` - i zakończy kompilację z błędem
    return 0;
}

void f() {
    printf("Hello\n");
}

Prawidłowo ten kod wygląda tak:

void f() {
    printf("Hello\n");
}

int main() {
    f(); // kompilator poznał wcześniej definicję funkcji `f`, więc nie dostaniemy błędu
    return 0;
}

To tłumaczy, dlaczego #include oraz #define umieszczamy na samym początku pliku.

  1. Cały program w języku C to (z drobnymi wyjątkami) funkcje wymienione po kolei, z funkcją main na samym dole. Wykonanie programu zaczyna się od funkcji main.

Przydatne linki:

Sposób kompilacji w laboratorium:

  1. Zapisać program w danym folderze (koniecznie z rozszerzeniem .c)
  2. Otworzyć w tym folderze terminal (Kliknąć prawym przyciskiem myszy i wybrać "Otwórz terminal")
  3. Skompilować program gcc <nazwa pliku z kodem> -o <nazwa pliku docelowego>, np jeśli nasz plik z kodem nazywa się lab3.c, to kompilujemy gcc lab3.c -o lab3
  4. Uruchomić program ./<nazwa programu docelowego>, np ./lab3

Przykładowy program:

#include <stdio.h> // Standard Input Output - zawiera np definicje `scanf` oraz `printf`
#include <stdlib.h> // Standard Library - zawiera np `exit`

#define MAX_ARRAY_SIZE 5 // definiujemy maksymalny rozmiar tablicy - teraz gdybyśmy chcieli go zmienić, to wystarczy podmienić daną liczbę w jednym miejscu


// Ta funkcja to tzw "wrapper" - "owija" inną funkcję i daje jej bardziej czytelną postać.
int scan_number() {
    int x;
    scanf("%d", &x); // tutaj konieczny jest & - czyli "ampersand", on powoduje, że funkcja `scanf` będziez mogła zmodyfikować wartość zmiennej `x`.
    return x;
}

void print(int x) {
    printf("%d\n", x);
}

void name_easy() {
    char name[] = "Adam";
    int age = 5;
    printf("Hi, my name is %s, I am %d years old\n", name, age);
}

void name_hard() {
    char name[20]; // przyjmujemy, że nikt nie poda imienia dłuższego niż naście znaków
    int age;
    printf("Name: ");
    scanf("%s", name); // tablice domyślnie przekazujemy w postaci umożliwiającej edycję, więc tutaj nie piszemy `&`
    printf("Age: ");
    scanf("%d", &age);
    printf("Hi, my name is %s, I am %d years old\n", name, age);
}

void add_two_numbers() {
    int a = scan_number();
    int b = scan_number();
    print(a + b);
}

void execute_given_operator_on_two_numbers() {
    int a = scan_number();
    int b = scan_number();
    char token[10];
    scanf("%s", token); // `%s` wczytuje kolejne "słowo", tzn kolejny ciąg znaków oddzielony spacjami/enterami/tabulatorami
    // teoretycznie potrzebujemy tylko jednego znaku, ale ta metoda jest dość wygodna - chociaż można próbować np z `%c`

    int c;
    switch(token[0]) { // `switch` zastępuje ciąg if'ów
        case '+':
            c = a + b;
            break; // instrukcja `switch` jest fall-though tzn po wykonaniu danego `case` przechodzi do kolejnego - jeśli nie chcemy tego efektu, to każdy `case` trzeba kończyć `break`
        case '*':
            c = a * b;
            break;
        case '-':
            c = a - b;
            break;
        case '/':
            c = a / b;
            break;
        default:
            printf("Wrong instruction\n");
            exit(1); // kończymy działanie programu w tym momencie, zakończenie programu z kodem powrotu innym od 0 oznacza, że nastąpił jakiś błąd
    }
    print(c);
}

void sort(int array[], int size) {
    // tutaj przydałoby się wkleić kod do sortowania z drugich zajęć
    return;
}

void read_input_and_sort() {
    int array[MAX_ARRAY_SIZE];
    for (int i = 0; i < MAX_ARRAY_SIZE; i++) {
        array[i] = scan_number(); // wczytujemy do tablicy podane liczby
    }
    sort(array, MAX_ARRAY_SIZE); // sortujemy tablicę
    printf("\n\n");
    for (int i = 0; i < MAX_ARRAY_SIZE; i++) {
        print(array[i]); // wypisujemy posortowane liczby
    }
}

int main() {
    name_easy();
    name_hard();
    add_two_numbers();
    execute_given_operator_on_two_numbers();
    read_input_and_sort();
    return 0;
}