пятница, 28 февраля 2014 г.

Операторы отношения и логические операторы

Операторами отношения являются
> >= < <=
Все они имеют одинаковый приоритет. Сразу за ними идет приоритет операторов сравнения на равенство:
== !=
Операторы отношения имеют более низкий приоритет, чем арифметические, поэтому выражение вроде i < lim - 1 будет выполняться так же, как
i < (lim - 1).

Более интересны логические операторы && и ||. Выражения, между которыми стоят операторы && или ||, вычисляются слева направо. Вычисление прекращается, как только становится известна истинность или ложность результата.

for (i = 0; i < lim - 1 && (с = getchar()) != EOF && с != '\n'; ++i)
  s[i] = c;

Прежде чем читать очередной символ, нужно проверить, есть ли для него место в массиве s, иначе говоря, сначала необходимо проверить соблюдение условия i < lim - 1. Если это условие не выполняется, мы не должны продолжать вычисление, в частности читать следующий символ. Так же было бы неправильным сравнивать с и EOF до обращения к getchar; следовательно, и вызов getchar, и присваивание должны выполняться перед указанной проверкой.

Приоритет оператора && выше, чем таковой оператора ||, однако их приоритеты ниже, чем приоритет операторов отношения и равенства. Из сказанного следует, что выражение вида
i < lim - 1 && (с = getchar()) != '\n' && с != EOF
не нуждается в дополнительных скобках. Но, так как приоритет != выше, чем приоритет присваивания, в (с = getchar()) ! = '\n' скобки необходимы, чтобы сначала выполнить присваивание, а затем сравнение с '\n' .

По определению численным результатом вычисления выражения отношения или логического выражения является 1, если оно истинно, и 0, если оно ложно.

Унарный оператор ! преобразует ненулевой операнд в 0, а нуль в 1. Обычно оператор ! используют в конструкциях вида
if (!valid)
что эквивалентно
if (valid == 0)

четверг, 27 февраля 2014 г.

Арифметические операторы

Бинарными (т. е. с двумя операндами) арифметическими операторами являются +, -, *, /, а также оператор деления по модулю %.

Деление целых сопровождается отбрасыванием дробной части.

#include <stdio.h>
int main(void)
{
    int a = 17;
    int b = 3;
    float c = 0;
    c = a / b;
    printf("%f\n", c);

    return 0;
}
$ ./a.out
5.000000

Тогда как 17 / 3 = 5,6666666666666666666666666666667

Картина меняется, если одну из переменных (либо float a = 17, либо float b = 3) опредлеить как float. Тогда
$ ./a.out
5.666667

Выражение x % y дает остаток от деления x на y и 0 (нуль), если x делится на y нацело.

Например, год является високосным, если он делится на 4, но не делится на 100. Кроме того, год является високосным, если он делится на 400.

#include <stdio.h>
int main(void)
{
    int year;
    for (year = 2004; year <= 2014; ++year)
    {
        if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0)
            printf("%d високосный год\n", year);
        else
            printf("%d невисокосный год\n", year);
    }
    return 0;

}

Оператор % к операндам типов float и double не применяется.

1. #include <stdio.h>
2. int main(void)
3. {
4.     printf("17.0 % 5.0 = %d\n", 17.0 % 5.0);
5.     return 0;
6. }

test.c: In function 'main':

test.c:4:38: error: invalid operands to binary % (have 'double' and 'double')

В какую сторону (в сторону увеличения или уменьшения числа) будет усечена дробная часть при выполнении / зависит от машины.
В моем случае, в сторону уменьшения.

Каким будет знак результата операции % с отрицательными операндами, так же, зависит от машины. В моем случае знак "-".

#include <stdio.h>
int main(void)
{
    signed int x = -8;
    signed int y = -5;
    printf("-8 %% -5 = %d\n", x % y);

    return 0;
}
$ ./a.out
-8 % -5 = -3

Бинарные операторы + и - имеют одинаковый приоритет, который ниже приоритета операторов *, / и %, который в свою очередь ниже приоритета унарных операторов + и -. Арифметические операции одного приоритетного уровня выполняются слева направо.

Объявления

Все переменные должны быть объявлены раньше, чем будут использоваться. Объявление специфицирует тип и содержит список из одной или нескольких переменных этого типа.
int lower, upper, step; 
char с, line [1000];
int i;
В своем объявлении переменная может быть инициализирована.
int i = 0; 
int limit = MAXLINE + 1;
Инициализация неавтоматической переменной осуществляется только один раз — перед тем, как программа начнет выполняться, при этом начальное значение должно быть константным выражением. Явно инициализируемая автоматическая переменная получает начальное значение каждый раз при входе в функцию или блок, ее начальным значением может быть любое выражение. Внешние и статические переменные по умолчанию получают нулевые значения. Автоматические переменные, явным образом не инициализированные, содержат неопределенные значения ("мусор").

К любой переменной в объявлении может быть применен квалификатор const для указания того, что ее значение далее не будет изменяться.
const double e = 2.71828182845905; 
const char msg[] = "предупреждение: ";
Применительно к массиву квалификатор const указывает на то, что ни один из его элементов не будет меняться. Указание const можно также применять к аргументу-массиву, чтобы сообщить, что функция не изменяет этот массив:
int strlen(const char[]);
Реакция на попытку изменить переменную, помеченную квалификатором const, зависит от реализации компилятора.

1. #include <stdio.h>
2. int main(void)
3. {
4.     const double e = 2.71828182845905;
5.     printf("Число Эйлера: %lf\n", e);
6.
7.     e = 2.718282; /* Попытаемся изменить переменную */
8.
9.     return 0;
10. }

При компилации выдается ошибка о том, что переменная e назначена только для чтения.

test.c: In function 'main':
test.c:7: error: assignment of read-only variable 'e'

вторник, 18 февраля 2014 г.

Константы

При рассмотрении данной темы я буду использовать оператор sizeof, который возвращает размер в байтах объекта или типа данных.
$ vim test.c
#include <stdio.h>

#define INTEGER 1       /* целая константа, нет окончания */
#define LONG 1L         /* константа типа long */
#define UNLONG 1UL      /* константа типа unsigned long */
#define LONGLONG 1LL    /* константа типа long long */
#define FLOAT 1.0F      /* константа типа float */
#define DOUBLE 1.0      /* константа с плавающей точкой, без окончания, тип double */
#define LONGDBL 1.0L    /* константа типа long double */

int main()
{
    printf("Размер типа \"INTEGER\", байт - %d\n", sizeof(INTEGER));
    printf("Размер типа \"LONG\", байт - %d\n", sizeof(LONG));
    printf("Размер типа \"UNSIGNED LONG\", байт - %d\n", sizeof(UNLONG));
    printf("Размер типа \"LONG LONG\", байт - %d\n", sizeof(LONGLONG));
    printf("Размер типа \"FLOAT\", байт - %d\n", sizeof(FLOAT));
    printf("Размер типа \"DOUBLE\", байт - %d\n", sizeof(DOUBLE));
    printf("Размер типа \"LONG DOUBLE\", байт - %d\n", sizeof(LONGDBL));

    return 0;
}
$ cc -g -O0 -Wall -o a.out test.c
$ ./a.out
Размер типа "INTEGER", байт - 4
Размер типа "LONG", байт - 4
Размер типа "UNSIGNED LONG", байт - 4
Размер типа "LONG LONG", байт - 8
Размер типа "FLOAT", байт - 4
Размер типа "DOUBLE", байт - 8
Размер типа "LONG DOUBLE", байт - 12

Целое значение помимо десятичного может иметь восьмеричное или шестнадцатеричное представление.
$ vim test_2.c
#include <stdio.h>

#define OCTAL 037
#define HEX 0x1F

int main(void)
{
    printf("Восьмиричная константа %d\n", OCTAL);
    printf("Шестнадцатеричная константа %d\n", HEX);

    return 0;

}
$ cc -g -O0 -Wall -o a.out test_2.c
$ ./a.out
Восьмиричная константа 31
Шестнадцатеричная константа 31

Символьная константа есть целое, записанное в виде символа, обрамленного одиночными кавычками. Значением символьной константы является числовой код символа из набора символов на данной машине. Например, символьная константа '0' в кодировке ASCII имеет значение 48, которое никакого отношения к числовому значению 0 не имеет.
$ vim test_3.c
#include <stdio.h>

#define SYMB '0'

int main(void)
{
    printf("Символ: %c\n", SYMB);
    printf("Код ASCII: %d\n", SYMB);

    return 0;
}
$ ./a.out
$ cc -g -O0 -Wall -o a.out test_2.c 
Символ: 0
Код ASCII: 48

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

Некоторые символы в символьных и строковых константах записываются с помощью эскейп-последовательностей, например \n (символ новой строки); такие последовательности изображаются двумя символами, но обозначают один. Кроме того, произвольный восьмеричный код можно задать в виде
'\ooo'
где ooo — одна, две или три восьмеричные цифры (0…7) или
'\xhh'
где hh — одна, две или более шестнадцатеричные цифры (0…9, а…f, А…F).
$ vim test_4.c
#include <stdio.h>
/* Восьмеричный код */
#define VTAB '\013'     /* вертикальная табуляция в ASCII */
#define BELL '\007'     /* звонок в ASCII */
int main(void)
{
    printf("%c", VTAB);
    printf("%c\n", BELL);
    return 0;
}
$ vim test_5.c
#include <stdio.h>
/* Шестнадцатеричный код */
#define VTAB '\xb'     /* вертикальная табуляция в ASCII */
#define BELL '\x7'     /* звонок в ASCII */
int main(void)
{
    printf("%c", VTAB);
    printf("%c\n", BELL);
    return 0;
}

Набор эскейп-последовательностей:
сигнал-звонок
\b возврат-на-шаг (забой)
\f перевод-страницы
\n новая-строка
\r возврат-каретки
\t горизонтальная-табуляция
\v вертикальная-табуляция
\\ обратная наклонная черта
\? знак вопроса
\’ одиночная кавычка
\” двойная кавычка
\ooo восьмеричный код
\xhh шестнадцатеричный код

Символьная константа '\0' — это символ с нулевым значением, так называемый символ NULL. Вместо просто 0 часто используют запись '\0', чтобы подчеркнуть символьную природу выражения, хотя и в том и другом случае запись обозначает нуль.
$ vim test_6.c
#include <stdio.h>
#define NULL_1 '\0'
#define NULL_2 NULL
int main(void)
{
    if (NULL_1 == NULL_2){
        printf("\'\\0\' == null\n");
        printf("%d\n", NULL_1);
        printf("%d\n", NULL_2);
    }
    return 0;

}
$ cc -g -O0 -Wall -o a.out test_6.c
test_6.c: In function 'main':
test_6.c:9:2: warning: format '%d' expects type 'int', but argument 2 has type 'void *'
$ ./a.out
'\0' == null
0
0

Константные выражения — это выражения, оперирующие только с константами. Такие выражения вычисляются во время компиляции, а не во время выполнения.
$ vim test_7.c
#include <stdio.h>
#define OFFSET 1
int main(void)
{
    int massiv [5 + OFFSET]; /* константное выражение */
    int i;
    massiv[0] = 0;
    massiv[1] = 1;
    massiv[2] = 2;
    massiv[3] = 3;
    massiv[4] = 4;
    massiv[5] = 5;
    printf("За пределами массива %d\n", massiv[6]);
    for (i = 0; i < 6; ++i)
            printf("%d ", massiv[i]);
    printf("\n");
    return 0;

}
$ cc -g -O0 -Wall -o a.out test_7.c

$ ./a.out
За пределами массива -1215582220
0 1 2 3 4 5

Строковая константа, или строковый литерал, — это нуль или более символов, заключенных в двойные кавычки, как, например,
"Я строковая константа"
или
"" /* пустая строка */
Кавычки не входят в строку, а служат только ее ограничителями. Так же, как и в символьные константы, в строки можно включать эскейп-последовательности. Строковые константы можно конкатенировать ("склеивать") во время компиляции.

Фактически строковая константа — это массив символов. Во внутреннем представлении строки в конце обязательно присутствует нулевой символ '\0', поэтому памяти для строки требуется на один байт больше, чем число символов, расположенных между двойными кавычками. Это означает, что на длину задаваемой строки нет ограничения, но чтобы определить ее длину, требуется просмотреть всю строку.

В Си имеется еще один вид константы — константа перечисления. Перечисление — это список целых констант.
$ vim test_8.c
#include <stdio.h>
int main(void)
{
    /* значения констант неспецифицированы,
     * значения констант генерируются автоматически начиная с 0 */
    enum boolean {NO, YES};
    printf("NO - %d\n", NO);
    printf("YES - %d\n", YES);

    /* все значения констант специфицированы */
    enum escapes {BELL = '\a', BACKSPACE = '\b', TAB = '\t',
    NEWLINE = '\n', VTAB = '\v', RETURN = '\r'};

    /* Прогрессия начинается от последнего специфицированного значения */
    enum months {JAN = 1, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC};
    printf("Значение FEB %d\n", FEB);

    return 0;
}
$ cc -g -O0 -Wall -o a.out test_8.c
$ ./a.out
NO - 0
YES - 1
Значение FEB 2

Имена в различных перечислениях должны отличаться друг от друга. Значения внутри одного перечисления могут совпадать.

среда, 12 февраля 2014 г.

Упражнение 2.1.

Напишите программу, которая будет выдавать диапазоны значений типов char, shortint и long, описанных как signed и как unsigned, с помощью печати соответствующих значений из стандартных заголовочных файлов и путем прямого вычисления. Определите диапазоны чисел с плавающей точкой различных типов. Вычислить эти диапазоны сложнее.

Write a program to determine the ranges of char, short, int, and long variables, both signed and unsigned, by printing appropriate values from standard headers and by direct computation. Harder if you compute them: determine the ranges of the various floating-point types.

вторник, 11 февраля 2014 г.

Типы и размеры данных

Базовые типы языка программирования Си:

char - единичный байт, который может содержать один символ;
int - целое, обычно отображающее естественное представление целых на машине;
float - число с плавающей точкой одинарной точности;
double - число с плавающей точкой двойной точности.

Имеется также несколько квалификаторов, которые можно использовать вместе с указанными базовыми типами. Например, квалификаторы short и long применяются к целым:
short int sh;
long int counter;
В таких объявлениях слово int можно опускать, что обычно и делается.

Размеры объектов типов short int, int и long int зависят от реализации компилятора, но должны быть соблюдены следующие правила:

  • значения типов short и int представляются по крайней мере 16 битами;
  • типа long — по крайней мере 32 битами;
  • размер short не больше размера int, который в свою очередь не больше размера long.

Квалификаторы signed (со знаком) или unsigned (без знака) можно применять к типу char и любому целочисленному типу. Значения unsigned всегда положительны или равны нулю и подчиняются законам арифметики по модулю
,
где n — количество битов в представлении типа. Так, если значению char отводится 8 битов, то unsigned char имеет значения в диапазоне от 0 до 255, a signed charот -128 до 127. Являются ли значения типа просто char знаковыми или беззнаковыми, зависит от реализации, но в любом случае коды печатаемых символов положительны.

Тип long double предназначен для арифметики с плавающей точкой повышенной точности. Как и в случае целых, размеры объектов с плавающей точкой зависят от реализации; float, double и long double могут представляться одним размером, а могут двумя или тремя разными размерами.

Именованные константы для всех размеров вместе с другими характеристиками машины и компилятора содержатся в стандартных заголовочных файлах <limits.h> и <float.h>.

Имена переменных

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

Большие (прописные) и малые (строчные) буквы различаются, так что х и X — это два разных имени.

Минимальные требования стандарта ANSI C на длину имен:
  • для внутренних имен - 31 символ;
  • для внешних имен - 6 символов.
Компиляторы могут поддерживать и имена большей длины.

Ключевые слова if, else, int, float и т. д. зарезервированы, и их нельзя использовать в качестве имен переменных. Все они набираются на нижнем регистре.