C/Przenośność programów: Różnice pomiędzy wersjami

m
Update syntaxhighlight tags - remove use of deprecated <source> tags
m (Update syntaxhighlight tags - remove use of deprecated <source> tags)
Jednak możemy posiadać implementację, która nie posiada tego pliku nagłówkowego. W takiej sytuacji nie pozostaje nam nic innego jak tworzyć własny plik nagłówkowy, w którym za pomocą słówka '''typedef''' sami zdefiniujemy potrzebne nam typy. Np.:
 
<sourcesyntaxhighlight lang="c">
typedef unsigned char u8;
typedef signed char s8;
typedef unsigned long long u64;
typedef signed long long s64;
</syntaxhighlight>
</source>
 
Aczkolwiek należy pamiętać, że taki plik będzie trzeba pisać od nowa dla każdej architektury na jakiej chcemy kompilować nasz program.
 
Przykładem może być komunikacja sieciowa, w której przyjęło się, że dane przesyłane są w porządku big-endian. Aby móc łatwo operować na takich danych, w standardzie POSIX zdefiniowano następujące funkcje (w zasadzie zazwyczaj są to makra):
<sourcesyntaxhighlight lang="c">
#include <arpa/inet.h>
uint32_t htonl(uint32_t);
uint32_t ntohl(uint32_t);
uint16_t ntohs(uint16_t);
</syntaxhighlight>
</source>
 
Pierwsze dwie konwertują liczbę z reprezentacji lokalnej na reprezentację big-endian (''host to network''), natomiast kolejne dwie dokonują konwersji w drugą stronę (''network to host'').
Można również skorzystać z pliku nagłówkowego endian.h, w którym definiowane są makra pozwalające określić porządek bajtów:
 
<sourcesyntaxhighlight lang="c">
#include <endian.h>
#include <stdio.h>
return 0;
}
</syntaxhighlight>
</source>
 
Na podstawie makra __BYTE_ORDER można skonstruować funkcję, która będzie konwertować liczby pomiędzy różnymi porządkami:
 
<sourcesyntaxhighlight lang="c">
#include <endian.h>
#include <stdio.h>
return 0;
}
</syntaxhighlight>
</source>
 
Ciągle jednak polegamy na niestandardowym pliku nagłówkowym endian.h. Można go wyeliminować sprawdzając porządek bajtów w czasie wykonywania programu:
 
<sourcesyntaxhighlight lang="c">
#include <stdio.h>
#include <stdint.h>
return 0;
}
</syntaxhighlight>
</source>
 
Powyższe przykłady opisują jedynie część problemów jakie mogą wynikać z próby przenoszenia binarnych danych pomiędzy wieloma platformami. Wszystkie co więcej zakładają, że bajt ma 8 bitów, co wcale nie musi być prawdą dla konkretnej architektury, na którą piszemy aplikację. Co więcej liczby mogą posiadać w swojej reprezentacje bity wypełnienia (ang. ''padding bits''), które nie biorą udziały w przechowywaniu wartości liczby. Te wszystkie różnice mogą dodatkowo skomplikować kod. Toteż należy być świadomym, iż przenosząc dane binarnie musimy uważać na różne reprezentacje liczb.
Przy zwiększaniu przenośności kodu może pomóc [[C/Preprocesor|preprocessor]]. Przyjmijmy np., że chcemy korzystać ze słówka kluczowego inline wprowadzonego w standardzie C99, ale równocześnie chcemy, aby nasz program był rozumiany przez kompilatory ANSI C. Wówczas możemy skorzystać z następującego kodu:
 
<sourcesyntaxhighlight lang="c">
#ifndef __inline__
# if __STDC_VERSION__ >= 199901L
# endif
#endif
</syntaxhighlight>
</source>
 
a w kodzie programu zamiast słówka inline stosować __inline__. Co więcej, kompilator GCC rozumie słówka kluczowe tak tworzone i w jego przypadku warto nie redefiniować ich wartości:
 
<sourcesyntaxhighlight lang="c">
#ifndef __GNUC__
# ifndef __inline__
# endif
#endif
</syntaxhighlight>
</source>
 
Korzystając z kompilacji warunkowej można także korzystać z różnego kodu zależnie od (np.) systemu operacyjnego. Przykładowo, przed kompilacją na konkretnej platformie tworzymy odpowiedni plik config.h, który następnie dołączamy do wszystkich plików źródłowych, w których podejmujemy decyzje na podstawie zdefiniowanych makr. Dla przykładu, plik config.h:
 
<sourcesyntaxhighlight lang="c">
#ifndef CONFIG_H
#define CONFIG_H
#endif
</syntaxhighlight>
</source>
 
Jakiś plik źródłowy:
 
<sourcesyntaxhighlight lang="c">
#include "config.h"
rob_cos_wersja_dla_linux(void);
#endif
</syntaxhighlight>
</source>
 
Istnieją różne narzędzia, które pozwalają na automatyczne tworzenie takich plików config.h, dzięki czemu użytkownik przed skompilowaniem programu nie musi się trudzić i edytować ich ręcznie, a jedynie uruchomić odpowiednie polecenie. Przykładem jest zestaw autoconf i automake.
90

edycji