Отправляет email-рассылки с помощью сервиса Sendsay

C и C++ для начинающих

  Все выпуски  

C и C++ для начинающих


Служба Рассылок Subscribe.Ru проекта Citycat.Ru

Доброго времени суток! Сегодня речь пойдет о базовых правилах языка.

1. Комментарии в языке С заключаются в пары символов /* и /. Все, что расположено
между ними, игнорируется компилятором. Вложенные комментарии не допускаются:

        / Какие-то комментарии /* Еще комментарии */ И еще комментарии /

        ^ Здесь комментарии                А здесь ^  ^ Синтаксическая
          начинаются                 заканчиваются      ошибка

Комментарии в С могут быть многострочными.


2. Сторонники Паскаля! Запомните: язык С ЧУВСТВИТЕЛЕН К РЕГИСТРУ! MyIdent и
myident - это два разных идентификатора (да, ИДЕНТИФИКАТОР - это набор букв,
цифр и знака подчеркивания, начинающийся с буквы или знака подчеркивания и не
совпадающий с ЗАРЕЗЕРВИРОВАННЫМ СЛОВОМ. О них зарезервированных словах) позже).


3. Я думаю, не существует такого языка, в котором нет переменных. ПЕРЕМЕННАЯ
- это некоторая область для хранения данных, с которыми работает программа.
Переменная характеризуется следующими параметрами: 1. ИМЕНЕМ - имя переменной
представляет собой идентификатор, по которому вы в дальнейшем будете обращаться
к ней; 2. ТИПОМ - тип переменной задает характер информации, хранящейся в ней,
например, целое число, дробное число, строка символов и т.д. 3. ОБЛАСТЬЮ ВИДИМОСТИ
- задается неявно и указывает на те части кода, при выполнении которых переменная
будет доступна; 4. Собственно значением переменной; 5. АДРЕСОМ переменной в
памяти. Последний пункт почему-то часто пропускают, хотя он является не менее
важнам, чем остальные - после компиляции программы все имена переменных, как
и функций (о них позже) будут убраны и заменены адресами. О роли адреса мы поговорим
позже.

ВСЕ ПЕРЕМЕННЫЕ В ЯЗЫКЕ С ДОЛЖНЫ БЫТЬ ОБЪЯВЛЕНЫ! Любители Бейсика, забудьте о
том, чтобы использовать большую кучу переменных без объявления! В принципе,
объявлять переменные до их использования - это просто правило хорошего тона.
Кроме того, оно позволяет компилятору разделить типы, переменные и функции.
Переменные в С объявляются следующим образом:

        [модификатор_типа] тип_переменной имя_переменной [= начальное значение]
                [, имя_переменной [= начальное_значение]... ];

Примеры:

        int a;
        int a = 5;
        int a, b;
        int a, b = 3;
        int a = 2, b = 3, c = 4;

Типов в языке С немного. Перечислю их все:

        void
        int
        char
        fload
        double

Все. Как видите, не такое уж и разнообразие. Но, как показывает практика, этих
пяти типов более чем достаточно. Более подроюно о каждом из них.

   void          Фиктивный тип. Размер значения типа void принимается
                 равным нулю, поэтому вы не можете объявлять переменные
                 такого типа. Тем не менее, этот тип находит широкое
                 применение, о котором позже.

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

   char          Разновидность типа int. Его размер составляет 8 бит, так что
                 чаще всего он используется для хранения символов. Однако,
                 ничего не запрещает вам хранить в нем целые числа, или, наоборот,
                 использовать для хранения символов тип int.

   float         Вещественное число одинарной точности (32 бита).

   double        Вещественное число двойной точности (64 бита).

Модификаторы типа:

   short         Явно указывает компилятору, что под данную переменную необходимо
отвести
                 2 байта, независимо от разрядности платформы. Я ни разу не
использовал
                 данный модификатор с типом, отличным от int, поэтому тут ничего
сказать не
                 смогу.

   long          Явно указывает компилятору, что под данную переменную необходимо
отвести
                 4 байта, независимо от разрядности платформы. Я ни разу не
использовал
                 данный модификатор с типом, отличным от int, поэтому тут ничего
сказать не
                 смогу.

   signed        Указывает компилятору, что данная переменная является знаковой
(то есть может
                 принимать как положительные, так и отрицательные значения).
Не может
                 использоваться с типами float и double.

   unsigned      Указывает компилятору, что данная переменная является беззнаковой
(то есть
                 может принимать только положительные значения). Не может использоваться
                 с типами float и double.

Типы short int, long int, signed int, unsigned int разрешается сокращать соответственно
до short, long, signed и unsigned.

Обратите внимание: тип char может являться как знаковым, так и беззнаковым,
в зависимости от конкретного компилятора. Поэтому, чтобы быть уверенным в допустимых
значениях, используйте типы signed char и unsigned char. Тип int по умолчанию
является знаковым.

Да, и еще: все указанные слова, а именно:

        int, char, float, double, void, short, long, signed, unsigned

- являются зарезервированными словами. То есть вы не можете объявить переменную
с именем char. Но если очень хотите - можете объявить ее с именем Char - как
я уже говорил, язык С чувствителен к регистру.


3. ВСЕ ЯВЛЯЕТСЯ ОПЕРАТОРОМ! Ну, или почти все :) Принципиальная разница С и
других языков в том, что в С все операторы равноправны. Скажем (для тех, кто
имеет некоторый опыт в программировании на других языках), что вызов функции
тоже являтеся оператором. Более того, каждый оператор (исключений
нет!) возвращает некоторое значение - результат своей работы. Ну, для оператора
сложения все понятно: оператор a + b возвратит сумму чисел a и b. А что возвращает
оператор присваивания (в С он записывается как = )? Оказывается, значение выражения
a = b равно a (или b, что неважно, так как
после выполнения оператора значения a и b равны). Эта на первый взгляд бессмысленная
возможность, во-первых, сильно упростила создание языка (нет необходимости разделять
операторы на возвращающие значение и нет, да еще проверять на правильность.
Например, на Паскале (кто знает) необхдимо специально проверять выражение на
предмет выражений вроде a := (b := c) ), а во-вторых, позволила сильно сократить
размер исходного текста программы. Например:

       a = b = c = d = 0;

как будет работать такой участок кода? Начнем сначала. Данное выражение присваивает
переменной a результат выполнения оператора b = c = d = 0, который, в свою очередь,
присваивает переменной b результат выполнения оператора c = d = 0, который...
То есть этот участок кода как бы развертывается в такой:

       d = 0;
       c = d;
       b = c;
       a = b;

Итак, как мы видим, всем четырем переменным присвоено начальное значение всего
одним оператором. Впечатляет? :)

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

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

       Примечание: в С истиной (true) считается всякое ненулевое значение, ложью
(false) -
                   нуль.

       a = b          Присваивает переменной a значение переменной b и возвращает
                      присвоенное значение.
       a + b          Возвращает сумму операндов.
       a - b          Возвращает разность операндов.
       a  b          Возвращает произведение операндов.
       a / b          Возвращает частное операндов. Обратите внимание: частное
двух
                      целых чисел является целым числом! Например, 9 / 2 = 4.
       a % b          Возвращает остаток от деления a на b.
       a << b         Возвращает a * 2^n (2^n здесь - n-ная степень числа 2).
       a >> b         Возвращает a / 2^n (2^n здесь - n-ная степень числа 2).
       a & b          Возвращает результат побитовой операции "и" (надеюсь все
знают,
                      что это такое. Если кто не знает - пишите, в следующий
раз
                      объясню) над операндами.
       a | b          Возвращает результат побитовой операции "или" над операндами.
       a ^ b          Возвращает результат побитовой операции "исключающее или"
над
                      операндами.
       ~a             Возвращает результат побитовой операции "не" над операндом.
       a == b         Возвращает true, если значения переменных a и b равны,
и false
                      в противном случае.
       a < b          Возвращает true, если значения переменной a меньше, чем
b, и false
                      в противном случае.
       a > b          Возвращает true, если значения переменной a больше, чем
b, и false
                      в противном случае.
       a && b         Возвращает true, если оба операнда содержат true, и false
                      в противном случае (логическое И).
       a || b         Возвращает true, если хоть один из операндов содержат
true,
                      и false в противном случае (логическое ИЛИ).
       !a             Возвращает true, если a содержит false, и false в противном
                      случае (логическое НЕ)

       Для следующих операторов я приведу их альтернативный вариант записи:

       a != b         ! (a == b)
       a <= b         ! (a > b)
       a >= b         ! (a < b)

       a += b         a = a + b
       a -= b         a = a - b
       a = b         a = a  b
       a /= b         a = a / b
       a %= b         a = a % b
       a &= b         a = a & b
       a |= b         a = a | b
       a ^= b         a = a ^ b
       a <<= b        a = a >> b
       a >>= b        a = a << b
       a++            a = a + 1
       ++a            a = a + 1
       a--            a = a - 1
       --a            a = a - 1

Обратите внимание на префиксную (++a, --a) и постфиксную (a++, a--) форму записи
последних операторов. Хоть все они имеют наивысший приоритет, но операторы ++a
и --a выполнятся в первую очередь, а a++ и a-- - в последнюю:

       int a = 1,                                int a = 1,
           b = 2,                                    b = 2,
           c;                                        c;

       c = a + ++b;                              c = a + b++;

       /* c = 4, a = 1, b = 3 /                 / c = 3, a = 1, b = 3 */



4. Следующей структурной единицей после выражения является ФУНКЦИЯ. Функция
- это некоторый участок кода, который выполняет некоторые действия и возвращает,
как и оператор, некоторое значение - результат своей работы. В отличие от операторов,
функции могут не возвращать значения. Такие функции ценны своим побочным эффектом
- например, функция clrscr очищает экран (в DOS), но, как можно догадаться,
ничего при этом не возвращает (считается, что в этом случае функция возвращает
тип void. Вот вам и одно из обещанных применений). Функция описывается так:

       тип_возвращаемого_значения имя_функции ([список_параметров])
       {
            описание_локальных_переменных

            операторы

            return возвращаемое_значение;
       }

Список параметров выглядит так:

      тип_параметра имя_параметра[, тип_параметра имя_параметра[, ...]]

Или так:

      void

Если после списка параметров следует многоточие (например: int myfunc (char
a, ...)), то после всех описанных параметров может следовать любое количество
любых параметров.

Обратите внимание на такое описание функции:

      int myfunc ()

С будет трактовать такое описание как int myfunc (...), а С++ - как int myfunc
(void).

Пример: функция, возвращающая значение полинома 3*x*x + 2*x + 1:

       int polynom (int x)
       {
          int a, b, c;

          a = 3  x  x;
          b = 2 * x;
          c = 1;

          return a + b + c;
       }

Эту же функцию можно переписать несколько покороче:

       int polynom (int x)
       {
          return 3*x*x + 2*x + 1;
       }

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

      return;

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

      int func1 (int a);

      int func2 (int a)
      {
          return func1 (a);
      }

      int func1 (int a)
      {
          return func2 (a);
      }

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

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


5. В программе обязательно должна присутствовать функция int main (void). Именно
с нее начинается выполнение программы. Когда функция main завершает свою работу,
программа прекращает выполнение и выставляет переменную errorlevel (кто не знает
- читайте описание DOS) (или код возврата, что одно и то же) на значение, возвращенное
функцией.

6. Функции могут вызывать друг друга. Пример (с написанной ранее функцией):

      int a, b;

      a = 4;
      b = myfunc (a);

Рассмотрим еще один пример.

      void myfunc (void)
      {
           /* Неважно, что она делает /
      }

      int main (void)
      {
           myfunc ();
           return 0;
      }

Сторонники Паскаля! Обратите внимание на разницу между mufunc и myfunc(). myfunc
- это ФУНКЦИЯ, а myfunc() - это РЕЗУЛЬТАТ ЕЕ РАБОТЫ. Поэтому если вы напишете:

      myfunc;

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

Функция может вызывать саму себя (т.н. рекурсия). Часто это бывает полезно.
Пример:

      int func (int a)
      {
          return a  func (a - 1);
      }

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

Функции в С совершенно равноправны. И функция main тоже может вызываться другими
функциями:

      int func (int a);

      int main (void)
      {
          return func (5);
      }

      int func (int a)
      {
          return main ();
      }

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

7. В языке с нет предопределенных функций. Каждая используемая функция должна
быть где-то определена до ее использования. Но самому писать многие требуемые
функции (ввод-вывод, файлы, и т.д.) каждый раз несколько скучно :) К счастью,
С снабжен библиотекой, содежращей в себе множество разнообразных функций. Для
того, чтобы использовать ее, подключите к вашей программе требуемый ЗАГЛОВОЧНЫЙ
ФАЙЛ (он имеет расширение .h) к вашей программе следующей строкой:

      #include <имя_файла.h>

На место этой строки в программу при компиляции будет позставлено содержание
указанного файла.

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

      printf ("Hello, world\n");
      printf ("%d + %d = %d\n", i, i, i + i);

С первым примером все ясно. Сложнее со вторым. printf принимает в качестве первого
параметра строку, в которой на месте последовательностей %x, где х - одно из:
d, с, f, s, печатаются параметры по очереди в формате (соответственно) целого
числа, символа, вещественного числа или строки символов. \n в конце строки трансформируется
в символ "новая строка".



Домашнее задание:

  1. Еще раз внимательно прочитать программу "Hello, world" из первого выпуска
и понять ее.
  2. Разобраться, что получится в результате выполнения следующего участка кода:

       int a = 1, b = 2, c = 3, d = 4, e = 5, f, g = 6, h = 8, i;

       f = (++a + b * (c = d) - (g == h));
       i = 3 < 2 < 1;

  3. Напишите программу решения квадратного уравнения, считая, что уравнение
невырождено и имеет корни.

За сим вроде все. Тема следующего выпуска - управляющие конструкции языка. Да
встречи!

Ведущий рассылки, av

http://subscribe.ru/
E-mail: ask@subscribe.ru

В избранное