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

Usunięta treść Dodana treść
Mina86 (dyskusja | edycje)
Totalna przebudowa rozdziału.
T ziel (dyskusja | edycje)
poprawki językowe i interpunkcja
Linia 1:
Jak dowiedziałeś się z poprzednich rozdziałów tego podręcznika, język C umożliwia tworzenie programów, które mogą być uruchamiane na różnych platformach sprzętowych pod warunkiem ich powtórnej kompilacji. Język C należy do grupy języków wysokiego poziomu, które tłumaczone są do poziomu kodu maszynowego (tzn. kod źródłowy jest kompilowany). Z jednej strony jest to korzystne posunięcie, gdyż programuprogramy są szybsze i mniejsze niż programy napisane w językach interpretowanych (takich, w których kod źródłowy nie jest kompilowany do kodu maszynowego, tylko ''na bieżąco'' interpretowany przez specjalnetzw. narzędzieinterpreter). jednakJednak istnieje także druga strona medalu - pewne zawiłości sprzętu, które ograniczają przenośność programów. Ten rozdział ma wyjaśnić Ci mechanizmy działania sprzętu w taki sposób, abyś bez problemu mógł tworzyć poprawne i całkowicie przenośne programy.
 
== Niezdefiniowane zachowanie i zachowanie zależne od implementacji ==
W trakcie czytania kolejnych rozdziałów można było się natknąć na zwroty takie jak zachowanie niezdefiniowane (ang. ''undefined behaviour'') czy zachowanie zależne od implementacji (ang. ''implementation-defined behaviour''). Cóż one tak właściwie oznaczają?
 
Zacznijmy od tego drugiego. Autorzy standardu języka C czuli, że wymuszanie jakiegoś konkretnego działania danego wyrażenia byłoby zbytnim obciążeniem dla osób piszących kompilatory, gdyż dany wymógł mógłby być bardzo trudny do zrealizowania na jakiejś architekturze. Dla przykładu, gdyby standard wymagał, że typ unsigned char ma dokładnie 8 bitów to napisanie kompilatora dla architektury, na której bajt ma 9 bitów byłoby cokolwiek kłopotliwe, a z pewnością wynikowy program działałby o wiele wolniej niżby mógłto było możliwe.
 
Z tego właśnie powodu, niektóre aspekty języka nie są określone bezpośrednio w standardzie i są pozostawione do decyzji zespołu (osoby) piszącego konkretną implementację. W ten sposób, nie ma żadnych przeciwwskazań (ze strony standardu), aby na architekturze, gdzie bajty mają 9 bitów, typ char również miał tyle bitów. Dokonany wybór musi być jednak opisany w dokumentacji kompilatora, tak żeby osoba pisząca program w C mogła sprawdzić jak dana konstrukcja zadziała.
Linia 12:
Zachowania niezdefiniowane są o wiele groźniejsze, gdyż zaistnienie takowego może spowodować dowolny efekt, który nie musi być nigdzie udokumentowany. Przykładem może tutaj być próba odwołania się do wartości wskazywanej przez wskaźnik o wartości NULL.
 
Jeżeli gdzieś w naszym programie zaistnieje sytuacja niezdefiniowanego zachowania, to to już nie jest już to kwestia przenośności kodu, ale po prostu błędu w programie, aczkolwiek nie zawsze. Otóż nic nie stoi na przeszkodzie, aby konkretny kompilator opisał działanie jakiegoś wyrażenia, które według standardu wpadanależy wdo omawianąomawianej klasęklasy zachowań. Tak często bywa z operatorem przesunięcia bitowego działającego dla liczb ujemnych. - wW takich wypadkachprzypadkach nadal pozostaje jednak kwestia przenośności kodu i jeżeli zależy nam na niej, nie możemy korzystać z takich funkcjiudogodnień kompilatora.
 
Istnieje jeszcze trzecia klasa zachowań. Zachowania nieokreślone (ang. ''unspecified behaviour''). Są to sytuacje, gdy standard określa kilka możliwych sposobów w jaki dane wyrażenie może działać i pozostawia kompilatorowi decyzję co z tym dalej zrobić. Coś takiego nie musi być nigdzie opisane w dokumentacji i znowu poleganie na konkretnym zachowaniu jest błędem. Klasycznym przykładem może być kolejność obliczania argumentów wywołania funkcji.
Linia 51:
Czasami zdarza się, że napisany przez nas program musi się komunikować z innym programem (może też przez nas napisanym), który działa na komputerze o (potencjalnie) innym porządku bajtów. Często najprościej jest przesyłać liczby jako tekst, gdyż jest on niezależny od innych czynników, jednak taki format zajmuje więcej miejsca, a nie zawsze możemy sobie pozwolić na taką rozrzutność.
 
Przykładem może być komunikacja sieciowa, w której przyjełoprzyjęło się, że dane przesyłane są w porządku big-endian. Aby móc łatwo operować na takich danych, w standardstandardzie POSIX definiujezdefiniowano następujące funkcje (w zasadzie zazwyczaj są to makra):
#include <arpa/inet.h>
uint32_t htonl(uint32_t hostlong);
Linia 143:
 
== Biblioteczne problemy ==
Pisząc programy nieraz będziemy musieli korzystać z różnych bibliotek. Problem polega na tym, że nie zawsze będą one dostępne na komputerze, na którym inny użytkownik naszego programu będzie próbował go kompilować. Dlatego też ważne jest, abyśmy korzystali z łatwo dostępnych bibliotek, które dostępne są na wiele różnych systemów i platform sprzętowych. '''Zapamiętaj''': twójTwój program jest na tyle przenośny na ile przenośne są biblioteki z których korzysta!
 
<noinclude>