17-bo‘lim
Preprotsessor va ko'p faylli loyiha
define, makroslar va ularning tuzoqlari, shartli kompilyatsiya, sarlavha fayllari, include qo'riqchisi va Makefile.
Ushbu bo‘lim mundarijasi
- Preprotsessor nima qiladi?
- #define - oddiy almashtirish
- Makroslar - argumentli almashtirish
- Foydali makroslar
- Shartli kompilyatsiya
- Loyihani fayllarga bo'lish
- talaba.h - sarlavha fayli
- talaba.c - amalga oshirish
- main.c - dastur boshi
- Kompilyatsiya
- include qo'riqchisi
- Sarlavhaga nima yoziladi?
- static - faylga xos
- Makefile
- Loyiha tuzilishi
- Xulosa
Katta dasturni bitta faylda yozib bo'lmaydi. Bu bo'limda loyihani fayllarga bo'lishni o'rganamiz.
Preprotsessor nima qiladi? #
#define - oddiy almashtirish #
#include <stdio.h>
#define PI 3.14159
#define MAKS_TALABA 50
#define SALOM "Assalomu alaykum"
int main(void) {
printf("%s!\n", SALOM);
printf("PI = %.5f\n", PI);
printf("Maksimal talaba: %d\n", MAKS_TALABA);
printf("Aylana uzunligi: %.2f\n", 2 * PI * 5.0);
return 0;
}
Assalomu alaykum!
PI = 3.14159
Maksimal talaba: 50
Aylana uzunligi: 31.42
Makroslar - argumentli almashtirish #
#include <stdio.h>
#define KVADRAT(x) ((x) * (x))
#define MAKS(a, b) ((a) > (b) ? (a) : (b))
#define MIN(a, b) ((a) < (b) ? (a) : (b))
int main(void) {
printf("KVADRAT(5) = %d\n", KVADRAT(5));
printf("KVADRAT(2 + 3) = %d\n", KVADRAT(2 + 3));
printf("MAKS(10, 25) = %d\n", MAKS(10, 25));
printf("MIN(10, 25) = %d\n", MIN(10, 25));
return 0;
}
KVADRAT(5) = 25
KVADRAT(2 + 3) = 25
MAKS(10, 25) = 25
MIN(10, 25) = 10
#define KVADRAT_YOMON(x) x * x
KVADRAT_YOMON(2 + 3)
Preprotsessor buni matn sifatida almashtiradi:
2 + 3 * 2 + 3 → 2 + 6 + 3 = 11
25 emas, 11!
Shuning uchun makrosda har bir argumentni va butun ifodani qavsga oling:
#define KVADRAT(x) ((x) * (x))
#define KVADRAT(x) ((x) * (x))
int i = 5;
int n = KVADRAT(i++); /* ((i++) * (i++)) - i ikki marta oshadi! */
Bu aniqlanmagan xatti-harakat. Makros argumentni ikki marta hisoblaydi.
Zamonaviy C da bunday holatlarda makros o'rniga static inline
funksiya yozing:
static inline int kvadrat(int x) { return x * x; }
U turni tekshiradi, argumentni bir marta hisoblaydi va nosozliklarni tuzatishda ko'rinadi.
Foydali makroslar #
#include <stdio.h>
#define ELEMENTLAR(m) (sizeof(m) / sizeof((m)[0]))
#define TEKSHIR(shart) \
do { \
if (!(shart)) { \
fprintf(stderr, "Tekshiruv buzildi: %s (%s:%d)\n", \
#shart, __FILE__, __LINE__); \
} \
} while (0)
int main(void) {
int ballar[] = {85, 92, 78, 95, 88};
printf("Elementlar soni: %zu\n", ELEMENTLAR(ballar));
int yosh = 25;
TEKSHIR(yosh > 18);
TEKSHIR(yosh > 100);
return 0;
}
Elementlar soni: 5
Tekshiruv buzildi: yosh > 100 (makros.c:22)
Qator raqami sizning faylingizda boshqacha bo'ladi - __LINE__ aynan
shu qatorning raqamini beradi. Xabar stderr ga yozilgani uchun uni
alohida faylga yo'naltirish ham mumkin.
| Belgi | Nima qiladi |
|---|---|
\ | Makrosni keyingi qatorga davom ettiradi |
#x | Argumentni satrga aylantiradi |
a##b | Ikki nomni birlashtiradi |
__FILE__ | Joriy fayl nomi |
__LINE__ | Joriy qator raqami |
__DATE__ | Kompilyatsiya sanasi |
__func__ | Joriy funksiya nomi |
do { ... } while (0) naqshi ko'p qatorli makroslar uchun standart
yechim - u makrosni bitta buyruqdek tutadi va if ichida to'g'ri
ishlaydi.
Shartli kompilyatsiya #
#include <stdio.h>
#define NOSOZLIK 1
int main(void) {
#if NOSOZLIK
printf("[nosozlik] Dastur boshlandi\n");
#endif
printf("Asosiy ish bajarilmoqda\n");
#ifdef _WIN32
printf("Windows tizimi\n");
#elif defined(__linux__)
printf("Linux tizimi\n");
#elif defined(__APPLE__)
printf("macOS tizimi\n");
#else
printf("Noma'lum tizim\n");
#endif
#if NOSOZLIK
printf("[nosozlik] Dastur tugadi\n");
#endif
return 0;
}
[nosozlik] Dastur boshlandi
Asosiy ish bajarilmoqda
Linux tizimi
[nosozlik] Dastur tugadi
#ifdef NOSOZLIK
#define JURNAL(...) fprintf(stderr, __VA_ARGS__)
#else
#define JURNAL(...) ((void) 0)
#endif
JURNAL("Qiymat: %d\n", son);
Ishlab chiqishda:
gcc -DNOSOZLIK dastur.c -o dastur
Ishlab chiqarishda -DNOSOZLIK siz kompilyatsiya qilinsa, barcha
jurnal qatorlari butunlay yo'qoladi - hech qanday tezlik yo'qotilmaydi.
Loyihani fayllarga bo'lish #
talaba.h - sarlavha fayli #
#ifndef TALABA_H
#define TALABA_H
#define MAKS_ISM 50
typedef struct {
char ism[MAKS_ISM];
int yosh;
double baho;
} Talaba;
/* Yangi talaba yaratadi. */
Talaba talaba_yarat(const char *ism, int yosh, double baho);
/* Talabani ekranga chiqaradi. */
void talaba_chiqar(const Talaba *t);
/* Guruh o'rtacha bahosini hisoblaydi. Guruh bo'sh bo'lsa 0 qaytaradi. */
double guruh_ortachasi(const Talaba guruh[], int soni);
#endif /* TALABA_H */
talaba.c - amalga oshirish #
#include <stdio.h>
#include <string.h>
#include "talaba.h"
Talaba talaba_yarat(const char *ism, int yosh, double baho) {
Talaba yangi;
strncpy(yangi.ism, ism, MAKS_ISM - 1);
yangi.ism[MAKS_ISM - 1] = '\0';
yangi.yosh = yosh;
yangi.baho = baho;
return yangi;
}
void talaba_chiqar(const Talaba *t) {
printf("%-20s %3d %6.2f\n", t->ism, t->yosh, t->baho);
}
double guruh_ortachasi(const Talaba guruh[], int soni) {
if (soni <= 0) {
return 0.0;
}
double yigindi = 0.0;
for (int i = 0; i < soni; i++) {
yigindi += guruh[i].baho;
}
return yigindi / soni;
}
main.c - dastur boshi #
#include <stdio.h>
#include "talaba.h"
int main(void) {
Talaba guruh[3];
guruh[0] = talaba_yarat("Husanboy Qodirov", 25, 4.60);
guruh[1] = talaba_yarat("Malika Yusupova", 21, 4.90);
guruh[2] = talaba_yarat("Nodira Sobirova", 24, 4.30);
printf("%-20s %3s %6s\n", "Talaba", "Yosh", "Baho");
printf("-------------------------------\n");
for (int i = 0; i < 3; i++) {
talaba_chiqar(&guruh[i]);
}
printf("-------------------------------\n");
printf("O'rtacha: %.2f\n", guruh_ortachasi(guruh, 3));
return 0;
}
Kompilyatsiya #
gcc -std=c17 -Wall -Wextra -c talaba.c -o talaba.o
gcc -std=c17 -Wall -Wextra -c main.c -o main.o
gcc talaba.o main.o -o dastur
./dastur
Talaba Yosh Baho
-------------------------------
Husanboy Qodirov 25 4.60
Malika Yusupova 21 4.90
Nodira Sobirova 24 4.30
-------------------------------
O'rtacha: 4.60
Yoki bir buyruqda:
gcc -std=c17 -Wall -Wextra talaba.c main.c -o dastur
include qo'riqchisi #
#ifndef TALABA_H
#define TALABA_H
/* ... e'lonlar ... */
#endif
Ikkita fayl bir xil sarlavhani qo'shsa, uning mazmuni ikki marta kiritiladi:
error: redefinition of 'struct Talaba'
#ifndef qo'riqchisi buni to'xtatadi: birinchi kiritishda TALABA_H
aniqlanadi, ikkinchisida esa butun blok o'tkazib yuboriladi.
Har bir sarlavha faylida qo'riqchi bo'lishi shart.
Zamonaviy kompilyatorlar #pragma once ni ham qo'llab-quvvatlaydi -
u qisqaroq, lekin standart emas:
#pragma once
Sarlavhaga nima yoziladi? #
Sarlavhaga (.h) | Manbaga (.c) |
|---|---|
| Funksiya prototiplari | Funksiya tanasi |
typedef va struct e'lonlari | Mahalliy yordamchi funksiyalar |
#define doimiylari | static o'zgaruvchilar |
extern o'zgaruvchilar | Global o'zgaruvchi ta'rifi |
Sarlavhaga hech qachon quyidagilarni yozmang:
- funksiya tanasi (
static inlinedan tashqari); - global o'zgaruvchi ta'rifi (faqat
externe'loni); usingyoki keraksiz#includelar.
static - faylga xos #
/* talaba.c ichida */
static int hisoblagich = 0; /* faqat shu faylda ko'rinadi */
static void ichki_yordamchi(void) { /* faqat shu faylda chaqiriladi */
hisoblagich++;
}
static ni faol ishlatingSarlavhada e'lon qilinmagan har bir funksiya va global o'zgaruvchini
static qiling.
Foydasi:
- boshqa fayllar bilan nom to'qnashuvi bo'lmaydi;
- kompilyator ularni yaxshiroq optimallashtiradi;
- kod o'quvchisiga «bu faqat ichki» deb aytadi.
Makefile #
CC = gcc
CFLAGS = -std=c17 -Wall -Wextra -g
MAQSAD = dastur
MANBALAR = main.c talaba.c
OBYEKTLAR = $(MANBALAR:.c=.o)
all: $(MAQSAD)
$(MAQSAD): $(OBYEKTLAR)
$(CC) $(OBYEKTLAR) -o $@
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
main.o: main.c talaba.h
talaba.o: talaba.c talaba.h
tozala:
rm -f $(OBYEKTLAR) $(MAQSAD)
ishga: $(MAQSAD)
./$(MAQSAD)
.PHONY: all tozala ishga
make # yig'adi
make ishga # yig'adi va ishga tushiradi
make tozala # oraliq fayllarni o'chiradi
Buyruq qatorlari tabulyatsiya bilan boshlanishi kerak - probel bilan emas. Bu Makefile ning eng ko'p uchraydigan xatosi:
Makefile:8: *** missing separator. Stop.
Tahrirchingizda Makefile uchun tabni probelga almashtirishni o'chiring.
make nima uchun tez?make fayl sanalarini solishtiradi. Agar talaba.c o'zgarmagan bo'lsa,
talaba.o qayta yaratilmaydi.
Yuzlab fayldan iborat loyihada bitta faylni o'zgartirsangiz, faqat o'sha bittasi qayta kompilyatsiya qilinadi. Bu daqiqalarni soniyalarga aylantiradi.
Shuning uchun bog'liqliklarni to'g'ri yozish muhim:
main.o: main.c talaba.h
Bu qator «talaba.h o'zgarsa, main.o ham qayta yig'ilsin» degani.
Loyiha tuzilishi #
loyiha/
├── Makefile
├── src/
│ ├── main.c
│ ├── talaba.c
│ └── hisobot.c
├── include/
│ ├── talaba.h
│ └── hisobot.h
├── tests/
│ └── talaba_test.c
└── build/
└── (yig'ilgan fayllar)
gcc -std=c17 -Wall -Iinclude src/*.c -o build/dastur
-Iinclude bayrog'i kompilyatorga sarlavhalarni qayerdan qidirishni
aytadi.
#definebilan bir nechta doimiy yarating.KVADRATmakrosini qavssiz yozib,KVADRAT(2+3)natijasini ko'ring.- Qavs qo'shib, natija to'g'rilanishini tasdiqlang.
ELEMENTLARmakrosini yozib, massivda sinang.__FILE__va__LINE__ishlatuvchi jurnal makrosi yozing.#ifdefbilan nosozlik xabarlarini yoqing va o'chiring.- Loyihani
talaba.h,talaba.cvamain.cga bo'ling. includeqo'riqchisini olib tashlab, xatoni ko'ring.- Makefile yozing va
make,make tozalani sinang. - Bitta faylni o'zgartirib,
makefaqat o'shani qayta yig'ishini kuzating.
Xulosa #
- Preprotsessor matn bilan ishlaydi - u C tilini bilmaydi.
#defineoddiy almashtirish qiladi; makrosda har bir argumentni qavsga oling.- Makros argumentni bir necha marta hisoblashi mumkin - yon ta'sirli ifoda bermang.
- Zamonaviy C da murakkab makros o'rniga
static inlinefunksiya afzal. #ifdefbilan shartli kompilyatsiya - platformaga qarab kod tanlash.- Loyiha
.h(e'lonlar) va.c(amalga oshirish) fayllariga bo'linadi. - Har bir sarlavhada
#ifndefqo'riqchisi bo'lishi shart. - Faqat ichki funksiyalarni
staticqiling. - Makefile faqat o'zgargan fayllarni qayta yig'adi.
- Makefile da buyruqlar tabulyatsiya bilan boshlanadi.
Keyingi bo'limda standart kutubxonaning eng foydali qismlarini ko'ramiz.
O‘qish tarixini saqlamoqchimisiz?
Tizimga kirsangiz, tugatgan bo‘limlaringiz saqlanadi va qoldirgan joyingizdan davom etasiz.
Xatolik topdingizmi?
Imlo xatosi, ishlamaydigan kod yoki noto‘g‘ri ma‘lumotni ko‘rsangiz - bizga xabar bering. Har bir xabar administrator tomonidan ko‘rib chiqiladi.