Plik źródłowy a nagłówkowy

0

Mam problem, czytałem książkę jak i przeglądałem wiele stron. Nie wiem nie jest to dla mnie nadal jasne. W pliku nagłówkowym z rozszerzeniem .h powinny się znaleźć deklaracje zmiennych i funkcji oraz wszystkie dyrektywy które są nam potrzebne a w źródłowym wszystkie definicje (tzn. cało funkcji) ? Jak użyć przy tym operatora "extern" aby pobrać deklaracje zmiennej z innego modułu, skoro dołączamy plik nagłówkowy do pliku żródłowego to ma już przecież wszystko jest. Na jak to dokładnie działa ?

4

Generalnie odradzam zmiennych globalnych. Ale jeśli już na prawdę masz uzasadnione użycie, to wtedy w pliku .h robisz extern, a w jakimś jednym pliku .c robisz definicje tej zmiennej, np.
.h:

extern int g_variable;

.c:

int g_variable;

Co umieszczać w nagłówkach?

  • możliwie jak najmniej includeów własnych nagłówków
  • symbole preprocesora
  • forward declarations i prototypy funkcji
  • definicje struktur i enumów, które są współdzielone między wieloma plikami .c
  • funkcje inline
  • wszelkie stałe
7
Frreq napisał(a):

Jak użyć przy tym operatora "extern" aby pobrać deklaracje zmiennej z innego modułu, skoro dołączamy plik nagłówkowy do pliku żródłowego to ma już przecież wszystko jest. Na jak to dokładnie działa ?

Gdybyś w pliku nagłówkowym nie używał extern to znaczy że definiujesz zmienną w pliku nagłówkowym. Potem jeśli 2 pliki .c dołączają ten plik nagłówkowy to każdy z nich będzie miał swoją kopię tej zmiennej. Co prowadzi do błędu linkera, bo istnieją 2 zmienne globalne o tej samej nazwie.

Teraz użycie extern w pliku nagłówkowym jedynie deklaruje taką zmienną, czyli mówi "ta zmienna gdzieś istnieje, ja się tym nie przejmuję ale linker będzie wiedział". Teraz oczywiście jeden z plików .c musi definiować taką zmienną, inaczej będzie błąd bo próbujesz używać zmiennej, która nie istnieje. Wszystkie inne pliki .c które dołączają nagłówek z extern będą używać właśnej tej jednej zmiennej.

Podobna sytuacja z deklaracją/definicją jest z funkcjami, ale różnica jest taka, że deklaracja funkcji nie potrzebuje słowa extern, po prostu brak ciała oznacza że to jest deklaracja.

0

Dzięki wielkie, rozjaśniło mi to trochę pogląd na podział programu na pliki i używanie głównie operatora extern. Poszukam jeszcze więcej informacji na ten temat jak to zoptymalizować.

0

Czyli jeżeli dobrze zrozumiałem powinno to wyglądać mniej więcej tak :

plik main.c

#include "Head.h"
#include "dodawanie.c"
#include "odejmowanie.c"
#include "mnozenie.c"


int main()
{
    int number1;
    int number2;
    int hnumber = 0;
    int option = 0;
    while(1)
    {
        printf("XXXXXXXX WITAJ W KALKULATORZE XXXXXXXX\n\n");
        printf("wybierz opcje : \n\n");

        printf("1. DODAWANIE\n");
        printf("2. ODEJMOWANIE\n");
        printf("3. MNOZENIE\n");
        printf("4. POTENGOWANIE\n");
        scanf("%d",&option);

        system("CLS");

        switch(option)
            {
            case 1: printf("DODAWANIE");
                    system("CLS");
                    printf("Podaj wartosc a : ");
                    scanf("%d",&number1);
                    printf("\nPodaj wartosc b : ");
                    scanf("%d",&number2);
                    hnumber = dodawanie(number1,number2);
                    printf("Wynik = %d",hnumber);
                    getch();
                    system("CLS");
                    break;
            case 2: printf("ODEJMOWANIE");
                    system("CLS");
                    printf("Podaj wartosc a : ");
                    scanf("%d",&number1);
                    printf("\nPodaj wartosc b : ");
                    scanf("%d",&number2);
                    hnumber = odejmowanie(number1,number2);
                    printf("Wynik odejmowania = %d", hnumber);
                    getch();
                    system("CLS");
                    break;
            /*case 3: printf("MNOZENIE");
                    system("CLS");
                    mnozenie();
                    break;
            case 4: printf("POTEGOWANIE");
                    system("CLS");
                    potengowanie();
                    break; */
            default : printf("Wybrales nie prawidolowa wartosc");
                      printf("Wcisnij dowolny klawisz aby wrocic");
                      getch();
                      system("CLS");
                      break;
            }
    }

    return 0;
}

Plik Head.h

#ifndef HEAD_H_INCLUDED
#define HEAD_H_INCLUDED

#include <stdio.h>
#include <conio.h>
#include <math.h>
#include <time.h>
#include <stdlib.h>

extern int number1,number2,hnumber,suma,option;

int dodawanie();
int odejmowanie();
float mnozenie();
float potengowanie();


#endif // HEAD_H_INCLUDED

plik dodawanie.c

#ifndef HEAD_H_INCLUDED
#define HEAD_H_INCLUDED


int dodawanie(int number1, int number2)
{

    int suma = 0;

    suma = number1+number2;

    return suma;

}

#endif

itd.

Czy można coś zoptymalizować w kodzie ? Miał to być prosty kalkulator

1

Po pierwsze się nie dołącza plików .c

#include "dodawanie.c"

To jest ogólna zasada. Z tego powodu plik .c nie powinien mieć strażników dołączania (autorskie tłumaczenie).
Natomiast w tym przypadku wystarczy, że dołączasz plik nagłówkowy i on ma tam deklaracje tych zmiennych globalnych.

Po drugie nie używaj zmiennych globalnych to nie będziesz musiał się bawić externami. Zmienne globalne są złe z wielu powodów, więc unikanie ich to dobra praktyka tak czy siak.

Po trzecie ten konkretny przykład jest na tyle banalny, że nie ma sensu go rozdzielić na osobne pliki.

0

Przykład wybrałem taki, ponieważ chciałem sprawdzić działanie. Lubie sprawdzić jak coś działa na przykładnie, łatwiej jest mi zapamiętać później jak wykorzystać dany element.

To już zrozumiałem, kompilator w takim razie dołącza wszystkie pliki źródłowe z automatu, a plik nagłówkowy w takim razie należy dołączyć ręcznie do każdego z plików źródłowych czy tylko to pliku głównego ?

1

Kompilator nie dołącza plików źródłowych, tylko je kompiluje, i to też nie automagicznie ale dlatego że masz te pliki dołączone do projektu.

A plik nagłówkowy dołączasz wtedy gdy potrzebujesz. Czyli gdy potrzebujesz którejś deklaracji w tym pliku. Inaczej mówiąc mógłbyś się obejść zupełnie bez pliku nagłówkowego, wystarczy że masz deklaracje rzeczy których używasz. Ale jeśli w kilku plikach .c potrzebujesz tych samych deklaracji to wygodniej jest je wrzucić do pliku nagłówkowego i zrobić jeden #include.

1 użytkowników online, w tym zalogowanych: 0, gości: 1