Что такое переносимая программа

• Язык • Заголовочные файлы и библиотеки • Организация программы • Изоляция • Обмен данными • Порядок байтов • Переносимость и внесение усовершенствований • Интернационализация • Заключение • Дополнительная литература Наконец, стандартизация, так же как и соглашения, может служить еще одной демонстрацией строгого порядка. Но, в отличиеЪт соглашений, она принимается в современной архитектуре как продукт, хоть и украшающий нашу технологию, но опасный изза ее потенциального доминирования и грубости.

Роберт Ветури Сложности и противоречия в архитектуре Написать корректную и эффективную программу трудно. Поэтому если программа уже работает в одной среде, то вам, скорее всего, не захочется повторять пройденный путь ее создания при переходе на другой компилятор, процессор или операционную систему. В идеале программа не должна требовать внесения никаких изменений.

Этот идеал называется переносимостью (portability). На практике термин «переносимость» нередко относят к более слабому свойству: программу проще видоизменить при переносе в другую среду исполнения, чем написать заново. Чем меньше изменений надо внести, тем выше переносимость программы.

Что такое Portable программы

Вы можете спросить, почему вообще зашел разговор о переносимости: если программа будет исполняться в какой-то конкретной среде, зачем тратить время на то, чтобы обеспечивать ей более широкую область применения? Ну, во-первых, каждая хорошая программа почти по определению начинает с какого-то момента применяться в непредсказуемых местах.

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

Чем меньше программа зависит от специальных возможностей, тем меньше вероятность возникновения сбоев при изменении условий и тем проще программа адаптируется к этим условиям. И наконец, последнее и самое важное обстоятельство: переносимая программа всегда лучше написана. Усилия, затраченные на обеспечение переносимоести программы, сказываются на всех ее аспектах; она оказывается лучше спроектированной, аккуратнее написанной и тщательнее протестированной. Технологии программирования переносимых программ непосредственно привязаны к технологиям хорошего программирования вообще.

Естественно, степень переносимости должна определяться исходя из реальных условий. Нет такой вещи, как абсолютно переносимая программа, — разве только программа, опробованная не во всех средах. Однако мы можем сделать переносимость одной из своих главных целей, стараясь создать программу, которая бы выполнялась без изменений практически везде.

Даже если эта цель не будет достигнута в полном объеме, время, потраченное на ее достижение, с лихвой окупится, если программу придется переделывать под другие условия. Мы хотим посоветовать следующее: старайтесь писать программы, которые бы работали при всех комбинациях различных стандартов, интерфейсов и сред, в принципе подходящих для ее исполнения. Не старайтесь исправить каждую ошибку переносимости, добавляя дополнительный код, наоборот, адаптируйте программу для работы при новых ограничениях. Используйте абстракцию и инкапсуляцию, чтобы ограничить и контролировать те непереносимые фрагменты кода, без которых не обойтись. Если ваш код сможет работать при всех ограничениях, а системные различия в нем будут локализованы, он станет еще и более понятным.

Программы Portable — что это и зачем нужны?

Язык

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

Однако и для него трудно, даже для стандартных языков, описать точно, каким именно образом компилятор преобразует его в машинные инструкции. Очень немногие из распространенных языков представлены единственной реализацией: обычно имеется множество производителей различных версий для различных операционных систем, которые к тому же со временем изменяются.

Каждая версия будет обрабатывать ваш код по-своему. Почему стандарт не является строгим описанием? Иногда стандарт неполон и не описывает отдельных специфических случаев. Иногда он намеренно неконкретен: например, тип cha r в С и C++ может иметь знак, а может и не иметь; он даже не обязательно должен быть 8-битовым.

Подобные детали оставлены на усмотрение создателя компилятора; в этом есть свои плюсы: стимулируется появление новых эффективных реализаций; снимаются ограничения, накладываемые на железо, но жизнь программиста, конечно, несколько усложняется. Вообще, степень проработанности стандарта зависит от многих глобальных причин.

И наконец, нельзя забывать, что языки достаточно запутанны, а компиляторы весьма сложны; в них могут быть неправильности интерпретации и ошибки реализации. Иногда же языки вообще не стандартизованы. Официальный стандарт ANSI/ISO С был принят в 1988 году, а стандарт ISO C++ ратифицирован только в 1998-м.

На момент написания этой книги не все из распространенных компиляторов поддерживают это официальное описание. Язык Java сравнительно молод, и его стандарт можно ждать только через несколько лет. Вообще стандарт языка разрабатывается только после того, как создаются несколько конфликтующих* версий, которые надо унифицировать, а сам язык получает достаточно широкое распространение, оправдывающее затраты на стандартизацию. А между тем попрежнему надо писать программы и поддерживать в этих программах различные среды исполнения. Итак, несмотря на то что пщ знакомстве со справочными руководствами и стандартами складывается впечатление жесткой спецификации языка, они никогда не описывают язык полностью, и различные версии компиляторов могут создавать

работоспособные, но несовместимые друг с другом реализации. Иногда возникают даже ошибки.

Вот довольно характерный пример: подобные внешние описания недопустимы в С и C++ ? *х[] = <«abc»>; Проверив с десяток компиляторов, мы выяснили, что лишь несколько из них корректно определяют пропущенный определитель типа — слово char для х. Значительная часть выдает предупреждение о несовместимости типов (очевидно, те, что используют старое описание языка: они неверно полагают х массивом указателей на int), а еще пара компилировала этот недопустимый код, не сделав ни вздоха. Следуйте основному руслу.

Читайте также:
Программа чтобы стать модератором

Неспособность некоторых компиляторов выявить ошибку в приведенном примере весьма прискорбна, но зато мы теперь сможем осветить важный аспект переносимости. У каждого языка есть свои непроработанные моменты, точки зрения на которые разнятся (например, битовые поля в С и C++), и благоразумие подсказывает избегать их использования.

Стоит использовать только те возможности, описание которых недвусмысленно и хорошо понятно. Очевидно, что именно такие возможности, скорее всего, будут широко доступны и реализованы везде одинаковым образом. Мы называем их основным руслом (mainstream) языка.

Трудно однозначно определить, какие именно конструкции входят в это основное русло, но зато те, что в него не входят, определить просто. Совершенно новые возможности, такие как complex или комментарии // в С, или возможности, специфичные для конкретной архитектуры, вроде ключевых слов near и far, обязательно создадут вам проблемы.

Если что-то в языке настолько необычно или непонятно, что для того, чтобы разобраться, вам приходится консультироваться с «языковым правоведом» — экспертом по чтению его описаний, не используйте такую возможность вовсе. В своем обсуждении мы сконцентрируем основное внимание на С и C++. широко распространенных и универсальных языках.

Стандарт С существует уже более десятка лет, и язык очень стабилен, правда, разрабатывается новый стандарт, так что впереди очередные подвижки. А вот стандарт C++ появился совсем недавно, и потому еще не все реализации этого языка приведены с ним в соответствие.

Что можно считать основным руслом в С? Этот термин обычно относят к установившемуся стилю использования языка, но иногда лучше принимать во внимание грядущие изменения. Например, первоначальная версия С не требовала создания прототипов функций. Описание функции sq rt выглядело при этом так: ? double sqrt(); что определяло тип возвращаемого значения, но не параметров. В ANSI С были добавлены прототипы функций, в которых определялось уже все: do.uble sqrt(double); Компиляторы ANSI С должны воспринимать и прежний синтаксис, но мы настоятельно рекомендуем вам забыть об этом и всегда писать прототипы для всех функций. Это сделает код более безопасным, вызовы функций будут полностью проверены на совместимость типов, и при изменении интерфейса компилятор это отловит. Если в коде употребляется вызов

func(7, PI); a func не имеет прототипа, компилятор не сможет проверить корректность такого вызова. Если библиотека впоследствии изменится так, что у func станет три аргумента, компилятор не сможет предупредить о необходимости внесения изменений в программу, потому что старый синтаксис не предусматривает проверки типов аргументов функций.

C++ — более мощный и обширный язык, и стандарт его появился лишь недавно, поэтому говорить об основном русле применительно к нему несколько сложнее. Например, нам представляется очевидным, что STL войдет в это основное русло, что происходит, однако, не мгновенно, и поэтому некоторые существующие реализации языка поддерживают STL не в полной мере.

Избегайте неоднозначных конструкций языка. Как мы уже говорили, некоторые вещи в стандартах умышленно оставлены неопределенными для того, чтобы предоставить создателям компиляторов большую свободу для маневра. Список таких неопределенностей обескураживающе велик. Размеры типов данных. Размеры основных типов данных в С и C++ не определены.

Не существует никаких гарантированных свойств, кроме общих правил, гласящих, что sizeof(char) <= sizeof (short) <= sizeof(int) <= sizeof(long) sizeof(float) <= sizeof(double) % а также. что char должен иметь как минимум 8 битов, short и int — как минимум 16, a long — по крайней мере 32. Не требуется даже, чтобы значение указателя умещалось в inj. Проверить, какие значения использует конкретный компилятор, достаточно просто: /* sizeof: выводит размеры базовых типов */ int main(void) < printf(«char %d, short %d, int %d, long %d,»,. sizeof(char), sizeof(short), sizeof(int), sizeof(long)); printf(» float %d, double %d, void* %dn», sizeof(float), sizeof(double), sizeof(void *)); return 0; >Результат будет одинаковым для большинства распространенных машин: char 1, short 2, int 4, long 4, float 4, double 8, void* 4 однако возможны и другие значения. Некоторые 64-битовые машины покажут такие значения:

char 1, short 2, int 4, long 8, float 4, double 8, void* 8 а ранние компиляторы под PC показали бы такое: char 1, short 2, int 2, long 4, float 4, double 8, void* 2 Во времена появления PC аппаратура поддерживала несколько видов указателей. Для того чтобы справиться с такой неразберихой, были придуманы модификаторы указателей far и near, ни один из которых не входит в стандарт, но до сих пор эти ключевые слова-призраки появляются во многих компиляторах.

Если ваш компилятор может менять размеры базовых типов или если вы имеете доступ к машинам, поддерживающим другие размеры, постарайтесь скомпилировать и оттестировать вашу программу при таких новых для нее условиях. Стандартный заголовочный файл stddef. h определяет ряд типов, которые могут помочь с переносимостью.

Наиболее часто используемый из них — size_t, который представляет собой тип беззнакового целого, возвращаемого оператором sizeof. Значения этого типа возвращаются функциями типа strlen и во многих функциях, включая mall ос, используются в качестве аргументов. Наученная чужим горьким опытом, Java четко определяет размеры всех своих базовых типов: byte — 8 битов, char и short — 16, int — 32 и long — 64 бита.

Читайте также:
Чем отличаются школьные программы

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

В С и C++ порядок вычислений операндов выражений, побочных эффектов и аргументов функций не определен. Например, в присваивании ? n = (getcharQ « 8) getchar(); второй getcha r может быть вызван первым: порядок, в котором выражение записано, не обязательно соответствует порядку, в котором оно исполняется. В выражении ? ptr[count] = name[++count]; значение count может быть увеличено как до, так и после использования его в качестве индекса pt г, а в выражении ? . printf(«%c %cn», getchar(), getchar()); первый введенный символ может быть распечатан на втором месте, а не на первом. В выражении ? printf(«%f %sn», logC-1.23), strerror(errno)); значение errno может оказаться вычисленным до вызова log. Для некоторых выражений существуют четкие правила вычисления. По определению все побочные эффекты и вызовы функций должны быть завершены до

ближайшей точки с запятой или при вызове функции. Операторы и | | выполняются слева направо и только до тех пор, пока это требуется для определения их значения (включая побочные эффекты). В операторе ?: сначала вычисляется условие, включая возможные побочные эффекты, и только после этого вычисляется одно из двух завершаюшжс оператор выражений.

В Java порядок вычислений описан более жестко. В нем предусмотрено, что выражения, включая побочные выражения, вычисляются слева направо; правда, в одном авторитетном руководстве дан совет не писать кода, который бы «критично» зависел от этого порядка.

Прислушаться к этому совету просто необходимо при создании программ, у которых есть хотя бы призрачный шанс конвертироваться в С или C++: как мы уже сказали, там нет никаких гарантий соблюдения такого же порядка. Конвертирование из одного языка в другой — экстремальный, но иногда весьма полезный тест на переносимость программы. Наличие знака у char.

В С и C++ не определено, является ли char знаковым или беззнаковым типом данных. Это может привести к проблемам при использовании комбинаций char и int, как, например, в приводимом коде, где вызывается функция getchar(), возвращающая значение типа int: Если вы напишете ?char с; /* должно было быть int */ ? с = getchar(); то значение с будет в диапазоне от 0 до 255, если char — беззнаковый тип, и в диапазоне от -128 до 127, если char — знаковый тип; речь идет о практически стандартной конфигурации с 8-битовыми символами на машинах с дополнительным до двух кодом.

Это имеет особый смысл, если символ должен использоваться как индекс массива или для проверки на EOF, который в stdio обычно представляется значением -1. Например, представим, что мы разработали этот код из параграфа 6.1 после исправления некоторых граничных условий в начальной версии.

Сравнение s[i] == EOF никогда не будет истиной, если char — беззнаковый тип: ? int i; ? char s[MAX]; ? for (1=0; i < MAX-1; i++) ? if ((s[i] = getcharO) == An’ || s[i] == EOF) ? break; ? s[i] = ‘О’: Когда getchar возвратит EOF, в s[i] будет сохранено значение 255 (OxFF, результат преобразования -1 в unsigned char). Если s[i] беззнаковое, то при сравнении с EOF его значение останется 255, и, следовательно, проверка не пройдет. Однако, даже если char является знаковым типом, код все равно некорректен. В этом случае сравнение с EOF будет проходить нормально, но при вводе вполне допустимого значения OxFF оно будет воспринято как EOF и цикл будет прерван. Так что вне зависимости от того, знаковый или беззнаковый у вас char, хранить значение, возвращаемое getcha r, вы должны в int, и тогда проверка на конец файла будет осуществляться нормально. Вот как должен выглядеть наш цикл в переносимом виде:

int c, i; char s[MAX]; for (1=0; i < MAX-1; i++) < if ((c = getcharO) ==’n’ || с == EOF) break; s[i] = c; >s[i] = ‘О’; В языке Java вообще нет спецификатора unsigned; порядковые типы данных являются знаковыми, а тип char (16-битовый) — беззнаковым. Арифметический или логический сдвиг.

Сдвиг вправо знаковых величин с помощью оператора » может быть арифметическим (при сдвиге распространяется копия знакового бита) или логическим (при сдвиге освободившиеся биты заполняются нулями). И здесь Java, наученная горьким опытом С и C++, резервирует » для арифметического сдвига вправо и предоставляет отдельный оператор >» для логического сдвига вправо. Порядок байтов.

Порядок байтов внутри short, int и long не определен; байт с младшим адресом может быть как наиболее значимым, так и наименее значимым. Этот вопрос зависит от аппаратуры, и мы подробно обсудим его несколько ниже в этой главе. Выравнивание членов структуры или класса.

Расположение элементов внутри структур, классов и объединений (union) не определено, утверждается лишь, что члены располагаются в порядке объявления. Например, в структуре struct X < char с; int i; >; адрес iможет находиться на расстоянии 2, 4 или 8 байтов от начала структуры.

Некоторые (немногие) машины позволяют целым храниться на нечетных границах, но большинство требует, чтобы п-байтовые элементарные типы данных хранились на и-байтовых границах, чтобы, например, double, которые имеют, как правило, длину в 8 байтов, хранились по адресам, кратным 8. В дополнение к этому создатели компиляторов могут вносить и свои ограничения — такие как принудительное выравнивание для повышения производительности. Никогда не рассчитывайте на то, что элементы структуры занимают смежные области памяти.

Читайте также:
Программа для того чтобы наложить голос на музыку

Ограничения на выравнивание вызывают появление «дыр» в структурах — так, st ruct X всегда будет содержать по крайней мере один байт неиспользуемого пространства. Из-за этих дыр размер структуры может быть больше, чем сумма размеров ее членов, причем этот размер может быть разным на разных машинах.

Если вы хотите зарезервировать память под структуру, всегда запрашивайте sizeof (struct X) байтов, ноне sizeof (char) + sizeof(int). Битовые поля. Битовые поля настолько зависят от конкретных машин, что никому не следует их использовать. Все перечисленные опасные места можно миновать, следуя нескольким правилам. Не используйте побочные эффекты нигде, кроме как в идиоматических конструкциях типа

Источник: studfile.net

Переносимые приложения. Эксплуатация в Windows-домене

Несмотря на кажущуюся простоту, создание и оптимизация переносимых приложений – достаточно сложная вещь. Возникающие сложности касаются не столько создания, сколько настройки приложения и его оптимизации перед компиляцией файла.

Выбор программы для создания переносимых приложений

Создать переносимые приложения можно с помощью таких программ, как VMware ThinApp [1], Microsoft App-V [2]. Несмотря на солидный список, все приложения работают по одному принципу. Их создание сводится к вычислению разницы в конфигурации операционной системы «до» и «после» установки и настройки приложения с последующей компиляцией выполняемого файла.

Чтобы быстро создать portable-приложение, рекомендуется использовать виртуальную машину VMware Workstation [3] или, например, Microsoft Virtual PC [4]. Участие виртуальных машин в данном процессе очень важно, поскольку одно из важнейших требований к созданию переносимого приложения – отсутствие предустановленной какой-либо версии данной программы. К сожалению, деинсталляторы после удаления приложения не могут обеспечить должную чистоту реестра и файловой системы. Единственный способ быстро получить «чистую» рабочую станцию – использовать виртуальную машину и в случае необходимости создавать так называемые состояния системы (snapshots).

Для создания переносимых приложений рекомендуется использовать связку: VMWare ThinApp совместно с VMware Workstation.

Пример создания переносимого приложения

Продемонстрируем создание переносимых приложений на примере Adobe Acrobat Reader X. Во время этого процесса специалист выполняет следующие шаги:

  • Подготовка виртуальной машины.
  • Создание проекта переносимого приложения:
  • Создание слепка системы до установки программы.
  • Установка приложения Adobe Acrobat Reader X.
  • Создание слепка системы после установки.
  • Оптимизация получившегося проекта.
  • Сборка переносимых приложений.
  • Внедрение созданной программы.

Подготовка виртуальной машины

Подготовка виртуальной машины к созданию переносимых приложений включает в себя два этапа: создание и настройка. Если она существовала ранее, то необходимо вернуться к сделанному раньше снимку системы (snapshot) или скопировать файлы виртуальной машины из заранее подготовленной резервной копии.

Создание виртуальной машины

Инсталляция и обновление программного обеспечения

Когда-то для того чтобы начать использовать программу, её достаточно было просто скопировать на свой компьютер. Сейчас большинство программ нужно устанавливать (инсталлировать). Для этого есть важные причины:

  • некоторые программы могут работать только на достаточно мощных компьютерах (например, только с 1 Гбайт оперативной памяти), и нужно проверить, выполняются ли такие требования;
  • программы содержат множество файлов, которые должны быть записаны на диск в определенные папки;
  • пользователь должен получить возможность выбрать нужные ему компоненты программы (остальные не устанавливаются);
  • при установке некоторых программ необходимо вводить ключ (серийный номер копии программы).

Установка ПО — это дополнительная работа для пользователей. Поэтому особой популярностью пользуются переносимые программы (англ. portable applications). Их не нужно устанавливать, они могут быть просто скопированы на жёсткий диск компьютера или запущены прямо с CD-, DVD-диска или флэш-накопителя. Такие программы очень полезны для тех, кто часто работает на разных компьютерах и хочет использовать привычный набор программ.

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

Выводы: · С помощью программного обеспечения можно приспособить компьютер для решения задач конкретных пользователей. · Пользователи решают свои задачи с помощью прикладных программ. · Операционная система – важнейшее программное обеспечение, без которого использовать современный компьютер практически невозможно. · Программисты пишут программы с помощью систем программирования. · Кроссплатформенные программы могут работать в различных операционных системах. · Пользователь работает с компьютером в режиме диалога. Он может использовать интерфейс командной строки или графический интерфейс. · Для быстрого доступа к программам и документам можно использовать ярлыки (ссылки). · Инсталляция – это установка и настройка программы на компьютере пользователя.

Интеллект-карта

1. Чем различаются три типа программного обеспечения?

2. Что означает слово «интерфейс»?

3. Как вы думаете, почему большинство простых встроенных компьютеров (например, в стиральных машинах) работают без операционных систем?

4. Какое ПО называется кроссплатформенным?

5. В каких случаях мы можем не найти программу в Главном меню операционной системы?

6. Что такое переносимая программа?

7. Что будет, если после создания ярлыка программу переместить в другую папку? (Проверьте на компьютере!)

8. Что такое инсталляция? Почему она необходима для многих современных программ?

9. Обсудите достоинства и недостатки автоматического обновления программного обеспечения. Как вы думаете, почему некоторые пользователи отключают автоматическое обновление?

Темы сообщений:

а) «Кроссплатформенные программы»

б) «Запуск программ в разных операционных системах»

в) «Главное меню операционной системы»

г) «Связь программ и документов»

д) «Настройка ярлыков»

е) «Зачем нужны инсталляторы?»

ж) «Живые диски (Live-CD)»

Интересные сайты:

Источник: cyberpedia.su

Рейтинг
( Пока оценок нет )
Загрузка ...
EFT-Soft.ru