Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

книги / Программирование на языке Си

..pdf
Скачиваний:
15
Добавлен:
12.11.2023
Размер:
17.16 Mб
Скачать

272

Программирование на языке Си

Если программист "запускает" программу на языке Си из ин­ тегрированной среды разработки (например, Turbo С), то он редко использует командную строку и должен предпринимать специальные меры, чтобы увидеть эту строку или внести в нее нужную информацию. (В Turbo С нужно в главном меню вы­ брать пункт RUN, а затем в ниспадающем меню выбрать пункт ARGUMENTS...).

При запуске программы из операционной системы команд­ ная строка явно доступна, и именно в ней записывается имя вы­ полняемой программы. Вслед за именем можно разместить нужное количество "слов", разделяя их друг от друга пробела­ ми. Каждое "слово" из командной строки становится строкойзначением, на которую указывает очередной элемент параметра argv[i], где 0<i<argc. Здесь нужно сделать одно уточнение.

Как и в каждом массиве, в массиве argv[] индексация эле­ ментов начинается с нуля, т.е. всегда имеется элемент argv[0]. Этот элемент является указателем на полное название запускае­ мой программы. Например, если из командной строки выполня­ ется обращение к программе EXAMPLE из каталога CATALOG, размещенного на диске С, то вызов в MS-DOS выглядит так:

С :\CATALOG\EXAMPLE.EXE

Значение argc в этом случае будет равно 1, а строка, которую адресует указатель argv[0], будет такой:

"С:\CATALOG\ЕXAMPLE.EXE"

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

#include <stdio.h>

char

* argv

[ ])

void main (int argc,

{

i;

 

 

 

int

i++)

 

 

for

(i=0; i<argc;

-> %s",

i, argv [i];

printf("\n argv

[%d]

)

Глава 5. Функции

273

Пусть программа запускается из такой командной строки:

C:\WP\test66 11 22 33

Результат выполнения программы:

argv [0] -> C:\WP\test66.exe argv [1] -> 11

argv [2] -> 22 argv [3] -> 33

В главной программе main( ) разрешено использовать и тре­ тий параметр char * envp[ ]. Его назначение - передать в про­ грамму всю информацию об окружении, в котором выполняется программа. Следующий пример иллюстрирует возможности этого третьего параметра основной функции m ain().

#include <std±o.h>

char

*argv[], char *envp[])

void main (int argc,

{

 

 

 

int n ;

'%s'

"

 

printf("\пПрограмма

при запуске: %d",

”\пЧисло параметров

argv[0], argc-1);

 

for (n=l; n<argc; n++)

 

%s", n, argv[n]);

printf ("\n%d-ii параметр:

printf("\п\пСписок переменных окружения:"); for (n=0; envp[n]; n++)

printf("\n%sM, envp[n]);

>

Пусть программа "запущена" на выполнение в MS-DOS под Windows 95 из такой командной строки:

C:\WWP\TESTPROG\MAINENVP.EXE qqq www

Результаты выполнения программы:

Программа 'С :\WWP\TESTPROG\MAINENVP.EXE ’

Число паргшетров при запуске: 2

1-й параметр: qqq

1 8 -3 1 2 4

274

Программирование на языке Си

2-й параметр: vrww

Список переменных окружения:

ТМР*С:\WINDOWS\TEMP

PROHPT=$p$g

winbootdir=C:\WINDOWS

COMSPEC—С :\WINDOWS\COMMAND.COM

TEMP»c:\windows\temp

CLASSPATH».;e:\cafe\java\lib\classes.zip

HOMEDRIVE=c:

HOMEPATH»\cafe\java

JAVA_HOME»c:\cafe\java

windir»C:\WINDOWS

NWLANGUAGE»English

PATH»C:\CAFE\BIN;C:\CAFE\JAVA\BIN;C:\BC5\BIN; C :\WINDOWS;C :\WINDOWS\COMMAND;C :\NC;

C :\ARC;C :\DOS;C :\VLM;C :\TC\BIN

CMDLINE»tc

Через массив указателей envp[] программе доступен набор символьных строк, образующих окружение программы. Чаще всего каждая строка содержит такую информацию:

имя_переменной = значение^переменной

Например, в приведенных результатах имя переменной ТМР связано с именем каталога для временных данных. Изменив значение переменной ТМР, можно "заставить" программу ис­ пользовать для хранения временных данных другой каталог. Для работы со строками окружения в программах на языке Си используют функции getenvO и putenvO-

Не рассматривая смысл остальных переменных окружения, выведенных программой mainenvp.exe, отметим, что перемен­ ные окружения широко используются при исполнении про­ грамм в операционной системе Unix.

Глава 6

СТРУКТУРЫ И О БЪ Е Д И Н Е Н И Я

6.1. С труктурн ы е ти п ы и структуры

Производные типы. Из базовых типов (и типов, определен­ ных программистом) можно формировать производные типы, к которым относятся: указатели, массивы, функции, структуры и объединения. Указатели, массивы, структуры и объединения являются производными типами данных. Иногда в качестве производного типа в руководствах по языку Си указывают стро­ ки, однако напоминаем, что в соответствии с синтаксисом языка строковый тип в нем отсутствует. Строка определяется как ча­ стный случай одномерного массива, т.е. как массив с элемента­ ми типа char, последний элемент которого равен '\0'.

Указатели и массивы подробно рассмотрены в предыдущих главах. Эта глава посвящена структурам и объединениям. Пре­ жде чем рассматривать их особенности и возможности, введем некоторые терминологические соглашения стандарта, которые не всегда понятны сами собой.

Данные базовых типов (int, float, ...) считаются скалярными данными. Массивы и структуры являются агрегирующими ти­ пами данных в отличие от объединений и скалярных данных, которые относятся к неагрегирующим типам. С массивами и скалярными данными мы уже хорошо знакомы, поэтому разли­ чие между агрегирующими и неагрегирующими типами пояс­ ним на примере массивов и скалярных данных базовых типов. Обычно агрегирующий тип включает несколько компонентов. Например, массив long В[12] состоит из 12 элементов, причем каждый элемент имеет тип long. Язык Си позволяет определять и одноэлементные массивы. Так, массив float А[1] состоит из одного элемента А[0]. Однако одноэлементные массивы есть слишком частный случай обыкновенных массивов со многими элементами. Итак, в общем случае массив есть агрегирующий тип данных, состоящий из набора однотипных элементов, раз-

18*

276

Программирование на языке Си

мещенных в памяти подряд в соответствии с ростом индекса. Для агрегирующего типа данных (например, для массива) в ос­ новной памяти ЭВМ выделяется такое количество памяти, что­ бы сохранять (разместить) одновременно значения всех элементов. Этим свойством массивов мы уже неоднократно пользовались. Например, следующее выражение позволяет вы­ числить количество элементов массива В[ ]:

sizeof (В) / sizeof (В [0])

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

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

название товара (char*);

оптовая (закупочная) цена (long);

торговая наценка в процентах (float);

объем партии товара (int);

дата поступления партии товара (char [9]).

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

Глава 6. Структуры и объединения

277

struct

goods {

/* Наименование

*/

char*

name;

long price;

/* Цена оптовая

*/

float percent;

/* Наценка в % */

int vol;

/* Объем партии

*/

char date [9];

/* Дата поставки партии */

};

Здесь struct - спецификатор структурного типа (служебное слово); goods - предложенное программистом название (имя) структурного типа. (Используя транслитерацию английского термина "tag", название структурного типа в русскоязычной ли­ тературе по языку Си довольно часто называют тегом.) В фи­ гурных скобках размещаются описания. элементов, которые будут входить в каждый объект типа goods. Итак, формат опре­ деления структурного типа таков:

struct имя структурногоjnuna

{ определения элементов };

где struct - спецификатор структурного типа;

имя структурного типа - идентификатор, произвольно выбираемый программистом;

определенияэлементов - совокупность одного или более описаний объектов, каждый из которых служит прототипом для элементов структур вводимого структурного типа.

Следует обратить внимание, что определение структурного типа (в отличие от определения функции) заканчивается точкой с запятой.

Конструкция struct имя структурного типа играет ту же роль, что и спецификаторы типов, например, double или int. С помощью struct goods можно либо определить конкретную структуру (как, например, объект структурного типа struct goods), либо указатель на структуры такого типа. Пример:

/* Определение структуры с именем food */ struct goods food;

/* Определение указателя point_to на структуры*/ struct goods *point_to;

278

Программирование на языке Си

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

Еще одну возможность ввести структурный тип дает служеб­ ное слово typedef, позволяющее ввести собственное обозначе­ ние для любого определения типа. В случае структурного типа он может быть введен и поименован следующим образом:

typedef struct {определения элементов) обозначение структурного типа;

Пример:

typedef struct

{double real; double imag;

}

complex;

Приведенное определение вводит структурный тип struct {double real; double imag;} и присваивает ему обозначение (название, имя) complex. С помощью этого обозначения можно вводить структуры (объекты) так же, как с обычным именем структурного типа (например, struct goods в предыдущем при­ мере). Пример:

/* Определены две структуры: */ complex sigma, alfa;

Структурный тип, которому программист назначает имя с помощью typedef, может в то же время иметь второе имя, вво­

Глава 6. Структуры и объединения

279

димое стандартным образом после служебного слова struct. В качестве примера введем определение структурного типа "рациональная дробь". Будем считать, что рациональная дробь - это пара целых чисел (m;n), где число п отлично от нуля. Опре­ деление такой дроби:

typedefrstruct rational_fraction

{ int numerator;

/* Числитель */

int denominator; /* Знаменатель */ } fraction;

f.

Здесь fraction - обозначение структурного типа, вводимое с помощью typedef. Имя rational_fraction введено для того же структурного типа стандартным способом. После такого опре­ деления структуры типа "рациональная дробь" могут вводиться как с помощью названия fraction, так и с помощью обозначения того же типа struct rational_ffaction.

С помощью директивы #define можно вводить имена типов подобно тому, как это делается с помощью typedef. Например, сведения о книге могут быть введены таким образом:

#define BOOK struct \

\

 

{ char

author[20];

/* Автор */

\

char

title [80] ;

/* Название

*/

int year;

/* Год издания

*/ \

>

Здесь BOOK - препроцессорный идентификатор, вслед за которым в нескольких строках размещена "строка замещения". Обратите внимание на использование символа продолжения 'V для переноса строковой константы. Кроме того, отметим отсут­ ствие точки с запятой после закрывающей скобки ’}'. Далее в программе можно определять конкретные объекты-структуры или указатели с помощью имени BOOK, введенного на препроцессорном уровне:

BOOK ss, *pi;

После препроцессорных замен и исключения комментариев это предложение примет вид (с точностью до размещения по строкам):

280

Программирование на языке Си

struct

{

char author[20]; char title [80]; int year;

}

ss, *pi;

(Смотрите ниже определение структур и определение указате­ лей на структуры.)

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

Из нашего примера определения структурного типа с назва­ нием goods следует, что наименование товара будет связано с указателем типа char*, имеющим имя паше. Оптовая цена еди­ ницы товара будет значением элемента типа long с названием price. Торговая наценка будет значением элемента типа float с именем percent и т.д. Все это следует из приведенного опреде­ ления структурного типа с названием goods. Но прежде чем элементы, введенные в определении структурного типа, смогут получить значения, должна быть определена хотя бы одна структура (т.е. структурированный объект) этого типа. Напри­ мер, следующее определение вводит две структуры, т.е. два объекта, типа goods:

struct goods coat, tea;

Итак, если структурный тип определен и известно его имя, то формат определения конкретных структур ^объектов структур­ ного типа) имеет вид:

struct имя структурного типа списокструктур;

где список структур - список выбранных пользователем имен (идентификаторов).

Глава 6. Структуры и объединения

281

Выше показано, что для структурного типа, имя которого введено с помощью служебного слова typedef, определение структур не должно содержать спецификатора типа struct. На­ пример, указатель на структуры для представления комплекс­ ных чисел с помощью определенного выше обозначения структурного типа complex можно определить так:

complex *p_comp;

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

/* Две структуры: */

struct rational_fraction ratio_l, ratio_2;

/* Две структуры: */ fraction zin, rat_X_Y;

Кроме изложенной последовательности определения струк­ тур (определяется структурный тип, затем с использованием его имени определяются структуры) в языке Си имеются еще две схемы их определения. Во-первых, структуры могут быть опре­ делены одновременно с определением структурного типа:

struct имя структурного типа { определения элементов } список структур;

Пример одновременного определения структурного типа и структур (объектов):

struct'student

{

char паше [15]; /* Имя */

char surname [20] ; /* Фамилия */ int year; /* Курс */

} student_l, student_2, student_3;

Здесь определен структурный тип с именем student и три конкретные структуры student_l, student_2, student_3, которые являются полноправными объектами. В каждую из этих трех

Соседние файлы в папке книги