Нижегородский государственный университет им. Н.И.Лобачевского
Опубликован: 25.11.2008 | Доступ: свободный | Студентов: 9592 / 1296 | Оценка: 4.06 / 3.66 | Длительность: 21:16:00
Лекция 6:

Основные синтаксические конструкции языка C

< Лекция 5 || Лекция 6: 1234 || Лекция 7 >
Аннотация: В данной лекции основное внимание уделено синтаксическим конструкциям языка C++. Приводятся практические примеры и основные определения
Ключевые слова: единица, функция, Pascal, программа, значение, служебное слово, void, ПО, адрес, место, выборка, запись, вызов функции, компилятор, заголовки, список, синтаксис, вероятность, прототип функции, координаты, константы, переменная, группа, статическая переменная, I-TV, файл, глобальные переменные, спецификатор, именованная константа, оператор присваивания, диапазон, потеря точности, переполнение, операторы, Размещение, арифметическое выражение, индекс, операции, Алгол, cod, вычисление, альтернативные, ветвь, else, операции отношения, логическая переменная, ложь, истина, метка, функция переходов, оператор goto, цикла, тело цикла, фигурные скобки, счетчик, массив, Паскаль, Windows, условие продолжения цикла, выход, память, оператор выбора, переключатель, выражение, составной оператор, пустой оператор, формальный параметр, тип исключения, нетипизированный указатель, адрес переменной, информация

5.1. Заголовок функции и прототип функции

Любая программная единица на языках C, C++ оформляется как функция, причем в отличие от языка Pascal функции не могут быть вложены друг в друга. Поэтому функция представляется как некий кирпичик, который может быть размещен в любом месте программы. А вся программа состоит из последовательности таких кирпичиков, среди которых обязательно присутствует главный – функция с именем main.

Описание любой функции начинается с ее заголовка, имеющего вид:

trv namef(type1 par1,type2 par2,...)

Здесь

  • trv – тип возвращаемого значения;
  • namef – имя функции;
  • par1 – имя первого аргумента функции, имеющего тип type1 ;
  • par2 – имя второго аргумента функции, имеющего тип type2 ;

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

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

  • по значению (в заголовке функции вслед за типом параметра располагается его имя);
  • по указателю (в заголовке функции имени параметра предшествует символ *);
  • по ссылке (в заголовке функции имени параметра предшествует символ &).

Пример 1. Функции передаются два значения, по которым она находит и возвращает среднее арифметическое.

double mid(double x,double y)
{ return (x+y)/2.0; }

Пример 2. Функции передаются два параметра по указателю. Функция меняет местами значения переданных ей переменных.

void swap(int *x,int *y)
{ int tmp;
  tmp=*x;  *x=*y;  *y=tmp;
}

Пример 3. Функции передаются два параметра по ссылке. Функция меняет местами значения переданных ей переменных.

void swap(float &x,float &y)
{ float tmp;
  tmp=x;  x=y;  y=tmp;
}

По сути дела, два последние способа передачи параметров одинаковы – и в том, и в другом случае функции сообщают адрес расположения параметра в оперативной памяти. По этому адресу функция может извлечь текущее значение параметра или записать на его место новое значение. Разница только в форме обращения по полученному адресу. Ссылки делают это обращение более простым, к имени параметра не надо ничего добавлять. При работе с указателями выборка и запись по указанному адресу сопровождается дополнительным символом *.

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

Пример 4. Заголовки функций вынесены в список прототипов.

double mid(double  x, double y);
void swap1(double *x,double *y);
void swap2(double &x,double &y);

void main()
{ double a=1.5, b=-2.5,c;
  c=mid(a,b);
  swap1(&a,&b);
  swap2(b,c);
.............
}
double mid(double  x, double y)
{ return (x+y)/2.0; }
void swap1(double *x,double *y)
{ double tmp;
  tmp=*x;  *x=*y;  *y=tmp;
}
void swap2(double &x,double &y)
{ double tmp;
  tmp=x;  x=y;  y=tmp;
}

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

double mid(double, double);

Для нужд компилятора, который проверяет в вызовах функций только количество параметров и их тип, этого достаточно. И синтаксис языков C, C++ разрешает так делать. Но, на наш взгляд, выбор запоминающихся мнемонических имен формальных параметров и сохранение их в прототипах могут уменьшить вероятность появления ошибок, связанных с перестановкой данных. Например, как догадаться о смысле параметров по следующему прототипу функции, определяющей точку пересечения двух отрезков:

int intersect(double&,double&,double&,double&,double&,double&);

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

int intersect(double &x1, double &y1, double &x2, double &y2,
              double &x,  double &y);

5.2. Объявление локальных и внешних данных

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

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

#include <iostream.h>
#include <conio.h>
int x=20; //глобальная переменная
void main()
{ int x=40; //локальная переменная
  cout << "Local x=" << x << endl;
  cout << "Global x=" << ::x << endl;
  getch();
}

Глобальным переменным место в памяти выделяется до начала исполнения программы, и это место сохраняется за ними до завершения работы программы. В отличие от этого место для хранения локальных переменных выделяется только в момент вызова функции, а при выходе из функции выделенные ресурсы возвращаются системе. Поэтому значения локальных переменных пропадают, их бывшее место в оперативной памяти будет перераспределено под нужды других функций. Однако существует специальная группа локальных переменных, которая описывается внутри функции со спецификатором static. Ячейки памяти, выделенные для их хранения, фиксируются до окончания работы всей программы. При повторном обращении к функции значения ее статических переменных сохранены, и функция вновь может ими воспользоваться. Однако для других функций внутренние статические переменные недоступны, это – собственность объявившей их функции.

Объявление переменных в общем случае выглядит следующим образом:

[static] tv namev [=value]

Здесь

  • tv – тип переменной;
  • namev – имя переменной;
  • value – начальное значение переменной.

Если переменная объявляется как глобальная и ее начальное значение не указано, то системы BC 3.1 и BCB выделяют ей соответствующий участок памяти и заносят нули в выделенные байты. Однако из соображений переносимости программы не стоит рассчитывать на такую чистку памяти. Лучше принудительно задавать те или иные значения (в том числе и нулевые) – этот способ никогда не подведет.

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

Объявление глобальной переменной тоже может сопровождаться спецификатором static. Это имеет смысл, когда полный текст программы разбросан по нескольким файлам. В этом случае статические глобальные переменные доступны только тем функциям, которые включены в тот же файл. Из других файлов эти переменные не доступны. Для ссылок на глобальные переменные, описанные в другом файле, обычно используют спецификатор extern (от англ. external – внешний):

double qq(int n,double r)
{ extern float eps;
...................

Глобальные переменные, объявленные в этом же файле, в таком дополнительном пояснении не нуждаются.

Для объявления именованных констант обычно используют следующую конструкцию:

const [tc] namec=value;

Здесь

  • tc – необязательный тип константы (по умолчанию tc=int );
  • namec – имя константы;
  • value – значение константы.

Например:

const Nmax=100;
  const double eps=1e-6;

Иногда для задания таких же констант прибегают к механизму простейшей макроподстановки:

#define Nmax 100
  #define eps 1e-6

Это означает, что перед трансляцией программы компилятор (точнее, прекомпилятор) просмотрит ее текст и всюду, где будет встречено сочетание символов Nmax, его заменят на число 100, а сочетание символов eps на число 1e-6. Результат будет тем же самым, но работа по макроподстановке связана с более заметными затратами времени.

< Лекция 5 || Лекция 6: 1234 || Лекция 7 >
Alexey Ku
Alexey Ku

Попробуйте часть кода до слова main заменить на 

#include "stdafx.h" //1

#include <iostream> //2
#include <conio.h>

using namespace std; //3

Александр Талеев
Александр Талеев

#include <iostream.h>
#include <conio.h>
int main(void)
{
int a,b,max;
cout << "a=5";
cin >> a;
cout <<"b=3";
cin >> b;
if(a>b) max=a;
else max=b;
cout <<" max="<<max;
getch();
return 0;
}

при запуске в visual express выдает ошибки 

Ошибка    1    error C1083: Не удается открыть файл включение: iostream.h: No such file or directory    c:\users\саня\documents\visual studio 2012\projects\проект3\проект3\исходный код.cpp    1    1    Проект3

    2    IntelliSense: не удается открыть источник файл "iostream.h"    c:\Users\Саня\Documents\Visual Studio 2012\Projects\Проект3\Проект3\Исходный код.cpp    1    1    Проект3

    3    IntelliSense: идентификатор "cout" не определен    c:\Users\Саня\Documents\Visual Studio 2012\Projects\Проект3\Проект3\Исходный код.cpp    6    1    Проект3

    4    IntelliSense: идентификатор "cin" не определен    c:\Users\Саня\Documents\Visual Studio 2012\Projects\Проект3\Проект3\Исходный код.cpp    7    1    Проект3

при создании файла я выбрал пустой проект. Может нужно было выбрать консольное приложение?