Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Почтовая рассылка

Подписчиков: -1
Последний выпуск: 19.06.2015

ГЛАВА 3 Быстрый старт пакета Quick-C.

В данной Главе даются основы программирования на языке СИ в сжатой форме. Она разработана для того, чтобы вы как можно скорее начали программировать на языке СИ.

Предполагается, что вы уже знакомы с программированием на языках Pascal или BASIC. Если вы являетесь абсолютным новичком в программировании, для изучения программирования и языка СИ вы можете одновременно пользоваться как данной Главой, так и книгами, перечисленными ниже.

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

Каждый раздел сначала определяет и описывает данную тему (как, например, форматный ввод/вывод), затем переходит к краткой схеме данной операции. Каждый раздел заканчивается примером СИ-программы или программного сегмента, освещающим данную тему.

Рекомендуемое изучение языка СИ.

В дальнейшем, вы можете работать с языком СИ с помощью следующей литературы:

- Hagcock, Les, Morris Krieger. "The C Primer, 2d ed." New York: McGraw-Hill, 1985 (Руководство по языку СИ для начинающих программистов).

- Kochan, Stephen. "Programming in C". Hasbrouck Heights, NJ: Hayden Book Company, Inc., 1983. (Исчерпывающее руководство по языку СИ с некоторым погружением в среду UNIX).

- Plum,Thomas. "Learning to Program in C". Cardiff, New Jersey: Plum Hall, Inc., 1983 (Широко известное руководство по программированию на языке СИ для колледжей).

- Schildt, Herbert. "C Made Easy". Berkely, CA: Osborne/McGraw- -Hill, 1985 (Хорошее введение в язык СИ для тех, кто уже знает BASIC). - *Waite, Mitchell, Stephen Prata and Donald Martin. "C Primer Plus". Indianopolis, IN: Howard W. Sams. Inc., 1984 (Пользующееся наибольшим спросом введение в язык СИ).

* Есть русский перевод в издательстве "Мир". Примечание переводчика.

3.1.Структура программы на языке СИ.

Все программы на языке СИ содержат директивы препроцессора, описания, определения, выражения, операторы и функции.

-Директива препроцессора.

Директива препроцессора-это команда препроцессора языка СИ (который автоматически вызывается на первом шаге компиляции программы). Две наимболее общих директивы препроцессора-это директива #define, которая подставляет текст вместо заданного идентификатора, и директива #include, которая включает в программу текст внешнего файла.

-Описания.

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

-Определение.

Определение устанавливает содержимое переменной или функции. Опеределение также отводит память, требуемую для переменных и фунций. -Выражение.

Выражение-это комбинация операций и операндов, которое принимает единственное значение.

-Оператор.

Операторы управляют порядком выполнения программы на языке СИ. -Функция.

Функция-это набор описаний, определений, выражений и операторов, которое выполняет определенную задачу. Тело функции заключено в фигурные скобки. В языке СИ функции могут быть вложены друг в друга. -Функция main (главная).

Все программы на языке СИ, там где начинается выполнение программы, имеют функцию с именем main. Фигурные скобки, в которые заключено тело функции main, определяют начало и конец программы.

-Пример: Основная структура СИ-программы.

                       /* директивы препроцессора */
#include <stdio.h>     /* подключение стандартного заголовочного
                       файла СИ */
#define PI 3.14        /* определение символьной константы */
float area;            /* глобальное описание */
int square (int);      /* прототип функции */
main()


{                      /* начало функйии main и программы */
    int_radius squared; /* локальное описание */
    int radius = 3;    /* описание и инициализация */
    radius_squared = square (radius); /* передача значения функции */
    area = PI * radius_squared; /* оператор присваивания */
    printf ("area: %6.2f\n", area);
}                       /* конец функции main и программы */
square (r)              /* заголовок функции */
   int r_squared;       /* описания, известные только */
                        /* для функции square */
   r_squared = r * r;
   return (r_squared);  /* возврат значения в вызывающее выражение */
}

3.2 Описания.

Описания устанавливают имена и типы переменных, а также типы функций, определяемые где-либо.

-Стандартные типы данных.

Язык СИ имеет стандартный набор типов данных, куда входят: символьные (char), целые (int) и с плавающей точкой (float). -Описания переменных и функций.

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

-Создание новых наименований типов данных.

Для создания новых наименований типов для уже существующих типов данных используйте описание typedef. Применение описаний typedef может сделать программы более мобильными и надежными.

Определяемый тип может обозначать тип операции, разрешенной с данной переменной (например, логической операции). Испольщуйте ключевое слово typedef для защиты ваших программ в проблемах переносимости. Применяйте описание typedef для тех типов данных, которые являются машинно-зависимыми. Тогда, при перемещении программы будут требовать изменения только описание typedef. Наконец, typedef можно использовать для лучшего документирования вашей программы. Например, тип данных "employee" (служащий) легче понять, чем что-то описанное в виде сложной структуры. В нижеследующей таблице приведены несколько типичных имен typedef и соответствующие значения:

Имя typedef            Значение

ulong                  длинное целое без знака
bool                   целое (используемое для условий нуль/не нуль)
string                 указатель на символьное значение
-Пример: Описания и определения типов в языке СИ.
#include <stdio.h>
typedef unsigned long ulong; /* типы, определяемые программистом */
typedef unsigned short ushort;
typedef int bool;
typedef char *string;
/* typical function declaration:
   function name -- get_avg
   return value -- float
   expected parameters -- two integers
*/
float get_avg (int, int); /* прототип функции */
main()
{
   int matrix [3] [3]; /* описание 2-х мерной матрицы */
   struct date {   /* описание структуры */
        int month;
        int day;
        int year;
   };
   /* однажды созданные, имена typedef могут быть использованы в
      дальнейшем */
   bool true = 1;
   string example string;
   example_string = "text";
   printf ("string: %s\n",exampleestring);
   printf ("bool: %d\n",true);
}

3.3 Директивы препроцессора и включаемые файлы.

Тексты внешних файлов могут быть включены в СИ-программу посредством директивы препроцессора #include. Препроцессор автоматически вызывает на первом шаге компиляции. Он интерпретирует директивы препроцессора и затем посылает компилятору модифицированный СИ-файл.

Директива #include требует, чтобы препроцессор подставил содержимое внешнего файла на место строки, содержащей директиву.

-Заголовочные файлы языка СИ.

Заголовочные СИ-файлы обычно содержат общие переменные и описания функций. Такие файлы можно включить в исходный текст программы, чтобы использовать содержащиеся в них переменные или функции. Внешние файлы, подключаемые директивой #include носят также название "заголовочных" файлов, которые для удобства имеют в операционной системе DOS расширение .h.

Директива #include-это команда препроцессора. Символ # должен быть первым значащим символом в строке. Строка не должна заканчиваться точкой с запятой. Чаще всего директива #include ставится в начале программы перед функцией main.

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

Управление поиском подключаемого файла.

Маршрут поиска определяется форматом в директиве #include, используемым для заголовочного файла.

Разрешены три следующих формата:

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

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

- Явное имя маршрута задается в любой форме (внутри двойных ка- вычек или угловых скобок), оно определяет единственный маршрут для поиска файлов.

Пример: Подключение внешних файлов.

#include <stdio.h> /* стандартные заголовочные файлы процедур ввода-
                      -вывода из стандартного каталога */
#include <mach.h>  /* заголовояный файл математической функции */
#include <graph.h> /* заголовочный файл графических процедур */
#include "local.h" /* локальный заголовочный файл ищется в том ката-
                      логе, в котором расположен исходный код */
#include "c:\test\math87.h" /* заданный файл ищется только на дис-
                    ководе C и только в подкаталоге "test" */
{
      /* тело СИ-программы */
}

3.4. Описания переменных.

Все переменные перед использованием следует описать. Они не должны быть предварительно проинициализированы. Неинициализированные переменные содержат непредсказуемые значения.

-Типы данных.

Описания содержат спцификатор типа, за которым следует список переменных, данного типа. Переменные в списке должны отделяться друг от друга запятыми.

-Область действия.

Местоположение описания переменной опеределяет сферу влияния переменной.

Глобальные переменные описываются вне функций. К ним можно обращаться из любого места внутри исходного файла, в котором они описаны. Они также доступны из других исходных модулей. Глобальные переменные обычно описываются в начале программного текста перед функцией main и после директив #include.

Локальные переменные описываются внутри функции и известны только в той функции, в которой они описаны.

Локальные переменные всегда описываются в начале функции перед выполняемыми операторами.

-Требования к памяти.

Ниже приведены требования к памяти для основных типов данных:

Тип            Память в байтах   Диапозон

char           1 байт             от -128 до 127
int
short          2 байта           от -32,768 до 32,767
long           4 байта           от -2,147,482,648 до 2,147,482,647
unsined char   1 байт            от 0 до 255
unsined
unsined short  2 байта           от 0 до 65,535
unsined long   4 байта           от 0 до 4,294,967,295
float          4 байта           от +3.4E-38 до 3.4E+38
double         8 байт            от +1.7E-308 до 1.7E+308

Примечание: Если требования к памяти или диапозон не определены, значит они зависят от конкретного применения.


Зависят от применения типы int и unsigned. Для семейства микропроцессоров 8086 и 80286 тип int эквивалентен типу short, а тип unsigned-типу unsigned short.

-Регистровые переменные.

Регистровые переменные-это целое или указатель, хранимые в машинном регистре. Такая память ускоряет выполнение программы посредством увеличения скорости доступа к переменным.

Если не имеется свободных регистров, описания е будет сделано, но регистр не будет использоваться для хранения.

Пример: Описания переменных.

#include <stdio.h> /* стандартный заголовочный СИ-файл */
float pi=3.14159; /* глобальное описание и инициализация */
typedef unsigned short ushort /* unhort-это теперь синоним для типа
                                 unsigned short */
main()
{
    int i = 0; /* локальное описание и инициализация целого
                  (integer) */
    ushort limit; /* переменная limit имеет тип ushort, что эквива-
                     лентно unsigned short */
    register int j; /* хранение переменной j в машинном регистре */
/* тело функции main, следующее за описаниями */
}


function1()
{
     int x=47; /* описание и инициализация целого X известного
только
                  внутри функции function1 */
...тело функции function1, следующее за описаниями.

3.5. Операторы, выражения и операции.

Оператор языка СИ состоит из ключевых слов, управляющих порядком выполнения программы, (таких как for или while), выражений и вызовов функций. "Выражение" представляет собой комбинацию операндов и операций, которые при вычислении дают единственное значение. "Операция" показывает, какие действия производить с операндами. "Операнд"-это константа, либо переменная, входящая в выражение.

-Формат оператора.

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

-Составные операторы.

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

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

В языке СИ, присваивания считаются выражениями.

-Операция присваивания.

Операция присваивания определяет, что значение операнда с правой стороны помещается в память по адресу, задаваемому операндом с левой стороны.

-Операции.

Язык СИ поддерживает более 40 операций, начиная от основных арифметических операций до логических и поразрядных операций. Операции языка СИ дают результат, который может быть вложен внутрь большого выражения. Операции также могут быть объединены с оператором присвоения (=) для формирования составного оператора присваивания в следующей форме:

    X+=Y;

-Преобразования типов.

Явное приведение типов может быть сделано с помощью операции приведения (cast), которая представляет собой тип, заключенный в скобки. В нижеследующем переменная i (предварительно описанная, как int) преобразуется в тип float:

    (float)i

-Операции приращения и уменьшения.

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

      Используйте эти операнды, как описано ниже:
      if (i-->0) /* i сравнивается с нулем, затем i уменьшается */
      printf ("сравнивается, затем уменьшается");
      if (--i>0) /* i уменьшается, затем сравнивается с нулем */
      printf ("уменьшается, затем сравнивается")

      -Примеры: Некомментированные СИ-выражения.
         while ((c=getchar())!=EOF)
Пример, приведенный выше, является вложенным выражением, вызывает функцию getchar, которая возвращает целое. Затем, результат запоминается в переменной C, и сравнивается с переменной EOF, что дает в результате либо "ложь", либо "истинну". Данный результат определяет, будет ли выполняться тело цикла while.

    a=b=7;

В данном двойном операторе присваивания, значение 7 запоминается в переменной b, затем значение b (которое равно 7) запоминается в переменной a.

      x+=7;
      a<<=b;

Первый составной оператор увеличивает x на 7, второй составной оператор сдвигает a влево на b разрядов.

3.6. Описание и определение функций.

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

-Описание функции.

Описание функции состоит из типа возвращаемого значения, имени функции и необязательного списка аргументов. Заголовок функции состоит из типа данных, возвращаемых функций, за которым следует имя функции и список формальных параметров. Тело функции состоит из локальных описаний и составного оператора (или "блока"), который определяет, что делает данная функция.

-Типы возвращаемых значений.

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

Стандартный тип возвращаемого значения-это int. Вам не следует писать функции, возвращающие структуру или смесь. Огромный размер таких данных замедляет выполнение программы.

-Список типов аргументов и прототипы функций.

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

Формальные параметры в описании функции являются переменными, которые получают значения аргументов, переданные функции. -Передача аргументов функции.

Все аргументы (за исключением массивов) передаются значением. То есть функции передается копия данного аргумента (а не его адрес). В результате, СИ-функция не изменяет содержимое переданной ей переменной. Изменяется это правило передачи параметра значением только когда аргумент, передаваемый функции-это адрес. В этом случае, указатель косвенно разрешает функции изменять содержимое переменной с данным адресом.

Пример: Описание и определение функции.

#include <stdio.h> /* описания стандартных функций */
#include <stdlib.h> /* содержит описание прототипа atof */
typedef char *string;
main()
{
   string ascii_number;
   float float_number;
   ascii_number = "-6.02E-23";
   printf ("string: %s\n", ascii_number);
   /* следующая строка генерирует предупреждающее сообщение компиля-
      тора */
   float_number = atof(); /* слишком мало аргументов */
   float_number = atoy (ascii number); /* корректный вызов */
   printf ("number: %e'n", float_number);
}
Приведенная выше СИ-программа показывает, как использлвание прототипа функции может предупредить вас о некорректном вызове функции. Функция atof преобразует символьную строку в число с плавающей точкой. Прототип функции содержащийся в файле stdlib.h определяет, что функция принимает один аргумент.

3.7. Операторы цикла.

В языке СИ операторы for и while обеспечивают возможности повтрорения выполняемых операторов.

-Оператор for.

Оператор for используется для повторения какого либо оператора или составного оператора определенное количество раз. Он состоит из трех частей:

1. Выражение, стартующее цикл ( init-expr).

2. Текстовое выражение (cond-expr), вычисляемое перед каждой итерацией.

3. Выражение цикла (loop-expr), выполняемое в конце каждой итерации. Формат оператора for приведен ниже:

      for([init-expr];[cond-expr];[loop-expr])statement
Сначала вычисляется init-expr. Затем, пока вычисляемое выражение cond-expr не равно нулю, выполняется оператор statement. В конце цикла вычисляется выражение loop-expr. Как только выражение cond-expr получает значение 0, управление передается на оператор, следующий за телом цикла for.

Каждое выражение в операторе цикла for может быть любым корректным выражением языка СИ. Любое из трех или все три выражения могут быть опущены. Если выражения опущены, точки с запятой должны оставаться. Несколько выражений могут быть помещены в оператор for, но должны отделяться друг от друга запятыми. Типичный пример использования одновременно нескольких операторов-это инициализация нескольких значений в части init-expr цикла for, как показано ниже:

    for(i=1, j=1; i<=100; i++) /* инициализация i и j */
-Оператор while.

Оператор цикла while состоит из текстового выражения (test-expr), которое вычисляется перед телом выполняемого цикла. Если test-expr при вычислении дает "ложь", цикл никогда не выполняется. Формат оператора while следующий:

      while ([test-expr])
      statement
Тело цикла while состоит из оператора или составного оператора. Если тестовое выражение дает при вычислении "истину", тело цикла выполняется до тех пор, пока выражение не станет "ложным".

-Окончание работы операторов for и while.

Обычно, операторы for и while завершают работу, как только тестовое выражение в цикле примет значение "ложь". Если необходимо прервать цикл раньше, можно воспользоваться операторами break, goto или return. Оператор continue прерывает интерацию без выхода из цикла и передает контроль следующей итерации оператора for или while.

Пример: Использование циклов for и while.

#include <stdio.h>
main()
{
   int i, done;
   printf ("table of squres (every sixth number)\n");
   printf ("\nfor loop\n");
   printf ("number\t\tsquare\n");
   for (i=0; i<=20; i+=6)
      printf ("%d\t\t%d\n",i, i*i);
   printf ("nwhile loop\n");
   printf ("number\t\tsquare\n");
   i = 0;
   while (i <= 20) {
      printf ("%d\t\t%d\n",i, i*i);
      i += 6;
      }
   printf ("nwhile (nonzero test expression version)\n");
   printf ("number\t\tsquare\n");
   i = 0;
   done = 0;
   while (!done) {   /* будет выполняться пока done не получит
                        значение 1 */
      printf ("%d\t\t%d\n",i, i*i);
      i += 6;
      if (i > 20)
         done = 1;
   }
}
Программа, приведенная выше использует циклы for и while для вычисления таблицы квадратов для каждого 6-числа между 0 и 20.

3.8. Условные операторы и операторы перехода по условию.

В языке СИ функции условий и переходов выполняют операторы if и switch.

-Оператор if.

Если тестовое выражение в операторе if получает значение "истина", выполняется тело оператора if. В противном случае, программа продолжается со следующего оператора. Оператор if может иметь также операторную скобку else. Однако, поскольку в языке СИ нет конструкции "else if", для достижения того же самого эффекта используйте вложенные операторы if. Без явного указания, язык СИ спаривает каждое else с ближайшим предыдущим if, которому не хватает else. Компилятору будет "яснее", если вы заключите группу операторов в фигурные скобки. Нижеследующие примеры показывают типичные операторы if:

      if (x < 0)
         printf ("Square root operation invalid'n");
      if (x >= 0)
         {
         answer = sqrt (x);
         printf ("square root is: %6.2f\n", answer);
         }
-Оператор switch.

Оператор switch заменяет большое количество вложенных конструкций if и else.

Оператор switch передает управление оператору внутри своего тела. Управление переходит на оператор, для которого константное выражение case соответствует (целая либо символьная константа, или константное выражение) значению тестового выражения оператора switch. Выполнение начинается с выбранного оператора и продолдается до конца тела оператора switch, или до тех пор, пока какой-либо оператор не передает управление за пределы оператора switch. Для прерывания выполнения определенной ситуации оператора switch используйте оператор break. Без оператора break программа перейдет к следующей ситуации.

Оператор default выполняется в том случае, если соответствия константного выражения case и тестового выражения оператора switch не существует. Если оператор default опущен, и не найдено соответствия константному выражению case, ни один из операторов в теле оператора switch не выполняются.

Никакие две константы case в одном операторе switch не могут иметь одинаковых значений. Ниже приведен пример использования оператора switch.

Пример:

switch (i) {
   case 1:
           printf ("number 1\n");
           break; /* продолжается после закрывающей фигурной
                     скобки  */
   case 2:
   case 4:


      printf ("even'n"); /* выполняется, если i==2 или i==4 */
      break; /* продолжается после закрывающей фигурной скобки */
   default:
           /* печатается, если i не равно 1,2 или 4 */
           printf ("number not in our test list'n");
   }
-Пример: Использование операторов if и switch.

#include <stdio.h>
#include <ctype.h> /* необходимо для функции преобразования
                       toupper */
main()
{
   char response [10];
   char test_char;
   printf ("Please enter your response (yes/no/quit): ");
   scanf ("%s" response); /* форматный ввод в строку */
   if (toupper(response[0]) == 'Q') { /* тест для q */
    printf ("program exit\n"); /* выполнить, если равно q */
    exit(1);
   }
   switch (response[0]) { /* переключатель по первому символу */
      case 'y': /* множественные метки case */
      case 'n':
         printf ("lowercase y or as first letter\n");
         break;
     default:
         printf ("not a lowercase y or n as first letter\n");


      break;
}
test_char = toupper(response[0]); /* преобразование в верхний
                                     регистр */
switch (test char) {
   case 'Y':
      printf ("Response is yes\n");
      break;
   case 'N':
      printf ("Response is no\n");
      break;
   default:
      printf ("Poease enter a yes or no\n");
  }
}

3.9. Массивы и строки.

"Массив"-это набор элементов данных одиного типа. "Строка"-это массив символов, оканчивающийся символьным нулем ('\0'). -Типы массивов.

Основной тип элементов массива определяет и тип массива. Тип массива может быть любым, разрешенным в языке СИ, включая, так называемые "агрегатные" или составные типы, например структуры.

-Размещение в памяти массивов.

Элементы массива хранятся в последовательных областях памяти, причем по возрастанию адресов от первого элемента к последнему. Массивы хранятся по строкам. (То есть, сначала хранятся все колонки первой строки, затем все колонки второй строки и т.д.). Имя массива без скобок-это указатель на первый элемент массива. Начальный элемент массива имеет номер 0.

-Строки.

В языке СИ строка-это массив символов, оканчивающийся пустым символом ('\0'). Массивы, представляющие собой строки должны резервировать память для хранения нулевого окончания.

Пример: Использование массивов.

#include <stdio.h> /* стандартный заголовочный файл */
typedef char *string; /* переименование типа "char" в тип "string"
*/
int strlen (string); /* прототип функции */
/* описание и инициализация символьного массива test string */
char test string [] = "This is a C string";
main()
{
   int x;
   x = strlen1 (test string);
   printf ("length (array method): %d\n",x);
}
/* версия strlen (массива описателя) */


strlen1 (s)
char s[];
{
   int i = 0;
   while (s[i] != '\0')
      i++;
   return (i);
}
Программа, приведенная выше, вычисляет длину строки путем подсчета количества символов до нулевого окончания.

3.10. Указатели.

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

-Создание указателей.

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

Ниже приведены некоторые примеры описания указателей:

int*intptr; /* указатель на тип int */
char*name; /* указатель на тип char */
-Требования к памяти для указателей.

Количество памяти, требуемое для размещения указателя-это количество байт, требуемых для задания машинного адреса. В семействе микропроцессоров 8086 "короткие" указатели требуют 16 разрядов, а "длинные" указатели-32 разряда.

-Операторы для указателей.

Символ звездочки (*)-это оператор косвенного обращения к объекту, который означает "указать на данные". Символ амперсенда (&)-это оператор адресации. Если объекту предшествует оператор адресации, то это-адрес, по которому хранится данный объект.

-Указатели и функции.

Указатели обеспечивают передачу аргументов функции по ссылке. Если функции передаются массиву, передается только адрес начального элемента. -Действия с указателями.

С указателями разрешается производить только некоторые действия: сложение указателя и целого числа (наращивание значения указателя); вычитание из указателя целого числа ( уменьшение указателя); вычитание двух указателей (вычитание количества элементов между ними); сравнение двух указателей.

Если указатель увеличивается или уменьшается на единицу целого типа, язык СИ измеряет значение этого целого по размеру адресуемого объекта. Это гарантирует то, что увеличение на единицу, например, всегда будет указывать на следующий элемент данных, независимо от размера элемента. -Пример: Функция измерения длины строки с

использованием указателей.

/* версия strlen с увеличением указателя */
strlen1 (s)
char *s;
{
   int i;
   for (i=0; *s != '\0'; s++) /* увеличение указателя переменной s */
      i++;
   return (i);
}
/* версия strlen с вычитанием указателя */
strlen2 (s)
char *s;
{
   char *p = s; /* установка p для адресации первого символа s */
   while (*p != '\0')
     p++;   /* продвижение к следующему символу */
   return (p-s);
}
Две функции, приведенные выше, могут быть подставлены на место функции вычисления длины строки, описываемой в Разделе 3.9., "Массивы и строки". Вместо массива для вычисления длины строки, передаваемой функции, эти функции используют действия с указателем.

3.11. Указатели на функции.

Указатель может быть описан для адресации любого сложного объекта, включая структуру или функцию. Используйте указатели на функции для создания "родовых" функций, которые манипулируют данными любого типа. -Создание указателей на функции.

Описывайте указатель на функцию, как показано ниже:

    int(*number compare) ();

Тип переменной number compare-это "указатель на функцию, которая вызвращает целое". Скобки вокруг *number compare обязательны. Без них, язык СИ будет использовать свои правила порядка выполнение действий и интерпретирует строку, как описание функции, возвращающей указатель на элемент типа int-что совсем не то же самое.

-Использование указателей на функции.

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

С указателями на функцию следует использовать только операторы присвоения, сравнения и косвенного наименование (*); все другие операции не определены.

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

#include <stdio.h>
#include <ctype.h>
main()
{
   int number_compare ();
   int string_compare ();
   char s1[80], s2[80];
   printf ("generic test for equality\n");
   printf ("enter first item:\n");
   gets (s1);
   printf ("enter second item:\n");


   gets (s2);
   if (isaipha (*s1))
      compare (s1, s2, string_compare); /* передача адреса */
   else
      compare (s1, s", number_compare); /* передача адреса */
}
compare a, b, compare_function)
char *a, *b;

/* указатель на функцию, возвращающую целое значение */
int 8*compare_function) ();
{
   /* использование оператора (*) для вызова конкретной функции

      сравнения */
   if ((*compare_function) (a,b))
      printf ("equal\n");
   else
      printf ("not equal\n");
}
number_compare (a,b)
char *a, *b;
{
   if (atoi (a) == atoi (b))
      return (1);
   else
      return (0);
}


string_compare (a,b)
char *a, *b;
{
if (stricmp(a,b))
   return (0);
else
   return (1);
}

3.12. Структуры.

"Структура"-это набор логически связанных элементов данных различных типов.

-Создание структур.

При определении данных типа "структура" задаются элементы типов данных структуры и создается сама структура. Отдельные переменные в структуре называются "члены", а имя структуры называется "тэг" структуры. Данные типа structure описываются в следующей форме: struct structure-name { member-declarations }; Структура date,например, создается по следующему определению:

      struct date
         {
           int month;
           int day;
           int year;
         };
После того, как структура определена, вы можете описать переменную уже определенного типа. Тэг структуры должен следовать за ключевым словом struct.

Следующие ниже описания определяет переменную todayc date типа struct date:

    struct date todays_date;

-Переменные в структурах.

Переменные, являющиеся элементом типа структуры, могут использоваться также, как и любые другие переменные. Оператор членства (.) определяет имя члена структуры и структуры, элементом которой он является. В приведенном ниже примере переменной month в структуре todays date присваивается значение 12:

    todays date.month=12;

-Структура и указатели.

Указатели на структуры описываются также, как и на другие типы данных. Для ссылки на член структуры, адресуемой с помощью указателя, в языке СИ используется символ (->).

-Действия со структурами.

Для переменных-структур разрешены только три операции: вы можете получить ее адрес с помощью оператора адресации (&); вы можете получить доступ к одному из ее членов; вы можете присвоить одну структуру другой посредством операции присвоения.

Пример: Использования структур.

#include <stdio.h>
#include <time.h>
main()
{
   struct ti *current_time;
   time_t long_time; /* значение времени*/
   time (&long time); /* получение количества секунд в long time */
   /* преобразование в структуру времени */
   current_time = localtime(&long_time);
   /* использование оператора выборки члена структуры для доступа
      к отдельному члену структуры */
   printf ("hour: %d'n", current_time->tm_hour);
}
Приведенные выше программа использует оператор выборки члена структуры для доступа к отдельным элементам структуры tm, которая содержит информацию о времени. Затем распечатывается значение текущего часа. Функция localtime не содержится в библиотеке процедур Quick-C. В результате, вам понадобится для компиляции вашей программы в среде Quick-C воспользоваться программным списком. Использование программных списков описывается в Разделе 6.1.

3.13 Использование функций ввода/вывода языка СИ.

Стандартая библиотека процедур СИ содержит необходимый для программиста на языке СИ полный набор функций ввода/вывода (I/O). -Функции ввод/вывод одного символа (getchar, putchar). Функция getchar получает следующий по порядку символ с клавиатуры и возвращает его в качесве значения. Функция putchar выводит один символ на экран.

-Форматный вывод (printf).

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

Управляющая последовательность       Символ

         \n                          сдвиг строки
         \t                          табуляция
         \'                          одинарная кавычка
         \''                         двойная кавычка
         \\                          обратный слэш
-Форматный ввод - scanf.

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

-Спецификации строки формата.

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

Спецификация           Значение
преобразования

%d                     десятичное целое
%u                     десятичное без знака
%o                     восьмиричное без знака
%x                     шестнадцатеричное без знака
%e                     экспонента
%f                     число с плавающей точкойа
%c                     один символ
%s                     строка символов
-Пример: Использование функций ввода/вывода языка СИ.
#include <stdio.h>
typedef char * string;
main()
{
   char c, j;
   int i;
   string item1[10], item2[10];
   float x;


   printf ("please enter a single character:\n");
   c = getchar();
   printf ("\tthe character just input was -- %c\n",c);
   printf ("nenter a digit, a string, a float, and a string: ");
   scanf ("%d %s %f %s", &i, item1, &x, item2);
   printf ("\n\ayou entered\n");
   printf ("%d\n%s\n%f\n%s", i, item2, x, item2);
   printf ("\n\nexample of conversion specifications:\n");
   printf ("decimal\toctal\thex\tcharacter\n");
   for (j = 65; j<=70; j++)
      printf ("%d\t%o\t%x\t%c\n", j,j,j,j);
}

3.14 Использование функция ввода/вывода в файл.

Стандартная библиотека языка СИ имеет полный набор необходимых программисту функций ввода/вывода для файлов.

-Структура FILE.

Стандартный заголовочный файл процедур ввода/вывода stdio.h содержит описание структуры FILE, которая определяет внутренний характер файла. -Открытие и закрытие файлов.

Перед использованием файл должен быть открыт с помощью стандартной библиотечной функции fopen. После использования файл должен быть закрыт посредством библиотечной функции fclose.

Функция fopen принимает внешнее имя файла в системе DOS и возвращает указатель файла, используемый в дальнейшем в вызовах функций ввода/вывода. Указатель файла-это адресный указатель на структуру типа FILE. Функция fopen определяет также режим работы с файлом:чтение, запись добавление.

Если вы делаете попытку открыть файл для записи или добавления в то время, как файл не существует, он по возможности будет создан. Если при открытии файла произошла ошибка, функция fopen возвращает нулевой указатель (со значением NULL).

Для лучшей сохранности файла при выходе из программы непременно следует использовать финкцию fclose.

-Односимвольный ввод/вывод в файл (fputchar, fgetchar). Функция fputchar помещает символ в заданный файл. Функция fgetchar получает символ из заданного файла. Значение возвращаемое функцией, равное EOF, означает, что был достигнут конец файла.

-Форматный ввод/вывод в файл (fprintf, fscanf).

Функция fprintf помещает отформатированный вывод в заданный файл. Форматирование производится в соответствии со строкой формата, которая действует аналогично тому, как было описано в функции printf. Функция fscanf считывает информацию из заданного файла, в соответствии со строкой формата, как и в функции scanf. Значение EOF, возвращаемое функцией, означает, что была сделана попытка прочесть конец файла.

Пример: Использование ввода/вывода в файл.

#include <stdio.h>
#include <ctype.h>


main()
{
   FILE *fopen(), *fp_out;
   char fn_in[12], fn_out[12]; /* отводится память для filename.ext*/
   printf ("enter name of input file: ");
   scanf ("%s", fn_in);
   printf ("enter name of output file: ");
   scar ("%s", fn_out);
   fp_out = fopen (fn_out, "w");
   fp_in = fopen (fn,in,"r");
   /* проверка для несуществующего выодного файла; выход если вызов
      функции fopen потерпит неудачу */
   if (fp_in == NULL) {
      printf ("No input file %s'n",fn'in);
      exit (1);
   }
   /* если функция fopen завершилась удачно, выполнение оставщейся
      части программы*/
   convert_file(fp in,fp out);
   printf ("\nFile %s cleaned up\n",fp_in);
   printf ("Output in file: %s\n",fn_out);
   fclose (fp_in);
   fclose (fp_out);
   return (o);
}
convert_file (input, output)
FILE *input, *output;
{
   int c;


   /* получение символов до достижения конца файла (EOF) */
   while ((c = getc (input)) "= EOF){
      if (isupper(c)) /* преобразование регистра */
         c = tolower (c);
      else
         c = toupper (c);
   fputc(c,output); /* вывод в выходной файл */
   }
}
Программа, приведенная выше, изменяет регистр всех символов в файле.

3.15 Доступ к аргументам командной строки в языке СИ.

Аргументы командной строки в языке СИ обеспечивают способ передачи аргументов к программе при ее выполнении.

-Основные аргументы командной строки.

В операционной системе DOS любая информация, появляющаяся после имени программы, воспринимается, как аргумент командной строки. Язык СИ обеспечивает механизм для доступа к этим аргументам. Функция main связывает аргументы командной строки с двумя параметрами. Данные аргументы кратко описываются ниже:

Аргумент               Описание


argc                   "Счетчик аргументов"-это количество аргументов
                       в командной строке. Завершающим аргументом
                       посредством argv[argc-1], поскольку массивы в
                       языке начинаются с нулевого элемента.
argv                   "Вектор аргументов"-это указатель на массив

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

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

#include <stdio.h>
main (argc,argv) /* функция main, использующая аргументы командной
                    строки */
char *argv[]; /* вектор аргументов */
int argc; /* счетчик аргументов */
{
   int i;
   printf ("argc: %d\n", argc); /* печать количества аргументов*/
   for (i = =; i < argc; i++) /* печать содержимого аргументов */
   printf ("argv [%d]: %s\n", i, argv[i]);
}
Помните, что несколько аргументов ва командной строке задаются в Меню Run с помощью пукта меню: Set Runtime Options-во время создания и запуска вашей программа.

Также, примите во внимание, что в операционной системе DOS версии 3.0 значение arg[0] содержит строку "C", а не имя программы. -Использование символов-шаблонов DOS.

При обычной компиляции, программа, полученная в результате, не распознает имя файла DOS с символами-шаблонами (* и ?). Для того, чтобы иметь возможность использовать шаблоны DOS, вам следует подвязать к объектному модулю вашей программы соответствующий файл mSETARGV.OBJ (где m означает соответствующий модуль памяти).

При расширении имени файла с шаблонами, все файлы, соответствующие данному имени, передаются программе. Если такого соответствия не найдено, аргумент передается литералом.

-Пример: Обработка командной строки.

#include <stdio.h>
#include <ctype.h>
main(argc,argv)
char *argv[];
int argc;
{
   FILE +fopen(), *fp_in, *fp_out;
   /* проверка на корректное число аргументов */
   if (argc != 3) {
     printf ("correct usage is:\n");
     printf ("convcase input-file output-file\n");
     exit(0);
   }
   fp_out = fopen (argv[2], "w");
   fp_in = fopen (argv[1],"r");


   /* проверка на несоответствующий входной файл; выход если fopen
      закончилась неудачно */
   if (fp_in == NULL) {
      printf ("input file error : %s\n",argv[1]);
      exit (1);
   }
   /* если функция fopen завершилась удачно, выполняется оставшаяся
      часть программы */
   convert_file(fp in,fp out);
   printf ("\nFile %s converted\n",argv[1]);
   printf ("Output in file: %s\n",argv[2]);
   fclose (fp_in);
   fclose (fp_out);
   return (1);
}
convert_file (input,output)
{
/*
   The contents of the convert_file function
   are the same as in Section 3.14
*/
}

Программа, приведенная выше, является модификацией примера программы из раздела 3.14, "Использование функций ввода вывода в файл". Она принимает имена файлов в качестве аргументов командной строки, где имя входного файла предшествует имени выходного файла.

Оставить комментарий

Комментарий:
можно использовать BB-коды
Максимальная длина комментария - 4000 символов.
 
Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог