Не хотелось бы думать, что производитель оборудования цинично привязывает ваш продукт к своему компилятору, поэтому будем считать, что он искренне желает помочь. Но теперь вам решать — как использовать эту помощь, чтобы не навредить себе же в будущем. Вам придется ограничить круг файлов, которым будет известно о нестандартных расширениях языка C.
Взгляните на следующий заголовочный файл, созданный для семейства ACME процессоров цифровой обработки сигналов:
#ifndef _ACME_STD_TYPES
#define _ACME_STD_TYPES
#if defined(_ACME_X42)
typedef unsigned int Uint_32;
typedef unsigned short Uint_16;
typedef unsigned char Uint_8;
typedef int Int_32;
typedef short Int_16;
typedef char Int_8;
#elif defined(_ACME_A42)
typedef unsigned long Uint_32;
typedef unsigned int Uint_16;
typedef unsigned char Uint_8;
typedef long Int_32;
typedef int Int_16;
typedef char Int_8;
#else
#error
#endif
#endif
Заголовочный файл acmetypes.h не следует использовать непосредственно, иначе ваш код окажется тесно связанным с процессорами ACME. Вы думаете, что если используете процессор ACME, то какой вред он может причинить? Все просто, вы не сможете скомпилировать свой код, не подключив этот заголовочный файл. А если подключить его и определить символ _ACME_X42 или _ACME_A42, целые числа будут иметь неправильный размер при тестировании за пределами целевой платформы. Какое-то время это может не вызывать никаких проблем, но однажды у вас может появиться желание перенести свое приложение на другой процессор, и тогда вы обнаружите, что задача сильно усложнилась из-за отказа от переносимости и ограничения на подключение файлов, знающих о процессорах ACME.
Вместо использования acmetypes.h следует попробовать пойти более стандартным путем и использовать stdint.h. Но как быть, если в состав целевого компилятора не входит файл stdint.h? Вы можете сами написать этот заголовочный файл. Файл stdint.h может использовать acmetypes.h, когда выполняется компиляция для целевой платформы:
#ifndef _STDINT_H_
#define _STDINT_H_
#include
typedef Uint_32 uint32_t;
typedef Uint_16 uint16_t;
typedef Uint_8 uint8_t;
typedef Int_32 int32_t;
typedef Int_16 int16_t;
typedef Int_8 int8_t;
#endif
Использование stdint.h во встраиваемом программном обеспечении и микропрограммах поможет вам сохранить код чистым и переносимым. Всякое
void say_hi()
{
IE = 0b11000000;
SBUF0 = (0x68);
while(TI_0 == 0);
TI_0 = 0;
SBUF0 = (0x69);
while(TI_0 == 0);
TI_0 = 0;
SBUF0 = (0x0a);
while(TI_0 == 0);
TI_0 = 0;
SBUF0 = (0x0d);
while(TI_0 == 0);
TI_0 = 0;
IE = 0b11010000;
}
Эта маленькая функция страдает множеством проблем. Первое, что бросается в глаза, — присутствие последовательности символов 0b11000000. Такая форма записи двоичных чисел очень удобна, но поддерживается ли она стандартным языком C? К сожалению, нет. Еще несколько проблем проистекают непосредственно из использования нестандартных расширений:
• IE: устанавливает биты разрешения прерываний.
• SBUF0: буфер вывода последовательного порта.
• TI_0: прерывание опустошения буфера передачи последовательного порта. Если операция чтения возвращает 1, это указывает, что буфер пуст.
Переменные с именами, состоящими из букв верхнего регистра, в действительности представляют механизмы доступа к встроенной периферии микроконтроллера. Если программе понадобится управлять прерываниями и выводить символы, вам придется использовать эту периферию. Да, это удобно, но это уже не язык C.