Обращение к функциям на языке С и системным библиотекам — КиберПедия 

История создания датчика движения: Первый прибор для обнаружения движения был изобретен немецким физиком Генрихом Герцем...

Общие условия выбора системы дренажа: Система дренажа выбирается в зависимости от характера защищаемого...

Обращение к функциям на языке С и системным библиотекам

2019-10-30 113
Обращение к функциям на языке С и системным библиотекам 0.00 из 5.00 0 оценок
Заказать работу

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

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

Чтобы отменить в прототипах функций декорирование имен, в заголовочных файлах модулей используют оператор

 extern “C” {

<К именам между фигурными скобками не добавляются дополнительные символы>

  }

Фигурные скобки можно опускать, когда отмена выполняется только для одной функции:

extern "C" void Func(int,int);

Если заголовочный файл может использоваться как в модулях на С, так и на С++, используют условную компиляцию:

#ifdef __cplusplus

extern “C” {

#endif

 <прототипы функций, написанных на С>

#ifdef __cplusplus

}

#endif

Если заголовочный файл включен в модуль С препроцессор удалит из него строкe extern “C” { и закрывающую скобку.

 

1.3. Данные типа объединение (union)

1.3.1. Стандартные объединения

Разработчики первых языков высокого уровня ориентировались на применение компьютеров с небольшим (десятки килобайт) объемом оперативной памяти, операционные системы которых выполняли узкий круг функций (часто только загрузку модулей в ОЗУ и управление периферийными устройствами). Поэтому в этих языках предусматривались средства повышения эффективности использования ОЗУ путем поочередного размещения в одной области памяти различных данных. Причем это делалось средствами языка, а не обращением к операционной системе. Характерным примером являются общие (common) блоки памяти языка Фортран. Всем переменным (например, массивам А и В), размещенным в общем блоке, компилятор выделял одинаковый начальный адрес, поэтому программист после работы с массивом А мог ввести данные в массив В и работать с ним. Но при этом запись в массив В, естественно, уничтожала данные массива А.

В современных компьютерах объем ОЗУ увеличился на несколько порядков. Задача динамического распределения памяти для ее повторного использования возложена на операционную систему. Но средства, позволяющие рассматривать одну область памяти как разные типы данных, оказались по-прежнему полезными, хотя и для других целей. Поэтому они есть и в новых в языках. В Паскале это записи с вариантами, с которыми мы не стали знакомиться, а в С++, это объединения (данные типа union). 

Объединение объявляется аналогично структурам, отличаясь только словом union вместо struct:

union UNI1{

       char a[4];

       double dVar;

} * pUni, Uni 1 ={2.5};

Они используют такой же синтаксис для работы с полями:

Uni1.a[7]=0;

pUni ->a[0]=0xff;                   

Отличие от структур заключается в том, что все поля объединения имеют один начальный адрес. Таким образом, оператор pUni->a[0]=0xff записывает код  0xff и в первый элемент массива Uni1.aи в младший байт вещественного числа Uni1.dVar.

Для хранения объединения выделяется блок памяти, размер которого равен размеру большего из полей. В приведенном выше примере под переменную Uni1 будет выделено 8 байтов – размер поля dVar.

Замечание. При инициализации объединения указывают значение не большего, а первого из полей. В рассмотренном примере инициализация Uni1 ={2.5} выглядит так, будто инициализируется вещественное поле dVar. Но в действительности число 2.5 округляется до 2 и записывается в сh[0], остальные, неинициализированные байты заполнятся нулями.

Если мы попробуем указать имя поля, как показано ниже

union UNI1{

       char a[4];

       double dVar;

} *pUni,Uni1 ={ dVar =2.51};

то получим тот же результат. Инициализация переменных выполняется не компилятором, а программой, поэтому для инициализации элементов массива ch[] можно использовать выражения (в том числе и с использованием переменных, а не только констант). Компилятор организует вычисление программой выражения dVar =2.51 и округленный результат, число 2, по-прежнему запишется в Uni1.ch[0]. Обратите внимание на одну деталь ­– мы можем ожидать, что при вычислении l-значения dVar =2.51 сначала восемь байтов поля dVar заполнятся кодом числа 2.51, а уже потом заполнятся байты массива char a[4]. Но в Visual С++ этого не происходит, в старшие 4 байта поля dVar также пишутся нули. Как организует инициализацию Borland C++, можете проверить самостоятельно.  

 Мы уже решали задачу вывода 16-ричных кодов символов, из которых состоит вещественное число, при помощи переопределения типа указателя. Теперь вы можно решить ее, используя объединение UNI2:

union UNI 2{

       char С ode[8];

       double Number;

} U={0,0,0,0,0,0, 0xe8, 0x3f};

Можно составить программу, которая вещественное число записывает в поле U.Number, а коды байтов этого числа выводит из массива U.Code[]. Но достаточно просто объявить инициализированную переменную U и воспользоваться отладчиком Visual C++. Отладчик покажет и заданное выше содержимое поля Сode и равное 0.75 содержимое поля Number (аналогично другим вещественным типам старший бит старшего байта задает знак числа, следующие 11 битов – его смещенный порядок, после чего следует мантисса, в которой все разряды кроме старшего равны нулю). (Попробуйте в данном объявлении изменить порядок следования полей. Если поле Number будет первым, как выполнится инициализация U={0,0,0,0,0,0, 0xe8, 0x3f}?) 

Данный тип данных часто используют для объединения структуры и массива. Это позволяет обращаться к полям структуры по именам или считать эти поля элементами массива и обращаться к ним по индексам. (В последнем случае можно вычислить заранее неизвестный номер требуемого поля. При обращении по именам этого сделать нельзя.)

Например, в трехмерной графике необходимо выполнять операции с квадратными матрицами размером 4*4.

Чтобы в одних ситуациях к элементам матрицы обращаться по именам, а в других (например, при применении операторов цикла для умножения или сложения матриц) как двумерному массиву, можно объявить два варианта в виде объединения

Union

{

float m[4][4];

   struct A

   {

       float m00, m01, m02, m03;

       float m10, m11, m12, m13;

       float m20, m21, m22, m23;

       float m30, m31, m32, m33;

   }M; При знакомстве со структурами мы видели, что если здесь нет имени переменной, ошибки компиляции не будет, но и такой структуры в объединении не будет.

} D 3 D;

После этого к элементу, расположенному в первой строке и в первой строкематрицы D3D, можно обращаться двумя способами:

D3D.m[0][0]=0;

D 3 D. M. m 00=0;

Следует отметить, что второй вариант не так уж и удобен даже при обращении к конкретному элементу матрицы, потому что кроме имени матрицы D3D и имени поля mm0 приходитcя указывать не несущее никакой смысловой нагрузки имя структуры M. Но при обращении по имени компилятор указывает конкретный адрес элемента, а для определения адреса в массиве выполняются длинные команды умножения.

На практике мы можем встретить структуры данных, элементы которых в разных задачах (или в разных функциях одной задачи) имеют разный физический смысл. Для обеспечения понятности программы, «самодокументируемости» ее исходного текста, одно поле в этих ситуациях должно иметь разные имена. Это достигается включением поля в структуру в виде объединения нескольких однотипных полей с разными именами.

1.3.2. Анонимные объединения

Пусть для вывода на экран точек некоторого изображения объявлена структура POINT3D, определяющая цвет точки Color и ее координаты в плоскости экрана

Struct POINT3D

{

int Color;

union _KOORD

{

int Koord[2];

struct _D2

{ int x;

   int y;

} Record;

}uniD2;

}Point;

Координаты точки включены в POINT3D как объединение массива Koord и структуры Record. При этом мы получаем два варианта обращения к координатам, например,

Point.uniD2.Record.x=30;

Point.uniD2.Koord[0]=30;

В каждом из этих обращений приходится указывать составное имя uniD2.Record или uniD2.Koord, потому что имена полей Koord, Record видны только внутри объединения uniD2. В C++ введен специальный тип данных, анонимные объединения, позволяющий расширить область видимости полей за пределы объединения и избавиться в обращениях к полям от имени объединения. Анонимное объединение отличается тем, что в нем не указывают ни имени типа объединения ни имени переменной.

Чтобы в данном примере сделать объединение анонимным, удалим его тип _KOORD и имя uniD2:

Struct POINT3D

{

 int Color;

 union

{

int Koord[2];

struct _D2

{ int x;

int y;

} Record;

};

} Point;

Теперь поля объединения видны во внешнем по отношении к нему блоке и мы можем написать

Point. Record.x=30;

Point. Koord[0]=30;

Анонимные объединения не обязательно объявлять внутри структуры. Если объявить объединение в следующим образом

Void Function (void)

{

union

{ char cM;

long lM;

}

.

.

.

}

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

Static union

{ char cM;

  long lM;

}

void Function (void)

{ cM=’A’;

   .

 .

 .

}

1.3.3. Анонимные структуры в Visual C++

В стандартном С++ предусматриваются анонимные объединения, но нет анонимных структур. Из-за этого в п. 4.3.1. при работе c элементами матрицы D3D вместо естественного обращения

D3D.m00=0;

состоящего из имени матрицы и имени элемента мы вынуждены были указывать имя M структуры, которая объединяет элементы матрицы:

D3D.M.m00=0;

При указании координат точки экрана в 4.3.2

 Point. Record.x=30;

 Point. Record.y=30;

также указывалось не несущее смысловой нагрузки имя Record.

       Чтобы избавиться от этого недостатка фирма Microsoft ввела в свой диалект стандартного C++ анонимные структуры. Учитывая такую возможность, удалим в рассмотренном ранее (п. 4.3.1) объявлении матрицы имя структуры M:

Union

{

float m[4][4];

   struct   {             Нет имени типа.

       float m00, m01, m02, m03;

       float m10, m11, m12, m13;

       float m20, m21, m22, m23;

       float m 30, m 31, m 32, m 33;

   }                                 Нет имени переменной.

} D 3 D;

Теперь мы можем написать

float Sum = D 3 D. m 00 + D 3 D. m 11 + D 3 D. m 22;

Сделаем анонимной структуру Record в описании точки экрана:

Struct ROOM

{

int Color;

union

{

int Koord [2];

struct //_D3 Если здесь не указать тип, то потом можно не указывать и имени.

{ int x;

  int y;

}; //Record– удалили имя структуры.

};

}P3;

В этом случае мы также можем ограничиться указанием имени переменной и названия координаты:

P3.x=22;

P3.y=20;

1.4. Перечисляемый тип (enum)

      Перечисляемый тип (enumeration) - это множество поименованных це­лых констант. Перечисляемый тип определяет все допустимые значения, ко­торые могут иметь переменные этого типа. Основная форма объявления ти­па следующая:

          enum <имя_типа > {<список_имен> }

  Список имен может быть пустым. Пример определения пере­числимого типа и переменной данного типа:

enum COLOR { black, red, green, blue };

enum COLOR s=red;;

каждое из имен black, red, green, blue представляет собой константу целого типа, значение которой по умолчанию равно позиции имени в списке, т. е. black =0. Поэтому переменные перечисляемого типа и их значения совместимы с целыми типами.

Мы можем сделать объявление

int i=green;

или выполнить оператор

i=s;

Оператор

printf ("%d %d", black, blue);

выдаст на экран числа 0 и 3.

Значения black, red, green, blue ­– это имена констант, поэтому в одной области видимости нельзя объявить два перечисляемых типа, использующих одинаковые имена элементов (см. текст по Паскалю). В пределах видимости перечисления нельзя также объявлять переменные (структуры, вещественные и пр.) с именами black, red, green, blue.

 Во время объявления типа можно одному или нескольким символам присвоить другие значения, например:

enum Number {one=l, two, three, thousand=1000, th1,th2};

Тогда значение следующих элементов перечисления будут нарастать, начиная с этого значения. При печати элементов перечисления

printf ("%d %d %d %d \n", one, two, thousand, th1);

на экране появятся числа 1, 2, 1000, 1001, т. е. каждый следующий сим­вол увеличивается на единицу по сравнению с предыдущим, если не было явного присваивания.

С элементами перечисления можно производить все операции, которые могут производиться с константами целого типа. Основную причину использования перечислимого типа мы уже указывали при изучении Паскаля – это улучшение читаемости программ.  

Хотя и здесь приходиться быть осторожнее. Объявим переменную J:

enum COLOR { black, blue, green=2, red=4, White=0xf };

enum COLOR J;

Если аналогичное перечисление объявить Паскале и организовать цикл по элементам перечисляемого типа, мы получим цикл из пяти повторений.

В С++ цикл с заголовком for (J =Black; J< White; J++)... выполнится 16 раз.

Потому я таких циклов в С++ и не вижу – чаще просто именуют варианты при выборе ветвей в операторе switch.  

1.5. Организация ввода вывода

1.5.1. Консольные операции в среде MS DOS


Поделиться с друзьями:

Типы сооружений для обработки осадков: Септиками называются сооружения, в которых одновременно происходят осветление сточной жидкости...

Архитектура электронного правительства: Единая архитектура – это методологический подход при создании системы управления государства, который строится...

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

Состав сооружений: решетки и песколовки: Решетки – это первое устройство в схеме очистных сооружений. Они представляют...



© cyberpedia.su 2017-2024 - Не является автором материалов. Исключительное право сохранено за автором текста.
Если вы не хотите, чтобы данный материал был у нас на сайте, перейдите по ссылке: Нарушение авторских прав. Мы поможем в написании вашей работы!

0.064 с.