Практикум для изучения дисциплины «Основы программирования»



Pdf көрінісі
бет59/81
Дата08.07.2020
өлшемі1,55 Mb.
#74978
түріПрактикум
1   ...   55   56   57   58   59   60   61   62   ...   81
Байланысты:
А.А. Тюгашев
А.А. Тюгашев, А.А. Тюгашев, А.А. Тюгашев
ЗАМЕЧАНИЕ 
Потребность взять определенные участки битов в слове может возникать при 
взаимодействии  с  внешними  устройствами  ЭВМ,  а  также  при  решении  задач 
информационной безопасности. 
Значение может иметь тип либо int, либо unsigned int и занимать от 1 
до 16 бит. Поля размещаются в машинном слое в направлении от младших 
к  старшим  разрядам.  В  полях  типа  int  крайний  левый  бит —  знаковый. 
Если поле состоит ровно из одного бита, то это либо 0, либо –1. Поля не 
могут  быть  массивами  и  не  имеют  адресов,  поэтому  к  ним  нельзя 
применять операцию &. Пример: 


117 
 
/* битовое поле на Си */ 
struct prim 

  
int a:2; /* два бита с назанием a, значения от -3 до 1*/ 
  
unsigned b:3; /* три бита с названием b, значения 0-8 */ 
  int c:1; 
} i; /* (справа налево) 0011000=24 */ 
Поля — не единственный способ выделения наборов битов в слове. Могут 
использоваться  также  маскирование  с  помощью  операции  И —  &  (биты, 
которые  нам  нужны, —  значение  разряда  маски  1,  не  нужны —  0)  и 
побитовый сдвиг: 
(с&24)>>3; /* наложили маску 0011000=24 и сдвинули на 3 вправо */ 
Смесь —  переменная,  которая  в  разное  время  может  хранить  объекты 
различного типа и размера. Появляется возможность работы в одной и той 
же области памяти с данными различного вида. Синтаксис следующий: 
/* использование смеси в Си */ 
union k 

  int ik; 
  float fk; 
  char ck; 
} z; 
Переменная  z  должна  быть  достаточно  велика  для  того,  чтобы  хранить 
любой из трех типов. В один и тот же момент времени z может хранить 
значение  только  одной  из  переменных-компонентов.  Следующий  пример 
также иллюстрирует работу с битовыми полями и смесями. 
/* Проба битовых полей и смесей */ 
#include  
 
void printbin(int* ch) 

  int i; 
  for (i=15;i>=0;i--) 
    printf("%d", ((*ch)>>i) & 1); 
  printf("\n"); 

 
struct bits 

  unsigned a:3; 
  unsigned b:3; 


118 
 
  unsigned c:4; 
} sb; 
 
union uni1 

  float f; 
  char c; 
  int k; 
} u; 
 
int main() 

  struct bits* uk=&sb; 
 
  u.f=12.5; 
  u.c='c'; 
  u.k=5; 
 
  printf("
Значение смеси %f\n",u.f); 
 
  uk->a=5; 
  uk->b=5; 
  uk->c=0xB; 
 
  
printf("Битовое поле = %d\n",uk->a); 
  printbin((int*) uk); 
 
  return 0; 

Как  уже  отмечалось,  язык  Си —  язык  системного  программирования. 
Неудивительна поэтому легкость интеграции Си с ассемблером. Например, 
во  многих  версиях  Си-систем  вполне  допустимы  прямые  ассемблерные 
вставки  в  программу  по  следующему  принципу  (значение  команд 
ассемблера приводится в соответствующем разделе данной книги): 
/* ассемблерные вставки в программу на Си */ 
#include  
#pragma inline  
int main () 

    int a=10,b=20,c; 
    print ("a= %d, b=%d\n",a,b);     


119 
 
    
asm mov ax,10; /* строка на ассемблере — занесение 10 в регистр 
AX */ 
    
asm mul a; /* строка на ассемблере — команда умножения AX на a 
*/ 
    
c=_AX; /* прямой доступ к регистру AX процессора, с = 100 */ 
    print ("c= %d \n",c);     
    
/* целый фрагмент на ассемблере */ 
    asm {  mov ax,a 
 
    mov bx,b 
 
    xchg ax,bx 
 
    mov a,ax 
    mov b,bx 
    } 
    print ("a= %d, b=%d\n",a,b);     
    return 0; 

Как видно из примера (варианта реализации Си-системы), в программе на 
Си  возможно  прямое  обращение  к  регистрам  процессора,  при  этом  их 
имена  предваряются  символом  подчеркивания  _  (для  процессоров 
архитектуры Intel x86 это _AX, _BX, _DX и пр.). Для вставки одной строки 
на  языке  ассемблер  достаточно  написать  в  ее  начале  ключевое  слово 
asm
 — 
и все дальнейшее до знака перевода строки будет восприниматься 
как  строка  ассемблера.  Несколько  строк  можно  вставить  с  помощью 
конструкции  asm {}.  Чтобы  подобные  вставки  стали  допустимыми, 
используется  директива  препроцессора  #pragma  inline.  Изнутри 
ассемблерных  вставок  переменные,  объявленные  в  программе  на  Си, 
остаются  доступными  просто  по  их  именам  (контроль  типов — 
применимости  ассемблерных  команд  к  данным —  ложится  на 
программиста). 
Контрольные вопросы и упражнения 
1. 
Что означает запись (*z)++? Как изменится значение переменной z? 
Значение по адресу z? 
2. 
В  чем  разница  между  записями  a[5]  и  *(a+5)  при  обращении  к 
элементу массива? 
3. 
Каким образом вы применили бы в программах указатели на функции? 
Приведите пример. 
4. 
В какой ситуации нужны функции с переменным числом параметов? 
5. 
В каких ситуациях уместно использование битовых полей? Придумайте 
пример. 


120 
 
6. 
Возможен ли доступ из ассемблерной вставки к переменной Си? 
7. 
Возможен ли доступ в программе на Си к регистру процессора? 
Достоинства и недостатки языка Си 
Язык  программирования  Си  был  создан  довольно  давно —  в  1971  году. 
После появления он очень быстро завоевал популярность и потеснил такие 
языки,  как  Фортран,  Алгол  и  PL/I.  Впоследствии  язык  Си  сыграл 
значительную  роль  в  развитии  языков  программирования —  стал 
непосредственным прародителем или оказал влияние на такие языки, как 
Objective C, C++, Java, C#, PHP, D и др. 
Согласно  рейтингу  языков  программирования  TIOBE,  на  октябрь  2013 
года  чистый  Си  по  популярности  находится  на  первом  (!)  месте  среди 
языков программирования, опередив Java (второе место), С++ (четвертое) 
и  С#  (шестое).  Более  того,  шесть  первых  мест  рейтинга  заняты  языками, 
родственными Си! 
В чем причина такого успеха? 
Несомненными достоинствами языка программирования Си являются: 
 
высокая скорость и компактность получаемых машинных программ — 
эффективность, из-за которой язык широко используется при написании 
встроенных приложений; 
 
низкоуровневые  возможности —  также  востребованы  при  создании 
встроенных приложений и системных программ; 
 
широкая  известность  и  наличие  компиляторов  для  очень  большого 
числа платформ. 
В  то  же  время  следует  еще  раз  отметить  ряд  особенностей  языка 
программирования  Си,  которые,  на  взгляд  автора  (несмотря  на  то  что 
обычно  достоинства  являются  продолжениями  недостатков  и  наоборот), 
вполне  можно  считать  его  недостатками  и  которые  ограничивают  сферу 
его успешного применения. 
Конструкции  Си  изначально  рассчитывались  на  профессионалов — 
системных  программистов,  и  поэтому  текст  программы  как  минимум  не 
вполне ясен начинающему программисту, а кроме этого он предоставляет 
весьма  широкие  возможности —  можно  сказать:  язык  «остер,  как 
бритва» —  можно  порезаться!  На  Си  допустим,  к  примеру,  следующий 
фрагмент программы (сочинен студентами МАИ): 
int X; 
X = 1; 
X+=X++ + ++X; /* Догадайся, чему будет равен X? */ 
Си (и, увы, во многом произошедшие от него языки, хотя в них пытались 


121 
 
эту  проблему  решать —  например,  в  Java  запрещено  в  явном  виде 
использование  указателей и  отсутствует  адресная  арифметика) изобилует 
потенциально  небезопасными  конструкциями.  Так,  в  нем  можно 
применить присваивание вместо проверки на равенство внутри оператора 
if
, например: 
if (current->uid=0) retval=1; 
и это не вызовет ошибки. Кстати, для борьбы именно с этой уязвимостью 
был  предложен  метод  записи  условий  в  программах  на  Си,  названный 
нотацией  Йоды.  Этот  персонаж  саги  «Звездные  войны»  необычным 
образом строил предложения, меняя привычный порядок слов. При записи 
if
 
в программе на Си в виде условия Йоды сначала пишется константа, с 
которой производится сравнение, и лишь после знака — переменная: 
if (0==current->uid) retval=1; 
Естественно, присвоить числовой константе значение нельзя, и это вызовет 
ошибку. Некоторые программисты считают хорошим тоном использовать 
в  программах  на  Си  нотацию  Йоды.  Однако  это  не  спасает  ситуацию  в 
целом.  Не  вызывает  сомнений,  что  Си  –  изобилующий  потенциальными 
опасностями  и  не  вполне  прозрачный  для  восприятия  человеком  язык.  В 
приложении  А  содержится  позаимствованный  из  книги  Павловской  [12] 
пример совершенно нечитаемой и тем не менее корректной для языка Си 
программы.  Поэтому  Си  нужно  с  осторожностью  использовать  для 
начального 
обучения 
программированию. 
Весьма 
популярной 
альтернативой для этого у нас в стране является Паскаль. 
Из-за  слабого  контроля действий  программиста  (таких  особенностей,  как 
ручное  управление  динамической  памятью —  отсутствия  сборки  мусора, 
не  существующей  автоматической  инициализации  переменных,  адресная 
арифметика,  отсутствия  контроля  выхода  индекса  за  пределы  массива,  и 
пр.)  при  программировании  на  Си  потенциально  совершается  больше 
ошибок,  чем  при  программировании  на  Модуле-2,  Обероне  или  Аде.  Но 
несмотря  на  это  он,  как  кажется  автору,  неоправданно  популярен  при 
создании  критических  приложений,  например,  в  аэрокосмической 
промышленности  или  автоматизированных  системах  управления 
технологическими процессами. 
Своеобразный  промежуточный  уровень  языка  Си,  как  считают  многие 
исследователи,  не  позволяет  эффективно  применять  его  при  написании 
приложений,  требующих  высокого  уровня  абстракции,  например  систем 
искусственного  интеллекта.  Для  этих  областей  гораздо  лучше  подойдут 
Пролог или Лисп. 


122 
 
Язык ассемблера (автокод) 
Как мы уже знаем, ассемблеры возникли в качестве первых отличных от 
машинных  кодов  средств  программирования  и  относятся  к  второму 
поколению  языков  программирования.  Какой  же  смысл  сегодня,  в  XXI 
веке,  изучать  эту  древность?  Или  язык  ассемблера —  современное 
средство?  Разберем  этот  вопрос  подробнее.  Среди  языков 
программирования  ассемблер  ближе  всего  к  архитектуре  ЭВМ, 
следовательно, он требует от программиста досконального знания деталей 
реализации данной ЭВМ, особенностей архитектуры. Это позволяет писать 
эффективные  программы —  короткие,  быстрые,  не  требующие  большого 
объема памяти. Где они востребованы и востребованы ли? Ответ на этот 
вопрос —  востребованы,  и  эта  ситуация  будет  наблюдаться,  по  всей 
видимости, еще много лет. 
Как бы ни показалось удивительным некоторым читателям, но существуют 
применения ЭВМ, где доступные ресурсы до сих пор весьма ограниченны. 
Это,  например,  встроенные  микропроцессоры  и  микроконтроллеры.  А 
ведь  именно  они  представляют  собой  наиболее  многочисленный  класс 
современных ЭВМ, если подсчитать все используемые суперкомпьютеры, 
мэйнфреймы,  мини-ЭВМ  (рабочие  станции),  персональные  компьютеры 
и т. д.  Микроконтроллеры  повсюду —  в  телевизорах,  телефонах, 
микроволновых  печах,  автомобилях,  лифтах…  Другой  пример — 
системные  программы  (ядро  операционной  системы,  драйверы).  Можно 
привести  в  качестве  примера  также  большие  программные  комплексы, 
работающие в системах массового обслуживания, в которых имеется узкое 
«бутылочное  горлышко» —  фрагмент  программы,  наиболее  часто 
выполняющийся  и  критический  с  точки  зрения  производительности 
системы  в  целом.  Такой  фрагмент  целесообразно  писать  на  ассемблере, 
даже если остальная программная система использует Java или C#. 
Весьма  важный  аспект —  информационная  безопасность.  Вирусы  и 
вредоносные  программы,  программы-шпионы  используют  бреши  в 
уязвимости  компьютерных  систем  со  знанием  тончайших  деталей  и 
особенностей  архитектуры.  Для  того  чтобы  противостоять  им,  нужно 
обладать  не  меньшими  познаниями  и  искусством  программирования  на 
уровне  машины.  Неудивительно,  что  умение  программировать  на 
ассемблере —  своеобразный  знак  качества  программиста,  а  разработчики 
подобных  программ  окружены  ореолом  таинственности  как  носители 
тайного знания. Но это не каждому по плечу! 
Рассматривать  ассемблер  мы  будем  на  примере  программирования 
микропроцессоров Intel семейства x86. Данная архитектура не является ни 
наиболее  простой  для  изучения,  ни  наиболее  удобной  и  оптимальной  с 
точки 
зрения 
разработки 
программ. 
Настоящие 
ценители 


123 
 
программирования  на  ассемблере  помнят  изящество  архитектуры 
ассемблера  ЭВМ  разработки  компании  DEC  семейства  PDP-11 [13]. 
Причина  выбора  данного  языка  заключается  в  невероятном  количестве 
выпущенных  по  всему  миру  компьютеров,  основанных  на  архитектуре 
x86 — 
впрочем,  далеко  не  единственной  выпущенной  компанией  Intel,  в 
послужном  списке  которой  такие  замечательные  архитектуры,  как, 
например, iAPX 432 и Itanium. Из чего следует, что, во-первых, не должно 
быть  проблем  с  его  нахождением  и  проверкой  примеров,  а  во-вторых, 
читатель,  не  исключено,  сможет  извлечь  из  материала  некоторую 
практическую  пользу  для  себя.  Ведь,  как  показывает  многолетний  опыт 
преподавания,  учащиеся  не  любят  изучать  новые  языки  и  склонны  в 
практической работе на протяжении долгих лет использовать именно тот 
язык  программирования,  который  изучили  на  первом  курсе...  Ниже 
представлены этапы исторического развития процессоров Intel. 


Достарыңызбен бөлісу:
1   ...   55   56   57   58   59   60   61   62   ...   81




©engime.org 2024
әкімшілігінің қараңыз

    Басты бет