ГлавнаяРегистрацияВход >>> 08-308 <<<
Четверг, 02.10.2025, 21:42
Форма входа
Меню сайта

Главная » LEARN » Зачет №8 Системы программирования для Си

Модуль в программировании представляет собой функционально законченный фрагмент программы, оформленный в виде отдельного файла с исходным кодом, предназначенный для использования в других программах. Модули позволяют разбивать сложные задачи на более мелкие. Обычно проектируются таким образом, чтобы предоставлять программистам удобный для многократного использования функционал (интерфейс) в виде набора функций, классов, констант. Модули могут объединяться в пакеты и, далее, в библиотеки.
Модули могут быть обычными, т.е. написанными на том же языке, что и программа, в которой они используются, либо модулями расширения, которые пишутся на отличном от языка основной программы языке. Модули расширения обычно пишутся на более низкоуровневом языке, что позволяет получить выигрыш в скорости выполнения (производительности) программы.
В си модули подключаются с помощью директивы #include. Директива #include дает указание компилятору читать еще один исходный файл — в дополнение к тому файлу, в котором находится сама эта директива. Имя исходного файла должно быть заключено в двойные кавычки или в угловые скобки. Например, обе директивы
#include "stdio.h"
#include <stdio.h>
дают компилятору указание читать и компилировать заголовок для библиотечных функций системы ввода/вывода.
Файлы, имена которых находятся в директивах #include, могут в свою очередь содержать другие директивы #include. Они называются вложенными директивами #include. Количество допустимых уровней вложенности у разных компиляторов может быть разным. Однако в стандарте С89 предусмотрено, что компиляторы должны допускать не менее 8 таких уровней. А в стандарте С99 предусмотрена поддержка не менее 15 уровней вложенности.
Способ поиска файла зависит от того, заключено ли его имя в двойные кавычки или же в угловые скобки. Если имя заключено в угловые скобки, то поиск файла проводится тем способом, который определен в компиляторе. Часто это означает поиск определенного каталога, специально предназначенного для хранения таких файлов. Если имя заключено в кавычки, то поиск файла проводится другим способом. Во многих компиляторах это означает поиск файла в текущем рабочем каталоге. Если же файл не найден, то поиск повторяется уже так, как будто имя файла заключено в угловые скобки.
Обычно большинство программистов имена стандартных заголовочных файлов заключают в угловые скобки. А использование кавычек обычно приберегается для имен специальных файлов, относящихся к конкретной программе. Впрочем, твердого и простого правила, по которому кавычки требуется использовать именно таким образом, не существует.
В С-программе директиву #include можно использовать не только для указания имени файла, содержащего обычный исходный текст программы, но и для указания заголовка. В языке С определен набор стандартных заголовков, содержащих необходимую информацию о различных библиотеках этого языка. Заголовок — это стандартный идентификатор, который может соответствовать имени файла, а может и не соответствовать ему. Таким образом, заголовок является просто абстракцией, которая гарантирует наличие некоторой информации. Однако на практике в языке С заголовки почти всегда являются именами файлов.
Директивы #if, #else, #elif и #endif
Возможно, самыми распространенными директивами условной компиляции являются #if, #else, #elif и #endif. Они дают возможность в зависимости от значения константного выражения включать или исключать те или иные части кода.
В общем виде директива #if выглядит таким образом:
#if константное выражение
последовательность операторов
#endif
Если находящееся за #if константное выражение истинно, то компилируется код, который находится между этим выражением и #endif. В противном случае этот промежуточный код пропускается. Директива #endif обозначает конец блока #if. Например,
/* Простой пример #if. */
#include <stdio.h>

#define MAX 100

int main(void)
{
#if MAX>99
printf("Компилирует для массива, размер которого больше 99.\n");
#endif

return 0;
}
Это программа выводит сообщение на экран, потому что МАХ больше 99.
Директива #else работает в основном так, как else — ключевое слово языка С: задает альтернативу на тот случай, если не выполнено условие #if. Предыдущий пример можно дополнить следующим образом:
/* Простой пример #if/#else. */
#include <stdio.h>

#define MAX 10

int main(void)
{
#if MAX>99
printf("Компилирует для массива, размер которого больше 99.\n");
#else
printf("Компилирует для небольшого массива.\n");
#endif

return 0;
}
В этом случае выясняется, что МАХ меньше 99, поэтому часть кода, относящаяся к #if, не компилируется. Однако компилируется альтернативный код, относящийся к #else, и откомпилированная программа будет отображать сообщение Компилируется для небольшого массива.
Директива #elif означает "else if" и устанавливает для множества вариантов компиляции цепочку if-else-if. После #elif находится константное выражение. Если это выражение истинно, то компилируется находящийся за ним блок кода, и больше не проверяются никакие другие выражения #elif. В противном же случае проверяется следующий блок этой последовательности. В общем виде #elif выглядит таким образом:
#if выражение
последовательность операторов
#elif выражение 1
последовательность операторов
#elif выражение 2
последовательность операторов
#elif выражение 3
последовательность операторов
#elif выражение 4
.
.
.
#elif выражение N
последовательность операторов
#endif
Например, в следующем фрагменте для определения знака денежной единицы используется значение ACTIVE_COUNTRY (для какой страны):
#define US 0
#define ENGLAND 1
#define FRANCE 2

#define ACTIVE_COUNTRY US

#if ACTIVE_COUNTRY == US
char currency[] = "dollar";
#elif ACTIVE_COUNTRY == ENGLAND
char currency[] = "pound";
#else
char currency[] = "franc";
#endif
В соответствии со стандартом С89 у директив #if и #elif может быть не менее 8 уровней вложенности. А в соответствии со стандартом С99 программистам разрешается использовать не менее 63 уровней вложенности. При вложенности каждая директива #endif, #else или #elif относится к ближайшей директиве #if или #elif. Например, совершенно правильным является следующий фрагмент кода:
Директивы #ifdef и #ifndef
Другой способ условной компиляции — это использование директив #ifdef и #ifndef, которые соответственно означают "if defined" (если определено) и "if not defined" (если не определено). В общем виде #ifdef выглядит таким образом:
#ifdef имя_макроса
последовательность операторов
#endif
Блок кода будет компилироваться, если имя макроса было определено ранее в операторе #define.
В общем виде оператор #ifndef выглядит также как и #ifdef образом:
Блок кода будет компилироваться, если имя макроса еще не определено в операторе #define.
И в #ifdef, и в #ifndef можно использовать оператор #else или #elif. Например,
#include <stdio.h>

#define TED 10

int main(void)
{
#ifdef TED
printf("Привет, Тед\n");
#else
printf("Привет, кто-нибудь\n");
#endif
#ifndef RALPH
printf("А RALPH не определен, т.ч. Ральфу не повезло.\n");
#endif

return 0;
}
выведет Привет, Тед, а также A RALPH не определен, т.ч. Ральфу не повезло.
В соответствии со стандартом С89 допускается не менее 8 уровней #ifdef и #ifndef. А стандарт С99 устанавливает, что должно поддерживаться не менее 63 уровней вложенности.


Интерфейс отражает внешнее поведение объекта, описывая абстракцию поведения всех
объектов данного класса. Внутренняя реализация описывает представление этой абстракции и механизмы достижения желаемого поведения объекта. Разделение интерфейса и реализации позволяет защитить объекты от деталей реализации объектов более низкого уровня. Инкапсуляция позволяет вносить в программу изменения, сохраняя ее надежность и минимизируя затраты на этот процесс. Традиционным в C++ является помещение интерфейсной части модулей в отдельные файлы с расширением .h.
Класс - множество объектов, связанных общностью структуры и поведением. Существует явное разделение внутреннего и внешнего описания класса. Интерфейсная часть описания класса соответствует его внешнему проявлению, подчёркивает его абстрактность, но скрывает структуру и особенности поведения.
Реализация составляет его внутреннее проявление и определяет особенности поведения. Интерфейсная часть описания класса может быть разделена на три составные части: общедоступную; защищённую и обособленную.
С точки зрения контрактного программирования класс - это генеральный контракт между абстракцией и всеми ее клиентами. Все обязательства класса выражены в его интерфейсе.
Состояние объекта задается в его классе через определение констант или переменных, помещаемых в его защищенной или закрытой части.
Интерфейсом называется набор операций, используемый для определения услуг, предоставляемых классом или компонентом, выполняемых прецедентом или подсистемой. Интерфейс изображается в виде круга, присоединенному к реализующему его классу или компоненту. Интерфейс может быть изображен также как стереотипный класс с множеством операций. Для интерфейса определена операция реализации. Он может быть реализован несколькими классами и наоборот один класс может реализовывать несколько интерфейсов. Отметим, что у интерфейса нет атрибутов и нет непосредственных экземпляров.

ОБЪЕКТНЫЙ МОДУЛЬ
Обье́ктный мо́дуль (также — объектный файл, англ. object file) — файл с промежуточным представлением отдельного модуля программы, полученный в результате обработки исходного кода компилятором. Объектный файл содержит в себе особым образом подготовленный код (часто называемый бинарным), который может быть объединён с другими объектными файлами при помощи редактора связей (компоновщика) для получения готового исполняемого модуля либо библиотеки.
Объектные файлы представляют собой блоки машинного кода и данных, с неопределенными адресами ссылок на данные и процедуры в других объектных модулях, а также список своих процедур и данных. Компоновщик собирает код и данные каждого объектного модуля в итоговую программу, вычисляет и заполняет адреса перекрестных ссылок между модулями. Также в процессе компоновки происходит связывание программы со статическими и динамическими библиотеками (являющихся архивами объектных файлов.
Объектный модуль программы получается в результате трансляции исходного текста модуля. В состав объектного модуля программы помещается оттранслированный код модуля, информация для редактора связей, позволяющая объединять модули в единую программу, и отладочная информация (переменные, константы, метки и их адреса).
В большинстве современных языков программирования программа состоит из отдельных слабо связанных модулей. Как правило, каждому такому модулю соответствует отдельный файл исходного текста. Эти файлы независимо обрабатываются языковым процессором (компилятором), и для каждого из них генерируется отдельный файл, называемый объектным модулем.

Трансляция + Редактирование связей (комапоновка).

Программа - это последовательность инструкций, предназначенных для выполнения компьютером. В настоящее время программы оформляются в виде текста, который записывается в файлы. Этот текст является результатом деятельности программиста и, несмотря на специфику формального языка, остаётся программой для программиста.
Процесс создания программы предполагает несколько этапов. За этапом разработки проекта программы следует этап программирования. На этом этапе пишется программа. Программистами этот текст воспринимается легче двоичного кода, поскольку различные мнемонические сокращения и имена заключают дополнительную информацию.
Файл с исходным текстом программы (его также называют исходным модулем) обрабатывается транслятором, который осуществляет перевод программы с языка программирования в понятную машине последовательность кодов. Процесс трансляции разделяется на несколько этапов.
На первом этапе исходный текст (он обычно хранится в виде текстового файла) подвергается лексической обработке. Программа разделяется на предложения, предложение делится на элементарные составляющие (лексемы). Каждая лексема распознаётся (имя, ключевое слово, литерал, символ операции или разделитель) и преобразуется в соответствующее двоичное представление. Этот этап работы транслятора называют лексическим анализом.
Затем наступает этап синтаксического анализа. На этом этапе из лексем собираются выражения, а из выражений - операторы. В ходе трансляции последовательности терминальных символов преобразуются в нетерминалы. Невозможность достижения очередного нетерминала является признаком синтаксической ошибки в тексте исходной программы.
После синтаксического анализа наступает этап поэтапной генерации кода. На этом этапе происходит замена операторов языка высокого уровня инструкциями ассемблера, а затем последовательностями машинных команд. Результат преобразования исходного текста программы записывается в виде двоичного файла (его называют объектным модулем) с расширением ".obj".
Объектный модуль можно выполнять лишь после специальной дополнительной обработки (компоновки), которая осуществляется специальной программой-компоновщиком.
Рассмотрим в общих чертах процесс компоновки (Редактирования связей). Программа строится из инструкций и операторов. В свою очередь, операторы включают выражения, которые состоят из операций и операндов. По крайней мере, части операндов в выражениях должны соответствовать отдельные "участки" оперативной памяти, предназначаемые, например, для сохранения результатов вычислений.
В ходе трансляции устанавливается соответствие между операндами и адресами областей памяти вычислительной машины. Так вот задача компоновщика состоит в согласовании адресов во всех фрагментах кода, из которых собирается готовая к выполнению программа. Компоновщик отвечает за то, чтобы конкретному операнду выражения соответствовала определённая область памяти.
Компоновщик также добавляет к компонуемой программе коды так называемых библиотечных функций (они обеспечивают выполнение конкретных действий - вычисления, вывод информации на экран дисплея и т.д.), а также код, обеспечивающий размещение программы в памяти, её корректное начало и завершение.
Преобразованная компоновщиком программа называется загрузочным или выполнимым модулем. Файлы, содержащие загрузочные модули, называют загрузочными или выполнимыми файлами.

Процесс создания
В большинстве современных языков программирования программа состоит из отдельных слабо связанных модулей. Как правило, каждому такому модулю соответствует отдельный файл исходного текста. Эти файлы независимо обрабатываются языковым процессором (компилятором), и для каждого из них генерируется отдельный файл, называемый объектным модулем. Затем запускается программа,- называемая редактором связей, компоновщиком или линкером (linker — тот, кто связывает), которая формирует из заданных объектных модулей цельную программу.
Объектный модуль отчасти похож по структуре на перемещаемый загрузочный модуль. Дело в том, что сборку программы из нескольких модулей можно уподобить загрузке в память нескольких программ.
Кроме того, в объектных файлах может содержаться отладочная информация, формат которой может быть очень сложным. Следовательно, объектный файл представляет собой довольно сложную и рыхлую структуру. Размер собранной программы может оказаться в два или три раза меньше суммы длин объектных модулей.
Типичный объектный модуль содержит следующие структуры данных.
• Таблицу перемещений, т. е. таблицу ссылок на перемещаемые объекты внутри модуля.
• Таблицу ссылок на внешние объекты. Иногда это называется таблицей или списком импорта.
• Таблицу объектов, определенных в этом модуле, на которые можно ссылаться из других модулей. В некоторых случаях ее называют списком экспорта. Иногда таблицы экспорта и импорта объединяют и называют все это таблицей глобальных символов. В этом случае для каждого символа приходится указывать, определен он в данном модуле или нет, а если определен, то как.
• Различную служебную информацию, такую, как имя модуля, программу, которая его создала (например, строка "gcc compiled").
• Отладочную информацию.
• Собственно код и данные модуля.
Редактор связей и то, что с ним связано.
реда́ктор свя́зей, англ. linker, link editor) — программа, которая производит компоновку — принимает на вход один или несколько объектных модулей и собирает по ним исполняемый модуль.
Для связывания модулей, компоновщик использует таблицы имён, созданные компилятором в каждом из объектных модулей. Такие имена могут быть двух типов:
• Определённые или экспортируемые имена — функции и переменные, определённые в данном модуле и предоставляемые для использования другим модулям
• Неопределённые или импортируемые имена — функции и переменные, на которые ссылается модуль, но не определяет их внутри себя
Работа компоновщика заключается в том, чтобы в каждом модуле разрешить ссылки на неопределённые имена. Для каждого импортируемого имени находится его определение в других модулях, упоминание имени заменяется на его адрес.

Опция –l + Библиотеки

Ключи компоновщика задают режими работы компоновщика, наиболее часто используется ключ -l задающий имена системных библиотек объектных модулей. Системные библиотеки хранятся в каталоге /lib или /usr/lib а их имена начинаются с lib и заканчиваются .a. Например стандартная библиотека Си называется libc.a (эта библиотека используется всегда и ее указывать не требуется), библиотека математических функций хранится в файле libm.a. При использовании ключа -l нужно указать оригинальную часть имени системной библиотеки, т.е. для использования libm.a нужно указать ключ -lm.
Исходные_модули это имена одного или нескольких файлов с окончанием .c содержащих тексты программ на Си.
Объектные_модули это имена одного или нескольких файлов с расширением .o содержащих объектные модули, которые будут использованы для получения исполняемой программы.

3.1. Введение в библиотеки
Как уже неоднократно упоминалось в предыдущей главе, библиотека - это набор скомпонованных особым образом объектных файлов. Библиотеки подключаются к основной программе во время линковки. По способу компоновки библиотеки подразделяют на архивы (статические библиотеки, static libraries) и совместно используемые (динамические библиотеки, shared libraries). В Linux, кроме того, есть механизмы динамической подгрузки библиотек. Суть динамической подгрузки состоит в том, что запущенная программа может по собственному усмотрению подключить к себе какую-либо библиотеку.

Опция -l, переданная компилятору, обрабатывается и посылается линковщику для того, чтобы тот подключил к бинарнику библиотеку. Как вы уже заметили, у имени библиотеки "обрублены" префикс и суффикс. Это делается для того, чтобы создать "видимое безразличие" между статическими и динамическими библиотеками. Сейчас важно знать лишь то, что и библиотека libfoo.so и библиотека libfoo.a подключаются к проекту опцией -lfoo. В нашем случае libworld.a "урезалось" до -lworld.
Опция -L указывает линковщику, где ему искать библиотеку. В случае, если библиотека располагается в каталоге /lib или /usr/lib, то вопрос отпадает сам собой и опция -L не требуется. В нашем случае библиотека находится в репозитории (в текущем каталоге). По умолчанию линковщик не просматривает текущий каталог в поиске библиотеки, поэтому опция -L. (точка означает текущий каталог) необходима.

-Lкаталог
Добавить каталог в список каталогов, которые содержат объектные библиотечные модули.
-lбиблиотека
При редактировании связей подключить модули из библиотеки.

Библиотека (в программировании, от англ. library) — сборник подпрограмм или объектов для решения близких по тематике задач.
С точки зрения ОС и прикладного ПО библиотеки разделяются на: динамические и статические.
Также называются библиотеки общего пользования или разделяемые библиотеки (англ. shared library) или динамически подключаемые библиотеки (англ. Dynamic Link Library, DLL). Это отдельные файлы, предоставляющие прикладным программам набор наиболее часто используемых функций, и загружаемые на этапе выполнения при обращении программы к ОС с заявкой на выполнение функции из библиотеки. Если запрошенная библиотека уже загружена в ОЗУ, программа будет пользоваться загруженной копией. Такой подход позволяет экономить память, поскольку несколько программ используют одну копию библиотеки, загруженную в память.
Динамические библиотеки хранятся обычно в определенном месте и имеют стандартное расширение. Например, файлы .library в логическом томе Libs: в AmigaOS; в Microsoft Windows и OS/2 файлы библиотек общего пользования имеют расширение .dll; в UNIX‐подобных ОС — обычно .so; в MacOS — .dylib.
При написании программы программисту достаточно указать транслятору языка программирования (компилятору или интерпретатору), что следует подключить такую-то библиотеку и использовать такую-то функцию из указанной библиотеки. Ни исходный текст, ни исполняемый код функции в состав программы не входит.
Могут быть в виде исходного текста, подключаемого программистом к своей программе на этапе написания (например, для языка Fortran существует огромное количество библиотек для решения разных задач именно в исходных текстах), либо в виде объектных файлов, присоединяемых (линкуемых) к исполняемой программе на этапе компиляции (в Microsoft Windows такие файлы имеют расширение .lib, в UNIX‐подобных ОС — обычно .a). В результате программа включает в себя все необходимые функции, что делает её автономной, но увеличивает размер.
DLL (англ. Dynamic-link library — динамически подключаемая библиотека) — понятие операционной системы Microsoft Windows; динамическая библиотека, позволяющая многократное применение различными программными приложениями. K DLL относятся также элементы управления ActiveX и драйверы. В мире UNIX аналогичные функции выполняют т. н. shared objects («разделяемые объекты»).
Первоначально предполагалось, что введение DLL позволит эффективно организовать память и дисковое пространство, используя только одну инстанцию библиотечных модулей для многих приложений. Это было особенно важно для ранних версий Microsoft Windows с жёсткими ограничениями по памяти.
Далее, предполагалось улучшить эффективность разработок и использования системных средств за счёт модульности. Замена DLL-программ с одной версии на другую должна была позволить независимо наращивать систему, не затрагивая приложений. Кроме того, библиотеки DLL могли использоваться разнотипными приложениями — например, Microsoft Office, Microsoft Visual Studio и т. п.
В дальнейшем идея модульности выросла в концепцию COM.
Фактически, полных преимуществ от внедрения DLL получить не удалось по причине явления, называемого DLL_hell («ад DLL»). DLL Hell возникает, когда несколько приложений требуют одновременно различные, не полностью совместимые, версий DLL-библиотек, что приводит к сбоям в этих приложениях. Когда система выросла до определённых размеров, количество DLL стало превышать многие тысячи, не все из них обладали полной надёжностью и совместимостью, и конфликты типа DLL Hell стали возникать очень часто, резко понижая общую надёжность системы. Поздние версии Microsoft Windows стали разрешать параллельное использование разных версий DLL, что свело на нет преимущества изначального принципа модульности.


Если некоторый объект объявлен внутри блока, то он видим в этом блоке, и во всех внутренних блоках. Если объект объявлен на внешнем уровне, то он видим от точки его объявления до конца данного исходного файла.
Объект может быть сделан глобально видимым с помощью соответствующих объявлений во всех исходных файлах, образующих программу.
Спецификатор класса памяти в объявлении переменной может быть auto, register, static или extern. Если класс памяти не указан, то он определяется по умолчанию из контекста объявления.
Переменная, объявленная локально с классом памяти extern, является ссылкой на переменную с тем же самым именем, определенную глобально в одном из исходных файлов программы. Цель такого объявления состоит в том, чтобы сделать определение переменной глобального уровня видимым внутри блока.
Пример:
/* объявления переменной i, являющейся именем внешнего
массива длинных целых чисел, на локальном уровне */
/* исходный файл file1.c */
main()
{ ...
}
fun1()
{ extern long i[]; ...
}
/* исходный файл file2.c */
long i[MAX]={0};
fun2()
{ ...
}
fun3()
{ ...
}
Объявление переменной i[] как extern в приведенном примере делает ее видимой внутри функции fun1. Определение этой переменной находится в файле file2.c на глобальном уровне и должно быть только одно, в то время как объявлений с классом памяти extern может быть несколько.
Объявление с классом памяти extern требуется при необходимости использовать переменную, описанную в текущем исходном файле, но ниже по тексту программы, т.е. до выполнения ее глобального определения. Следующий пример иллюстрирует такое использование переменной с именем st.
Пример:
main()
{ extern int st[]; ...
}
static int st[MAX]={0};
fun1()
{ ...
}
Объявление переменной со спецификатором extern информирует компилятор о том, что память для переменной выделять не требуется, так как это выполнено где-то в другом месте программы.

Про модульное многоязычее ничего не нашел. Чисто предположение, что с помощью этой утилиты можно использовать одни и те же переменные в разных модулях, написанных на разных языках.


Утилита make предназначена для упрощения сборки (компиляция, редактирование связей, автоматическая подготовка документации) проектов программ модульной структуры. Характерными особенностями, позволившими этой достаточно простой утилите стать стандартным средством ведения проектов, является её переносимость, легкая настраиваемость на конкретные требования и т. д.
При использовании make проект разбивается на программные единицы (чаще всего, на объектные и исполняемые файлы, библиотеки, файлы с исходными текстами программ), между которыми устанавливаются взаимосвязи.

С помощью утилиты make можно не только быстро создать исполняемый файл, но также и задать некоторые скрипты используя операцию clear: и run: Также можно задать отдельное создание объектного файла и если есть объектный файл, то отдельно создание исполняемого файла. В утилите make объектные файлы и исполняемый файлы можно задать как переменные. Также можно выбрать компилятор.
Рассмотрим пример: есть библиотека stack.h, программа stack.c , где описаны все функции библиотеки stack.h и main файл lab26.c, все они находятся в папке scr. Объектный файл stack.o содержится в папке tmp.

СС=gcc #выбор компилятора
CFALGS= -c -std=c99 -Wall

LD=gcc
LDFLAGS=

all: objects prog #задание целий

objects: ./tmp/stack.o #создание объектного файла

./tmp/stack.o: ./scr/stack.c ./scr/stack.h

$(CC) $(CFALGS) -o ./tmp/stack.o ./scr/stack.c

prog: ./lab26 #исполняемого файла
./lab26: ./scr/lab26.c objects
$(LD) $(LDFLAGS) -o ./lab26 ./scr/lab26.c ./tmp/stack.o

clear: #скрипт удаляет запрашиваемые данные из данный папок
rm -i ./lab26 ./tmp/* ./scr/*~ ./scr/\#* ./scr/*.swp

run:
make all #скрипт создаёт исполняемый файл и тут же запускает его
./lab26


Для энтузиастов платформы Alpha-Linux компания Compaq предлагает бесплатную лицензию на компиляторы C (ccc) и C++ (cxx).
Базовая оптимизация достигается вызовом компиляторов с ключом '-fast'.
Compaq С , существующий для Compaq Tru64 UNIX, OpenVMS Alpha, Linux Alpha портирован на FreeBSD Alpha и включен в дерево портов в 2000 г



Онлайн всего: 1
Гостей: 1
Пользователей: 0
 
Поиск
Copyright MyCorp © 2025
Сделать бесплатный сайт с uCoz