Kiedy próbowałem zapoznać się w praktyce z C, zaprzyjaźnieni programiści wskazali mi przeglądarkę plików tekstowych SEE. To fonetyczne nieporozumienie typowe dla języka angielskiego, nieodmiennie śmieszy mnie do dziś.
|
prehistoria
Zakładowy dział informatyki lat 80-tych koncentrował się zwykle na przetwarzaniu danych, a dominującym językiem był Clipper, wyposażony w wygodne funkcje do manipulowania danymi typu DBF. Nauczyłem się całkiem biegle posługiwać tym narzędziem, ale dziś nie zaliczam go do swych fascynacji, ani nawet nie przyznaję się do tej umiejętności w obawie, że ktoś mógłby zechcieć ją wykorzystać. Clipper nie był zbyt wydajny w wyświetlaniu informacji, a na dodatek dysponowaliśmy bardzo kiepskimi komputerami. To pchnęło mnie w objęcia języka C, którego moduły łączyły się bezboleśnie z programami w Clipperze. Dzięki temu powstała biblioteka funkcji grzebiących bezpośrednio w pamięci karty graficznej i wyświetlających dane z prędkością światła. Z dzisiejszego punktu widzenia jest to umiejętność całkowicie nieprzydatna, ale zabawa w optymalizację kodu była bardzo pouczająca.
za jakie grzechy?
Trudno znaleźć język programowania łatwiejszy... do popełniania błędów. Swą niezwykłą popularność zawdzięcza prawdopodobnie temu, że stał się podstawowym narzędziem oprogramowania w systemie UNIX, a w ślad za nim w Linuksie. Stał się tak popularny, że dziś praktycznie nie ma systemu, dla którego nie znaleźlibyśmy (przeważnie darmowego) kompilatora. Wartym zauważenia atutem C jest bogaty wybór funkcji bibliotecznych.
Choć nie grzeszy urodą i ma wiele mankamentów, C trwale wrósł w historię i teraźniejszość informatyki, dał podwaliny wielu współczesnym językom, jak Perl, Java czy .NET - nie ma przed nim ucieczki.
kiedy używam C?
O tym, czy napisaliśmy program w C czy w C++ decyduje głównie wykorzystanie (bądź nie) elementów programowania obiektowego. Ale i tak zawsze można użyć kompilatora C++ do skompilowania takiego programu. Dla mnie trzymanie się czystego C w przypadkach, gdy złożoność zagadnienia na to pozwala, jest bardziej kwestią staromodnego przywiązania do prostoty niż oczekiwaniem jakichś wydumanych korzyści. Program skompiluje się szybciej, a jego kod binarny będzie z pewnością nieco krótszy, choć nie w stopniu, który by można oceniać w kategoriach marnotrawstwa pamięci masowej. Po prostu lubię używać minimalnych środków wystarczających do osiągnięcia założonego celu.
oprogramowanie portu szeregowego
Ten przykład pokazuje, jak można wykorzystać port szeregowy komputera w systemie Linux w charakterze prostego urządzenia WE/WY. Uwypukla atuty języka C: współużywane deklaracje zawarte w pliku nagłówkowym, wygodny dostęp do rejestrów sprzętowych komputera, zwarty, efektywny kod.
Uwaga: programowy dostęp do portu szeregowego wymaga uprawnień administratora. Plik nagłówkowy ser.h zawiera definicje bitów w rejestrach urządzenia i kojarzy je ze stykami gniazda. Rysunek w stylu ASCII-art pokazuje rozmieszczenie końcówek w gnieździe, tak jak je widać na tylnej ścianie komputera.
|
# ser.h # #include <stdlib.h> // exit #include <sys/io.h> // read, write I/O ports /* male +-----------+ 1 \ o o o o o / \ o o o o / 9 +-------+ */ #define PORTS { 0x3F8, 0x2F8, 0x3E8, 0x2E8 } #define MCR 0x04 // Modem Control Register #define MSR 0x06 // Modem Status Register #define DCD 0x80 // <- 1 Data Carrier Detect (MSR) // RXD <- 2 Receive Data // TXD -> 3 Transmit Data #define DTR 0x01 // -> 4 Data Terminal Ready (MCR) // GND -- 5 Signal Ground #define DSR 0x20 // <- 6 Data Set Ready (MSR) #define RTS 0x02 // -> 7 Request To Send (MCR) #define CTS 0x10 // <- 8 Clear To Send (MSR) #define RI 0x40 // <- 9 Ring Indicator (MSR) Pokazany obok program setser pozwala ustawić stan pojedynczego bitu portu szeregowego. a getser - odczytać stan jednego bitu.
Tworzymy binaria, które przydadzą się do zabawy z portem:
gcc -O setser.c -DBIT=DTR -o setdtr gcc -O setser.c -DBIT=RTS -o setrts gcc -O getser.c -DBIT=CTS -o getcts Poniższy skrypt przełącza świecenie diody, oczekuje na pstryknięcie wyłącznikiem, przełącza diodę do stanu początkowego i kończy działanie. Ponieważ złącze portu nie zawiera dodatniego napięcia odniesienia, musimy je sobie wykreować, podając na czas testu "1" na wyjście RTS.
#!/bin/sh ./setdtr 0 1 ./setrts 0 1 while ! ./getcts; do continue; done ./setrts 0 0 ./setdtr 0 0 |
/* setser.c by jbw, 2004-10-17 set/clear serial control line Arguments: serial device (0=/dev/ttyS0=COM1, 1=/dev/ttyS1=COM2...) 0 to clear, 1 to set */ #include "ser.h" #ifndef BIT #define BIT DTR #endif static unsigned int ports[] = PORTS; // serial ports int main(int argc, char **argv) { int dnum = 0, value = 0, adr; unsigned char b; // get command line if (argc > 1) { dnum = atoi(argv[1]); if((dnum < 0) || (dnum > 3)) dnum = 0; } if (argc > 2) { value = atoi(argv[2]); } adr = ports[dnum]+MCR; if (ioperm(adr, 1, 1)) exit(1); // request for port access b = inb(adr); // get current register value if (value) outb(b|BIT, adr); // set BIT line else outb(b&~BIT, adr); // clear BIT line return 0; // and release port } /* getser.c by jbw, 2006-04-26 Read serial port status bit Arguments: serial device (0=/dev/ttyS0=COM1, 1=/dev/ttyS1=COM2...) */ #include "ser.h" #ifndef BIT #define BIT CTS #endif static unsigned int ports[] = PORTS; // serial ports int main(int argc, char **argv) { int dnum = 0, msr; if (argc > 1) { // get command line dnum = atoi(argv[1]); if((dnum < 0) || (dnum > 3)) dnum = 0; } msr = ports[dnum]+MSR; if (ioperm(msr, 1, 1)) return -1; // request for port access return (inb(msr) & BIT) == 0; // and release port } |
|