• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

Modularyzacja programu, przekazanie struktury przez wskaźnik do pliku zasobów

VPS Starter Arubacloud
0 głosów
550 wizyt
pytanie zadane 25 maja 2019 w C i C++ przez Białozór Użytkownik (750 p.)

Próbowałam dokonać tego na trzy sposoby 1) poprzez udostępnienie struktury oraz potrzebnych danych przy użyciu extern 2) Poprzez wskaźniki (poniżej) 3) poprzez włączenie do pliku zasobów biblioteki prowadzącej do pliku głównego. Na czym polega błąd? 

Plik źródłowy:

//_CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>

struct Osoba {
	char imie[20];
	char nazwisko[20];
	char rokurodzenia[10];
	char numerkonta[27]; // +1
	char nazwabanku[20];
	char kwota[20];
};
 struct Osoba Tablicasob[10] = {
	{ "Karol","Kot","1998","31415926535897932384626433","Bank Bank","3248" },
	{ "Karol","Kotlicki","1998","31415926535897932384626434","Bank Bank","113246" },
	{ "Karol","Koterski","1998","31415926535897932384626445","Bank Bank","13247" },
	{ "Karol","Kotek","1998","31415926535897932384626436","Bank Bank","1348" },
	{ "Karol","Kociaż","1998","31415926535897932384626437","Bank Bank","13248" },
	{ "Karol","Kociarski","1998","31415926535897932384626438","Bank Bank","13248" },
	{ "Karol","Koci","1998","31415926535897932384626439","Bank Bank","13148" },
	{ "Karol","Pies","1998","31415926535897932384626431","Bank Bank","248" },
};

struct Osoba* wskOsoba;
int liczba_osob = 8;
liczba_osob* wsklos;
int main(void) {
	while (1) {

		char wybor = 0;

		struct Osoba* wskOsoba;
		//wskOsoba = &TablicaOsob;
		
		printf("wpisz 1    Jeśli chcesz wyświetlić pełną listę kont \n");
		printf("wpisz 6    jesli chcesz wyjść z programu \n");
		wybor = 0;
		wybor = getchar();
		getchar();
		switch (wybor) {
		case '1':
			wyswietl();
			break;

		case '6':
             return 0;

		}
	}
}

Plik zasobów:

#include "funkcje.h"
//#include "FunckjeModularyzacja2.c"

void wyswietl() {
	wskOsoba = &Osoba;
    liczba_osob =&liczba_osob; 
	printf("W tablicy struktur ListaOsoba typu Osoba  mamy nastepujace dane:\n");
	
	for (int i = 0; i < liczba_osob; i++) {
		printf("TablicaOsob[%d]\n", i);
		printf("imie: %s,   nazwisko: %s\n, rokurodzenia: %s\n, numerkonta: %s\n,   nazwabanku: %s\n,   kwota: %s\n \n", wskOsoba->imie, wskOsoba->nazwisko, wskOsoba->rokurodzenia, wskOsoba->numerkonta, wskOsoba->nazwabanku, wskOsoba->kwota);
		wskOsoba++;

	}
}

Plik nagłówkowy:

#ifndef funkcje_h
#define funkcje_h

void wyswietl();

#endif // !funkcje_h


Uwaga: w dalszej części programu zmieniana jest wartość liczby osób, dlatego posiada osobny wskaźnik.

Skoro kompilator pokazuje serię błędów dla: liczba osób, osoba, wskosoba jako niezadeklarowany identyfikator domyślam się że niepoprawnie napisałam wskaźniki.

1 odpowiedź

+1 głos
odpowiedź 25 maja 2019 przez Patrycjerz Mędrzec (192,340 p.)
wybrane 8 czerwca 2019 przez Białozór
 
Najlepsza

Dlaczego chcesz używać linkowania zewnętrznego zasobów? Nie ma to większego sensu, gdyż niepotrzebnie uzależniasz jedne moduły od drugich, tracąc na ich uniwersalności. Wg mnie lepiej przesyłać dane poprzez parametry funkcji. Jedynym kosztem tego sposobu jest te parę bajtów potrzebnych do przesłania jako argumenty, a zyskujesz znacznie więcej.

PS: Do elementów tablicy odwołujemy się za pomocą operatora tablicowego []. Nie ma sensu inkrementować wskaźnika, gdyż później będzie go trudno użyć ponownie (powrót do stanu pierwotnego).

Kod mógłby wyglądać mniej więcej tak:

//_CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>

#include "osoba.h"

struct Osoba Tablicasob[10] = {
    { "Karol","Kot","1998","31415926535897932384626433","Bank Bank","3248" },
    { "Karol","Kotlicki","1998","31415926535897932384626434","Bank Bank","113246" },
    { "Karol","Koterski","1998","31415926535897932384626445","Bank Bank","13247" },
    { "Karol","Kotek","1998","31415926535897932384626436","Bank Bank","1348" },
    { "Karol","Kociaż","1998","31415926535897932384626437","Bank Bank","13248" },
    { "Karol","Kociarski","1998","31415926535897932384626438","Bank Bank","13248" },
    { "Karol","Koci","1998","31415926535897932384626439","Bank Bank","13148" },
    { "Karol","Pies","1998","31415926535897932384626431","Bank Bank","248" },
};
 

int main(void) {
    int liczba_osob = 8;
    while (1) {
        char wybor = 0;
        printf("wpisz 1    Jeśli chcesz wyświetlić pełną listę kont \n");
        printf("wpisz 6    jesli chcesz wyjść z programu \n");
        wybor = getchar();
        getchar();
        switch (wybor) {
        case '1':
            wyswietl(Tablicasob, liczba_osob);
            break;
 
        case '6':
            return 0;
 
        }
    }
}
#ifndef OSOBA_H
#define OSOBA_H

struct Osoba {
    char imie[20];
    char nazwisko[20];
    char rokurodzenia[10];
    char numerkonta[27]; // +1
    char nazwabanku[20];
    char kwota[20];
};

void wyswietl(const struct Osoba* osoby, const int liczba_osob);

#endif
#include "osoba.h"

void wyswietl(const struct Osoba* osoby, const int liczba_osob) { 
    printf("W tablicy struktur ListaOsoba typu Osoba  mamy nastepujace dane:\n");
     
    for (int i = 0; i < liczba_osob; i++) {
        printf("TablicaOsob[%d]\n", i);
        printf("imie: %s,   nazwisko: %s\n, rokurodzenia: %s\n, numerkonta: %s\n,   nazwabanku: %s\n,   kwota: %s\n \n", osoby[i].imie, osoby[i].nazwisko, osoby[i].rokurodzenia, osoby[i].numerkonta, osoby[i].nazwabanku, osoby[i].kwota);
    }
}
komentarz 25 maja 2019 przez Białozór Użytkownik (750 p.)

1 Czy linkowanie zewnętrzne polega na tworzeniu funkcji pobierającej dane bez pośrednictwa?

2 "a zyskujesz znacznie więcej" Czy chodzi o łatwość w pisaniu programu?

3 Dlaczego struct Osoba jest w pliku osoba.h? Czy chodzi o to by nie było konieczne tworzenie dodatkowych wskaźników?

void wyswietl(const struct Osoba* osoby, const int liczba_osob)

4 dlaczego wykorzystane zostało const? Uniemożliwia to zmianę struktury, lecz dlaczego byłoby to ryzykowne? (w dalszej części programu dodaję nowe osoby)

5 Konieczne jest odwołanie się do osoby poprzez Osoba* by dostać się do danych z innego pliku. Jak to jest to możliwe skoro nie zadeklarowany został wcześniej wskaźnik? (pytanie powiązane z punktem 3)

komentarz 25 maja 2019 przez Patrycjerz Mędrzec (192,340 p.)
  1. Mógłbyś wytłumaczyć zwrot pobieranie danych bez pośrednictwa? Linkowaniem zewnętrznym nazwałem możliwość używania zasobu (zmiennej lub stałej), zdefiniowanego w pewnej jednostce translacji (pliku źródłowym .c po użyciu preprocesora), w innych jednostkach (modułach). W języku C używa się do tego słowa kluczowego extern. Przykładowo, tworzysz zmienną w plik1.c, a odwołujesz się do niej bezpośrednio (bez jakichkolwiek wskaźników) w plik2.c. Przeczytasz o tym tutaj.
  2. Tak jak napisałem wcześniej, zyskujesz dzięki temu integralność i niezależność modułu. Nie będzie musiał on być linkowany z innymi, aby uzyskać dostęp do potrzebnych mu zasobów. Wyobraź sobie, że tworzysz bibliotekę programistyczną. Gdyby działała ona na zasadzie linkowania zewnętrznego z kodem użytkownika, to ten użytkownik musiałby definiować globalne zasoby, które wykorzystywałaby twoja biblioteka i skazany byłby na linkowanie statyczne twoich plików binarnych. W przypadku używania funkcji z parametrami, użytkownik ma wolną rękę w pisaniu kodu, korzysta z czytelnego interfejsu oraz ma możliwość użycia innej biblioteki, bez większej ingerencji w swoim kodzie. Oczywiście, linkowanie zewnętrzne nie jest bezużyteczne, ale nie znam realnego przykładu, w którym mogłoby być ono wykorzystywane.
  3. Plik nagłówkowy osoba.h został stworzony w celu zamknięcia definicji struktury oraz deklaracji funkcji w jednym miejscu, dzięki czemu nie jest wymagane tworzenie ich w każdej jednostce translacji.
  4. Słowo kluczowe const zostało dodane w celu zabezpieczenia parametrów przed modyfikacją ich w funkcji. Ogranicza to niezamierzone błędy logiki kodu oraz informuje użytkownika funkcji, że dane przesyłane przez niego w argumentach nie zostaną zmienione.
  5. Wybacz, ale nie rozumiem tego pytania. Spróbuj inaczej opisać swój problem.
komentarz 25 maja 2019 przez niezalogowany

tak chciałem się z ciekawości podpiąć pod temat.

#ifndef funkcje_h
#define funkcje_h
void wyswietl();
extern Osoba* wskOsoba; 
extern int liczba_osob;
extern int * wsklos;
#endif // !funkcje_h

nie widzi typu osoba, nie pomaga deklaracja struct Osoba, ani tym bardziej extern struct osoba; Ja wiem że w main powinno być jak najmniej.

Ale czy nie da się zdeklarować klasy w main i przekazać do pliku nagłówkowego?

komentarz 26 maja 2019 przez Białozór Użytkownik (750 p.)

@Patrycjerz,

Wytłumaczę dokładnie na czym polegają pytania, wolałabym jednak najpierw skupić się na uruchomieniu programu (jeśli pytania nie dotyczą bezpośrednio problemów z tą czynnością).

Napisałam dwa warianty programu funkcje.c. program wygląda obecnie tak:

plik źródłowy

//_CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>

#include "funkcje.h"

struct Osoba Tablicasob[10] = {
	{ "Karol","Kot","1998","31415926535897932384626433","Bank Bank","3248" },
	{ "Karol","Kotlicki","1998","31415926535897932384626434","Bank Bank","113246" },
	{ "Karol","Koterski","1998","31415926535897932384626445","Bank Bank","13247" },
	{ "Karol","Kotek","1998","31415926535897932384626436","Bank Bank","1348" },
	{ "Karol","Kociaż","1998","31415926535897932384626437","Bank Bank","13248" },
	{ "Karol","Kociarski","1998","31415926535897932384626438","Bank Bank","13248" },
	{ "Karol","Koci","1998","31415926535897932384626439","Bank Bank","13148" },
	{ "Karol","Pies","1998","31415926535897932384626431","Bank Bank","248" },
};


int main(void) {
	int liczba_osob = 8;

	int najwieksza;
	int najmniejsza;
	int adr1;
	int adr2;
	int nosnik1;
	int nosnik2;

	while (1) {
		char wybor = 0;
		printf("wpisz 1    Jeśli chcesz wyświetlić pełną listę kont \n");
		printf("wpisz 6    jesli chcesz wyjść z programu \n");
		wybor = getchar();
		getchar();
		switch (wybor) {
		case '1':
			wyswietl(Tablicasob, liczba_osob);
			break;
		case '2':
			najwi(Tablicasob, Tablicasob, liczba_osob, najwieksza, najmniejsza, adr1, adr2, nosnik1, nosnik2);

			break;

		case '6':
			return 0;

		}
	}
}

Plik zasobów wariant 1: ( ten chciałabym uruchomić, pojawia się jednak błędy również dla imie i nazwisko " ->kwota" musi wskazywać na struct/union. W deklaracji void znajduje się: 

const struct Osoba* Tablicasob, const struct Osoba* osoby

Poniżej zadeklarowałam:

int* wskOsoba; 

Dlaczego nie sprawia to by program traktował wskOsoba jako wskaźnik?

#include "funkcje.h"


void wyswietl(const struct Osoba* osoby, const int liczba_osob) {
	printf("W tablicy struktur ListaOsoba typu Osoba  mamy nastepujace dane:\n");

	for (int i = 0; i < liczba_osob; i++) {
		printf("TablicaOsob[%d]\n", i);
		printf("imie: %s,   nazwisko: %s\n, rokurodzenia: %s\n, numerkonta: %s\n,   nazwabanku: %s\n,   kwota: %s\n \n", osoby[i].imie, osoby[i].nazwisko, osoby[i].rokurodzenia, osoby[i].numerkonta, osoby[i].nazwabanku, osoby[i].kwota);
	}
}


void najwi(const struct Osoba* Tablicasob, const struct Osoba* osoby, const int liczba_osob, int najwieksza, int najmniejsza, int adr1, int adr2, int nosnik1, int nosnik2) {

	int* wskOsoba;
	wskOsoba = &Tablicasob[0];
	najwieksza = atoi(wskOsoba->kwota);//wskOsoba->kwota

	for (int i = 1; i < liczba_osob; i++) { // 2 

		wskOsoba = &Tablicasob[i];
		if (atoi(wskOsoba->kwota) > najwieksza) {
			najwieksza = atoi(wskOsoba->kwota);
			nosnik1 = i;
		}
		adr1 = i;

	}
	wskOsoba = &Tablicasob[nosnik1];
	printf("najwieksza: %i\n \n", najwieksza);
	printf("imie: %s,   nazwisko: %s\n", wskOsoba->imie, wskOsoba->nazwisko);

	wskOsoba = Tablicasob;
	najmniejsza = atoi(wskOsoba->kwota);

	for (int i = 1; i < liczba_osob; i++) { // 2
		wskOsoba = &Tablicasob[i];
		if (atoi(wskOsoba->kwota) < najmniejsza) {
			najmniejsza = atoi(wskOsoba->kwota);
			nosnik2 = i;
		}
		adr2 = i;
	}
	wskOsoba = &Tablicasob[nosnik2];
	printf("najmniejsza: %i\n \n", najmniejsza);
	printf("imie: %s,   nazwisko: %s\n", wskOsoba->imie, wskOsoba->nazwisko);
}

 

Plik zasobów: wariant 2 (alternatywne podeście do problemu) Tutaj  pojawia się błąd CL.exse zakończone przez kod 2. Błąd ten odpowiada za uruchamianie nieodpowiedniej wersji sdk. Jednak w związku z tym że pojawił się dopiero po zamianie wszystkich wskaźników na odwołania bezpośrednio do tablicy. Podejrzewam że z jakiegoś powodu sama zamiana jest błędna, nie rozumiem jednak dlaczego.

#include "funkcje.h"


void wyswietl(const struct Osoba* osoby, const int liczba_osob) {
	printf("W tablicy struktur ListaOsoba typu Osoba  mamy nastepujace dane:\n");

	for (int i = 0; i < liczba_osob; i++) {
		printf("TablicaOsob[%d]\n", i);
		printf("imie: %s,   nazwisko: %s\n, rokurodzenia: %s\n, numerkonta: %s\n,   nazwabanku: %s\n,   kwota: %s\n \n", osoby[i].imie, osoby[i].nazwisko, osoby[i].rokurodzenia, osoby[i].numerkonta, osoby[i].nazwabanku, osoby[i].kwota);
	}
}

void najwi(const struct Osoba* Tablicasob, const struct Osoba* osoby, const int liczba_osob, int najwieksza, int najmniejsza, int adr1, int adr2, int nosnik1, int nosnik2) {

	int* wskOsoba;
	wskOsoba = &Tablicasob[0];
	najwieksza = atoi(Tablicasob[0].kwota);//wskOsoba->kwota

	for (int i = 1; i < liczba_osob; i++) { // 2 

		wskOsoba = &Tablicasob[i];
		if (atoi(Tablicasob[i].kwota) > najwieksza) {
			najwieksza = atoi(Tablicasob[i].kwota);
			nosnik1 = i;
		}
		adr1 = i;

	}
	wskOsoba = &Tablicasob[nosnik1];
	printf("najwieksza: %i\n \n", najwieksza);
	printf("imie: %s,   nazwisko: %s\n", Tablicasob[nosnik1].imie, Tablicasob[nosnik1].nazwisko);

	wskOsoba = Tablicasob;
	najmniejsza = atoi(Tablicasob[0].kwota);

	for (int i = 1; i < liczba_osob; i++) { // 2
		wskOsoba = &Tablicasob[i];
		if (atoi(Tablicasob[i].kwota) < najmniejsza) {
			najmniejsza = atoi(Tablicasob[i].kwota);
			nosnik2 = i;
		}
		adr2 = i;
	}
	wskOsoba = &Tablicasob[nosnik2];
	printf("najmniejsza: %i\n \n", najmniejsza);
	printf("imie: %s,   nazwisko: %s\n", Tablicasob[nosnik2].imie, Tablicasob[nosnik2].nazwisko);
}

plik nagłówkowy:

#ifndef FUNKCJE_H
#define FUNKCJE_H

struct Osoba {
	char imie[20];
	char nazwisko[20];
	char rokurodzenia[10];
	char numerkonta[27]; // +1
	char nazwabanku[20];
	char kwota[20];
};

void wyswietl(const struct Osoba* osoby, const int liczba_osob);
void najwi(const struct Osoba* Tablicasob, const struct Osoba* osoby, const int liczba_osob, int najwieksza, int najmniejsza, int adr1, int adr2, int nosnik1, int nosnik2);

#endif


 

komentarz 27 maja 2019 przez Patrycjerz Mędrzec (192,340 p.)
  1. int najwieksza;
    int najmniejsza;
    int adr1;
    int adr2;
    int nosnik1;
    int nosnik2;
    // ...
    case '2':
                najwi(Tablicasob, Tablicasob, liczba_osob, najwieksza, najmniejsza, adr1, adr2, nosnik1, nosnik2);
     
                break;
    Dlaczego przesyłasz do funkcji `najwi` niezainicjowane zmienne? Parametry funkcji służą do przekazywania jej danych, w tym przypadku przesyłasz jej "śmieci" (chociaż w kompilatorze GCC zazwyczaj zmienne lokalne są inicjowane zerem, ale i tak jest to spory błąd). Domyślam się, że chcesz coś przekazać na zewnątrz funkcji. W takim przypadku użyj wskaźników, tak jak np. tutaj:
    // wywołanie
    int i, j, k;
    funkcja(&i, &j, &k);
    printf("%d %d %d", i, j, k); // "1 2 3"
    
    // definicja
    void funkcja(int* i, int* j, int* k)
    {
        *i = 1;
        *j = 2;
        *k = 3;
    }
    
  2. int* wskOsoba;
    wskOsoba = &Tablicasob[0];
    najwieksza = atoi(wskOsoba->kwota);//wskOsoba->kwota
    Dlaczego przypisujesz adres elementu tablicy struktur do wskaźnika na typ int? Chyba czegoś nie rozumiesz w idei wskaźników. W tym przypadku wystarczyłoby normalne odwołanie się do elementu `kwota` z nazwy tablicy, tak jak poniżej:
    #include "funkcje.h"
     
     
    void wyswietl(const struct Osoba* osoby, const int liczba_osob) {
        printf("W tablicy struktur ListaOsoba typu Osoba  mamy nastepujace dane:\n");
     
        for (int i = 0; i < liczba_osob; i++) {
            printf("TablicaOsob[%d]\n", i);
            printf("imie: %s,   nazwisko: %s\n, rokurodzenia: %s\n, numerkonta: %s\n,   nazwabanku: %s\n,   kwota: %s\n \n", osoby[i].imie, osoby[i].nazwisko, osoby[i].rokurodzenia, osoby[i].numerkonta, osoby[i].nazwabanku, osoby[i].kwota);
        }
    }
     
     
    void najwi(const struct Osoba* Tablicasob, const int liczba_osob) {
     
        int najwieksza = atoi(Tablicasob[0].kwota);
        int najw_index = 0;
        for (int i = 1; i < liczba_osob; i++) { // 2 
            if (atoi(Tablicasob[i].kwota) > najwieksza) {
                najwieksza = atoi(Tablicasob[i].kwota);
                najw_index = i;
            }
        }
        printf("najwieksza: %i\n \n", najwieksza);
        printf("imie: %s,   nazwisko: %s\n", Tablicasob[najw_index].imie, Tablicasob[najw_index].nazwisko);
     
        struct Osoba* wskOsoba = Tablicasob; // to w celach demonstracyjnych, równie dobrze mógłbyś nadal używać Tablicasob
        int najmniejsza = atoi(wskOsoba->kwota);
        int najm_index = 0;
        for (int i = 1; i < liczba_osob; i++) { // 2
            wskOsoba = &Tablicasob[i]; // lub wskOsoba = Tablicasob + i, chociaż jest to nieeleganckie rozwiązanie
            if (atoi(wskOsoba->kwota) < najmniejsza) {
                najmniejsza = atoi(wskOsoba->kwota);
                najm_index = i;
            }
        }
        wskOsoba = &Tablicasob[najm_index];
        printf("najmniejsza: %i\n \n", najmniejsza);
        printf("imie: %s,   nazwisko: %s\n", wskOsoba->imie, wskOsoba->nazwisko);
    }
    
    
  3. W drugim wariancie nie wiem, czemu pojawia się taki błąd, ale nadal bezsensowne jest przypisywanie adresu elementów tablicy do wskaźnika na int. Z kolei, nie inicjujesz zmiennych `nosnik1` i `nosnik2`. Gdyby pierwsza osoba miała największą i najmniejszą kwotę, to program może się wysypać z powodu nieokreślonego stanu tych zmiennych (tak jak wspomniałem wcześniej, w GCC może to działać).

Nie wiem, czy masz jeszcze jakieś wątpliwości. Pamiętaj, że w C nie przesyłamy zmiennych przez referencję, tak jak w Javie. W celu ingerencji w obszar pamięci inny niż ten zdefiniowany w zmiennej, należy używać wskaźników. 

Podobne pytania

0 głosów
2 odpowiedzi 122 wizyt
pytanie zadane 23 listopada 2020 w C i C++ przez dnaiel Początkujący (400 p.)
0 głosów
3 odpowiedzi 452 wizyt
pytanie zadane 27 kwietnia 2019 w C i C++ przez miodowy Nowicjusz (120 p.)
0 głosów
3 odpowiedzi 234 wizyt
pytanie zadane 24 października 2019 w C i C++ przez magda_19 Gaduła (3,080 p.)

92,417 zapytań

141,222 odpowiedzi

318,984 komentarzy

61,831 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj.

Akademia Sekuraka

Akademia Sekuraka 2024 zapewnia dostęp do minimum 15 szkoleń online z bezpieczeństwa IT oraz dostęp także do materiałów z edycji Sekurak Academy z roku 2023!

Przy zakupie możecie skorzystać z kodu: pasja-akademia - użyjcie go w koszyku, a uzyskacie rabat -30% na bilety w wersji "Standard"! Więcej informacji na temat akademii 2024 znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...