Ce face #embed
Uneori un program are nevoie de date dintr-un fișier: o imagine, un font, un text implicit. Înainte de C23 se folosea un utilitar extern (xxd -i) care transforma fișierul într-un tablou C. #embed "fisier" face asta direct: preprocesorul înlocuiește linia cu lista octeților din fișier, separați prin virgulă, exact cum ar apărea într-un inițializator de tablou.
Fișierul este căutat ca la #include (cu ghilimele, întâi lângă fișierul sursă). Parametrii opționali: limit(n) ia cel mult n octeți, prefix(...) și suffix(...) adaugă elemente înainte și după (de exemplu un 0 final, ca să obții un șir C), iar if_empty(...) dă conținutul folosit când fișierul este gol.
__has_embed("fisier") spune dacă fișierul există: dă __STDC_EMBED_FOUND__, __STDC_EMBED_EMPTY__ sau __STDC_EMBED_NOT_FOUND__. Clang suportă #embed din versiunea 19, GCC din versiunea 15.
Parametri
| Parametru | Descriere |
|---|---|
limit(n) |
Cel mult n octeți din fișier. |
prefix(...) / suffix(...) |
Elemente puse înainte, respectiv după octeții fișierului (doar dacă fișierul nu este gol). |
if_empty(...) |
Ce se pune în loc când fișierul este gol. |
Exemple
Exemplul 1: Programul își citește propriul cod
Exemplul este salvat ca exemplu.c, deci se poate include pe sine. limit(18) ia primii 18 octeți, adică textul primei linii, iar suffix(, 0) adaugă terminatorul de șir.
#include <stdio.h>
static const char inceput[] = {
#embed "exemplu.c" limit(18) suffix(, 0)
};
int main(void) {
printf("Prima linie a programului: %s\n", inceput);
printf("Octeți (cu terminatorul 0): %zu\n", sizeof inceput);
return 0;
}
Rezultat
Prima linie a programului: #include <stdio.h>
Octeți (cu terminatorul 0): 19
Exemplul 2: Fișier care poate lipsi: __has_embed
sigla.png nu există lângă program, deci __has_embed dă __STDC_EMBED_NOT_FOUND__ și se folosește un text de rezervă. Fără verificare, un #embed spre un fișier lipsă oprește compilarea.
#include <stdio.h>
#if __has_embed("sigla.png") == __STDC_EMBED_FOUND__
static const unsigned char sigla[] = {
#embed "sigla.png"
};
#define ARE_SIGLA 1
#else
#define ARE_SIGLA 0
#endif
int main(void) {
if (ARE_SIGLA) {
printf("Sigla a fost inclusă în program\n");
} else {
printf("sigla.png lipsește: folosesc textul „dexblog”\n");
}
return 0;
}
Rezultat
sigla.png lipsește: folosesc textul „dexblog”
Exemplul 3: Numărarea octeților și a liniilor dintr-un fișier
Tot fișierul sursă ajunge în tablou la compilare; programul numără apoi caracterele de linie nouă. Numerele depind de conținutul fișierului, deci sunt mereu aceleași pentru același cod.
#include <stdio.h>
static const unsigned char sursa[] = {
#embed "exemplu.c"
};
int main(void) {
int linii = 0;
for (size_t i = 0; i < sizeof sursa; i++) {
if (sursa[i] == '\n') {
linii++;
}
}
printf("exemplu.c are %zu octeți și %d linii\n", sizeof sursa, linii);
return 0;
}
Rezultat
exemplu.c are 327 octeți și 15 linii
Greșeli frecvente
- Pui
#embedpe aceeași linie cu alt cod. Ca orice directivă de preprocesor, trebuie să stea singură pe linia ei, în interiorul inițializatorului. - Folosești tabloul ca șir de caractere fără
suffix(, 0). Octeții fișierului nu au terminator, iarprintf("%s")citește dincolo de tablou. - Incluzi fișiere mari în tablouri locale (pe stivă). Declară tabloul
static const, ca datele să stea în zona de date a programului.
Comenzi înrudite
#include: Copiază conținutul unui fișier antet (.h) în locul liniei, ca funcțiile și tipurile declarate acolo să poată fi folosite.#if, #ifdef, #ifndef: Compilare condiționată: preprocesorul păstrează sau elimină bucăți de cod în funcție de macro definite sau de valoarea lor.Tablouri (array): Un șir de elemente de același tip, așezate unul după altul în memorie și accesate prin index, de la 0.fread și fwrite: Scriu și citesc blocuri de octeți (tablouri, struct-uri) într-un fișier binar; fseek și ftell mută și arată poziția curentă.static: Într-o funcție, variabila își păstrează valoarea între apeluri; la nivel de fișier, ascunde funcția sau variabila de celelalte fișiere.