#include <stdio.h>
#include <string.h>
typedef struct A {
char Fio[31];
int God;
int Gruppa;
};
main()
{
struct A B;
strcpy(B.Fio,"Ivanow G.I."); B.God = 1977;
B.Gruppa = 384;
printf("Fio = %s\n",B.Fio);
printf("God = %d\n",B.God);
printf("Gruppa = %d\n",B.Gruppa);
}
В рассмотренном примере запись имеет следующую структуру:
struct A { /* A имязаписи} */
char Fio[31]; /* 1 поле записи */
int God; /* 2 поле записи */
int Gruppa; /* 3 поле записи */
}
Далее можем ввести структуру B аналогичную A покоманде
struct A B;
Далее можем в поля помещать конкретные данные, но мы должны уточнить, что поля принадлежат переменной B нотацией с точкой (B.Fio:=”Ivanow G.I.”; и т.д.).
С помощью ключа typedef структурам дается имя.
Пример можно переписать следующим образом.
#include <stdio.h>
#include <string.h>
typedef struct {
char Fio[31];
int God;
int Gruppa;
} A;
main()
{
A B;
strcpy(B.Fio,"Ivanow G.I.");
B.God = 1977;
B.Gruppa = 384;
printf("Fio = %s\n",B.Fio);
printf("God = %d\n",B.God);
printf("Gruppa = %d\n",B.Gruppa);
}
В примере мы рассмотрели одну запись, но в реальной жизни в группе не может быть одного студента, поэтому мы можем совместить массив и запись и создать массив записей. Рассмотримпример.
#include <stdio.h>
#include <string.h>
typedef struct A {
char Fio[31];
int God;
int Gruppa; };
main()
{
A WGruppa[12];
strcpy(WGruppa[1].Fio,"Ivanow G.I.");
WGruppa[1].God = 1977;
WGruppa[1].Gruppa = 384;
strcpy(WGruppa[2].Fio,"Petrow R.G.");
WGruppa[2].God = 1978;
WGruppa[2].Gruppa = 384;
printf("Fio1 = %s\n",WGruppa[1].Fio);
printf("God1 = %d\n",WGruppa[1].God);
printf("Gruppa1 = %d\n",WGruppa[1].Gruppa);
printf("Fio2 = %s\n",WGruppa[2].Fio);
printf("God2 = %d\n",WGruppa[2].God);
printf("Gruppa2 = %d\n",WGruppa[2].Gruppa);
}
Первоначально мы определили структуру A, а затем использовали ее при объявлении структуры WGruppa, как массива состоящего из 12 записей структуры B.
Теперь для адресации мы должны указать номер элемента массива и имя поля.
Существуют варианты, когда одна запись содержит другую запись, например, добавляется адрес, к рассмотренной выше записи студент. Пример.
#include <stdio.h>
#include <string.h>
typedef struct Adress {
char City[31];
char Street_Nd_Kw[61]; };
typedef struct A {
char Fio[31];
int God;
int Gruppa;
Adress D_addr; };
main()
{
A WGruppa[12];
strcpy(WGruppa[1].Fio,"Ivanow G.I.");
WGruppa[1].God = 1977;
WGruppa[1].Gruppa = 384;
strcpy(WGruppa[1].D_addr.City,"Shadrinsk"); strcpy(WGruppa[1].D_addr.Street_Nd_Kw,"Lenina 10 kw.1");
strcpy(WGruppa[2].Fio,"Petrow R.G.");
WGruppa[2].God = 1978;
WGruppa[2].Gruppa = 384;
strcpy(WGruppa[2].D_addr.City,"Kataisk"); strcpy(WGruppa[2].D_addr.Street_Nd_Kw,"Akulowa 1 kw.2");
printf("Fio1 = %s\n",WGruppa[1].Fio);
printf("God1 = %d\n",WGruppa[1].God);
printf("Gruppa1 = %d\n",WGruppa[1].Gruppa);
printf("City= %s\n",WGruppa[1].D_addr.City);
printf("Street= %s\n",WGruppa[1].D_addr.Street_Nd_Kw);
printf("Fio2 = %s\n",WGruppa[2].Fio);
printf("God2 = %d\n",WGruppa[2].God);
printf("Gruppa2 = %d\n",WGruppa[2].Gruppa);
printf("City= %s\n",WGruppa[2].D_addr.City);
printf("Street= %s\n",WGruppa[2].D_addr.Street_Nd_Kw);
}
1.4.6 Область видимости и время жизни переменных
По области видимости переменные в Си можно разделить на три группы:
1.Переменная, определенная во всех модулях (файлах) программы. Такая переменная определяется при помощи ключевого слова extern. Эта переменная будет видна во всех точках программы. Такая переменная является глобальной для всей программы.
2.Переменная, определенная в одном из модулей (файле) перед текстами всех функций. Такая переменная будет глобальной для данного модуля, т.е. может быть использована во всех точках данного модуля.
3.Переменная определенная в данной функции. Эта переменная может быть использована только в пределах данной функции. Такую переменную мы будем называть локальной.
По времени жизни все переменные можно разделить на две группы:
1.Переменные живущие в течении работы программы.
2.Переменные уничтожающиеся после выхода из функции.
Глобальные переменные относятся к первому типу по времени жизни. Локальные переменные уничтожаются по выходу из функции. В том случае, когда мы хотим сделать локальную переменную долгоживущей используется слово static. Локальные переменные имеющие такой тип живут начиная с момента первого вызова функции и до конца работы программы. Однако в смысле видимости эти переменные остаются локальными. Запись staticinti=0; Означает, что переменная инициализируется нулем при первом входе в функцию, но при последующих входах в функцию ее значение сохраняется в зависимости от тех действий, которые над ней были произведены.
Современные компиляторы Си транслируют программу так, чтобы максимально оптимизировать ее работу. Одним из средств оптимизации это хранение переменных, когда это возможно в регистрах, вместо ячеек памяти. В тех случаях когда вы хотите запретить, чтобы данная переменная хранилась в регистрах используется модификатор volatile. Такая необходимость может возникнуть если предполагается возможность изменение переменной в результате внешнего воздействия (например прерывания).
И последнее замечание. Динамически выделенная память, где бы вы ее не выделяли живет до тех пор пока вы ее не освободили.
1.5 Основные операторы
Операция присваивания.
Самой общей операцией является присваивание, например, с= a/b. В Си присваивание обозначается знаком равенства=, при этом значение справа от знака равенства присваивается переменной слева. Возможно, применять также последовательные присваивания, например: с = a = b.
Арифметические операторы.
В Си выполняются следующие группы арифметических операций:
1.Бинарные: сложение(+), вычитание(-), умножение(*), деление(/), целочисленное деление(%) (для типа int получение остатка).
2.Унарные: унарный плюс (+), унарный минус (-), адресация (&), косвенная адресация (*), определение размера памяти типа (sizeof).
3.Логические: и (&&), или (!!), не (!=).
4.Отношения:
a)равно (==), не равно(!>);
б) меньше чем (<), больше чем (>), меньше или равно (<=), больше или равно (>=);
5.Приращения (++) и уменьшения (--). Например, i++ обозначает, что i=i+1, а i-- обозначает i=i-1.
6.Побитовые операции - позволяют производить операции над битами.
7.Комбинированные операции. В Турбо-Си существуют сокращения при написании выражений, содержащих многочисленные операции:
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;
8.Адресные операции:
1. Операция определения адреса (&) 2. Операция обращения по адресу (*).
Операция & возвращает адрес данной переменной; если Х является переменной типа int, то &Х является адресом (расположения в памяти) этой переменной. С другой стороны, если msg является указателем на тип char, то *msg является символом, на который указывает msg. Рассмотримпример:
#include <stdio.h>
main()
{
int X;
char *msg;
X = 6 + 1;
msg = "Привет\n";
printf(" X = %d &X = %p \n",X,&X);
printf("*msg = %c msg = %p \n", *msg, msg);
}
При печати в первой функции печатается два значения: значение X 7 и адрес X (назначаемый компилятором). Во второй функции также печатается два значения: символ, на который указывает msg (П), и значение msg, которое является адресом этого символа (также назначен компилятором).
Старшинство операций в Си соответствует старшинству операций в математике.
Оператор запятая.
Для организации множественных выражений, расположенных внутри круглых скобок используется оператор запятая. Выражение внутри скобок вычисляется слева направо, и все выражение принимает значение, которое было вычислено последним. Например:
char X,Y;
(X = Y, Y = getch())
присваивает переменной X значение Y, затем считывает символ, вводимый с клавиатуры, и запоминает его в Y. Результатом всего выражения, в итоге, будет значение введенного с клавиатуры символа.
Операторы управления.
Оператор If... дает возможность в зависимости от условия выполнять ту или иную ветвь программы. Синтаксис оператора следующий:
If условие выражение1 else выражение2;
Условие должно давать результат в виде логического значения истинности или ложности. Выражение1 будет выполняться если условие истинно. Выражение2 будет выполняться если условие ложно.
Существует сокращенный вариант оператора:
If условие выражение1
Пример. Определить, является ли введенное число днем недели, т.е. входит ли число в диапазон от 1 до 7.
#include <stdio.h>
int A;
main()
{
printf("? ");
scanf("%d",&A);
if ((A < 1) || (A > 7))
printf("Error %d\n",A);
else printf("OK %d\n",A);
}
Выражение условия (A<1) || (A>7) будет давать TRUE, если выполняется A<1 или A>7 - в этом случае выполняется ветка printf('Error ',A);, иначе ветка printf('OK ',A);.
Существует другой вариант записи оператора If ... Пример:
#include <stdio.h>
main()
{
int y,t;
printf("? ");
scanf("%d",&t);
y=(t>0)? t*10: t-10; /* if t>0 y=t*10 else y=t-10;*/
printf("OK %d\n",y);
}
В данном варианте вид оператора показан в комментариях.
Оператор switch... case используется в случае, когда необходимо анализировать переменную и в зависимости от ее значения производить те или иные действия. Рассмотрим пример. С клавиатуры вводятся буквы латинского алфавиты. В зависимости от буквы произвести те или иные действия.
#include <stdio.h>
char A;
main()
{
printf("? ");
scanf("%c",&A);
switch (A) {
case 'c': printf(" smoll %c\n",A); break; /* выходизблока */
case 'F':
case 'G': printf(" big %c\n",A);
break;
default: printf("Error %c\n",A);
}
}
В данном примере если введен символ с, то выполняется printf(" smoll %c\n",A);, если вводится заглавные буквы F или G, то выполняется printf(" big %c\n",A);, если не один из рассмотренных символов не вводится, то выполняется printf("Error %c\n",A);.
Для повторения некоторого множества команд несколько раз можно использовать оператор do... while. Рассмотримпример.
#include <stdio.h>
main()
{
int A;
do {
printf("Zifra? ");
scanf("%d",&A);
printf("Error %d\n",A);
} while (!(A == 9));
printf("OK %d\n",A);
}
С клавиатуры вводится число. Выполняется оператор printf("Error %d\n",A);. Далее идет анализ - равно число 9 или нет, если не равно, снова выполняется тело цикла: