Область видимости переменной 


Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

Область видимости переменной



Переменные бываю локальными (объявленными внутри какой-нибудь функции) и глобальными. Глобальная переменная видна всем функциям, объявленным в данном файле. Локальная переменная ограничена своей областью видимости. Когда мы говорим, что переменная "видна в каком-то месте", это означает, что в этом месте она определена и её можно использовать. Например, рассмотрим программу, в которой есть глобальная переменная

 

#include<conio.h>

#include<stdio.h>

 

int global = 100;

 

void foo() {

printf("foo: %d\n", global);

}

 

void bar(int global) {

printf("bar: %d\n", global);

}

 

int main() {

foo();

bar(333);

getch();

}

 

Будет выведено

 

foo: 100

bar: 333

 

Здесь глобальная переменная global видна всем функциям. Но аргумент функции затирает глобальную переменную, поэтому при передаче аргумента 333 выводится локальное значение 333.

Вот другой пример.

 

#include<conio.h>

#include<stdio.h>

 

int global = 100;

 

int main() {

int global = 555;

printf("%d\n", global);

getch();

}

 

Программа выведет

 

 

Также, как и в прошлом случае, локальная переменная "важнее". Переменная, объявленная в некоторой области видимости не видна вне её, например.

 

#include<conio.h>

#include<stdio.h>

 

int global = 100;

 

int main() {

int x = 10;

{

int y = 30;

printf("%d", x);

}

printf("%d", y);

}

 

Этот пример не скомпилируется, потому что переменная y существует только внутри своего блока.

Вот ещё пример, когда переменные, объявленные внутри блока перекрывают друг друга

 

#include<conio.h>

#include<stdio.h>

 

int global = 100;

 

int main() {

int x = 10;

{

int x = 20;

{

int x = 30;

printf("%d\n", x);

}

printf("%d\n", x);

}

printf("%d\n", x);

getch();

}

 

Программа выведет:

 

 

Глобальных переменных необходимо избегать. Очень часто можно услышать такое. Давайте попытаемся разобраться, почему. В ваших простых проектах глобальные переменные выглядят вполне нормально. Но представьте, что у вас приложение, которое:

· разрабатывается несколькими людьми и состоит из сотен тысяч строк кода;

· работает в несколько потоков.

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

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

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

 


Тема №3

Оператор присваивания

Когда мы работаем с переменными, необходимо изменять их значение. Это делается с помощью оператора присваивания «=».

 

#include<conio.h>

#include<stdio.h>

 

int main() {

signed long long a, b, c;

a = 1234567890123456789LL;

b = -1234567890123456789LL;

c = 123;

 

printf("%lld\n", a);

printf("%lld\n", b);

printf("%lld\n", c);

getch();

}

 

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

 

#include<conio.h>

#include<stdio.h>

 

int main() {

unsigned a, b, c;

a = b = c = 10;

printf("%u = %u = %u = 10", a, b, c);

getch();

}

 

В этом примере переменной c присваивается значение 10, после чего оператор присваивания возвращает значение 10, которое используется следующим оператором, и присваивает значение 10 переменной b, после чего возвращает значение 10, которое используется следующим оператором. Это свойство - возвращать значение - часто используется. Например, при выделении памяти мы можем узнать, была ли действительно выделена память.

 

#include<conio.h>

#include<stdio.h>

#include<stdlib.h>

 

int main() {

int *a = NULL;

 

printf("Try to allocate memory\n");

if (a = (int*)malloc(1024*1024*1024 * sizeof(int))) {

printf("Memory was allocated");

} else {

printf("Error: can't allocate memory");

}

getch();

free(a);

}

 

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

 

a = (int*)malloc(1024*1024*1024 * sizeof(int));

if (a) {

printf("Memory was allocated");

} else {

printf("Error: can't allocate memory");

}

 

(вам этот пример кажется непонятным, здесь важно для вас только использование оператора =).

Rvalue и lvalue

Любая переменная связана с адресом в памяти и хранит некоторое значение. Выражение типа

 

int i;

i = 20;

 

говорит о том, что мы заносим в область памяти, на которую указывает переменная i значение 20. Выражение

 

20 += 3;

 

не имеет смысла, поскольку 20 - это литерал, значение, которое не связано с областью памяти.

Для обозначения объектов, которым может быть присвоено значение, используется понятие lvalue (буквально, то, что может стоять слева от оператора присваивания). Для обозначения объектов, которые имеют только значения, используют выражение rvalue (то, что стоит справа от оператора присваивания). lvalues в свою очередь делят на modifiable lvalues, которые могут быть изменены, и non-modifiable lvalues, которые связаны с областью памяти, но не могут быть изменены, например, константы. В том случае, если lvalue стоит справа от оператора присваивания, например:

 

int i, j;

j = 20;

i = j;

 

то происходит lvalue-to-rvalue convertion, преобразование lvalue к rvalue.

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

К lvalue относятся переменные, выражения ссылочных типов, результат операции разыменования *, строковые литералы, результат префиксных операций ++ и -- над ссылочными типами.

Имена функций и массивов, также объявленные как немодифицируемые объекты, являются non-modifiable lvalues.

Ввод-вывод

Форматированный вывод

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

Функция форматированного вывода printf получает в качестве аргументов строку формат и аргументы, которые необходимо вывести в соответствии с форматом, и возвращает число выведенных символов. В случае ошибки возвращает отрицательное значение и устанавливает значение ferror. Если произошло несколько ошибок, errno равно EILSEQ (Ошибочная последовательность байтов).

int printf (const char * format,...);

 

#include <stdio.h>

#include <conio.h>

 

void main() {

//функция не получает никаких аргументов, кроме строки

printf("Hello world");

getch();

}

 

Функция проходит по строке и заменяет первое вхождение %<спецификатор формата> на первый аргумент, второе вхождение %<спецификатор формата> на второй аргумент и т.д. Далее мы будем просто рассматривать список флагов и примеры использования.

Общий синтаксис спецификатора формата

 

%[флаги][ширина][.точность][длина]спецификатор

 

Спецификатор – это самый важный компонент. Он определяет тип переменной и способ её вывода.

 

Спецификатор Что хотим вывести Пример
d или i Целое со знаком в десятичном виде  
u Целое без знака в десятичном виде  
o Беззнаковое в восьмеричном виде  
x Беззнаковое целое в шестнадцатеричном виде 7fa
X Беззнаковое целое в шестнадцатеричном виде, верхний регистр 7FA
f или F Число с плавающей точкой 3.4563745
e Экспоненциальная форма для числа с плавающей точкой 3.1234e+3
E Экспоненциальная форма для числа с плавающей точкой, верхний регистр 3.1234E+3
g Кратчайшее из представлений форматов f и e 3.12
G Кратчайшее из представлений форматов F и E 3.12
a Шестнадцатеричное представление числа с плавающей точкой -0xc.90fep-2
A Шестнадцатеричное представление числа с плавающей точкой, верхний регистр -0xc.90FEP-2
c Буква a
s Строка (нуль-терминированный массив букв) Hello World
p Адрес указателя b8000000
n Ничего не пачатает. Аргументом должен быть указатель на signed int. По этому адресу будет сохранено количество букв, которое было выведено до встречи %n  
% Два идущих друг за другом процента выводят знак процента %

 

Примеры

 

#include <stdio.h>

#include <conio.h>

 

void main() {

int a = 0x77, b = -20;

char c = 'F';

float f = 12.2341524;

double d = 2e8;

char* string = "Hello, World!";

 

printf("%s\n", string);

printf("a = %d, b = %d\n", a, b);

printf("a = %u, b = %u\n", a, b);

printf("a = %x, b = %X\n", a, b);

printf("dec a = %d, oct a = %o, hex a = %x\n", a, a, a);

printf("floating point f = %f, exp f = %e\n", f, f);

printf("double d = %f or %E\n", d, d);

printf("not all compiler support %a\n", f);

printf("character c = %c, as number c = %d", c, c);

printf("address of string is %p", string);

getch();

}

 

Строка формата также может включать в себя следующие необязательные суб-спецификаторы:

· флаг,

· ширина,

·.точность и

· модификатор (именно в таком порядке).

 

Флаг Описание
- Выключка влево на заданное шириной значение
+ Явно указывать знак у числа, даже для положительных чисел
(пробел) Если знак не будет выведен, то вставляет пробел перед выводимым числом
# Когда используется вместе с o, x или X, вставляет перед числом 0, 0x или 0X. Когда используется со спецификаторами a, A, e, E, f, F, g или G, вставляет десятичную точку, даже если после неё нет десятичных знаков
  Вставляет нули, когда объявлен спецификатор ширины

 

Ширина Описание
(число) Минимальное количество знаков, которое необходимо вывести. Если в числе меньше знаков, то вставляет пробелы (или нули)
* Ширина не указана в строке формата, она передаётся отдельно в виде аргумента, который должен предшествовать выводимому числу

 

Точность Описание
.число Для спецификаторов целых (d, i, o, u, x, X) точность определяет минимальное количество знаков, которое необходимо вывести. Если значение короче, то выводятся нули перед числом. Значение не обрезается, даже если оно длиннее. Точночть 0 означает, что для значения 0 ничего не выводится. Для спецификаторов чисел с плавающей точкой (a, A, e, E, f, F) это число знаков, которые необходимо вывести после десятичной точки (по умолчанию 6). Для g и G - это число значащих разрядов, которые необходимо вывести. Для s - выводится указанное число символов. По умолчанию выводятся все символы до первого нулевого. Если число не стоит, то по умолчанию точность равна 0
.* Точность не указана в строке формата, она передаётся отдельно в виде аргумента, который должен предшествовать выводимому числу

 

Примеры

 

#include <stdio.h>

#include <conio.h>

 

void main() {

int a = 0x77, b = -20;

char c = 'F';

float f = 12.2341524;

double d = 2e2;

char* string = "Hello, World!";

 

printf("%.3f\n", f);

printf("%.*f\n", 2, f);

printf("%010.3f\n", d);

printf("%*d\n", 6, a);

printf("%+d\n", b);

printf("%0.6d\n", a);

printf("%.f\n", d);

printf("%.4s", string);

getch();

}

 

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

 

  Спецификаторы
Длина d, i u o x X f F e E g G a A c s p n
(none) int unsigned int double int char* void* int*
hh signed char unsigned char         signed char*
h short int unsigned short int         short int*
l long int unsigned long int   wint_t wchar_t*   long int*
ll long long int unsigned long long int         long long int*
j intmax_t uintmax_t         intmax_t*
z size_t size_t         size_t*
t ptrdiff_t ptrdiff_t         ptrdiff_t*
L     long double        

 

Примеры

 

#include <stdio.h>

#include <conio.h>

 

void main() {

long long x = 12300000000579099123;

short i = 10;

printf("%llu\n", x);

printf("%d\n", i);

printf("%hd\n", i);

getch();

}

 

Форматированный ввод

Рассмотрим форматированный ввод функцией scanf.

 

int scanf(const char*,...)

 

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

Формат спецификатора ввода

 

%[*][ширина][длинна]спецификатор

 

Спецификатор Описание Выбранные символы
i, u Целые Произвольное число цифр (0-9), возможно, начинающихся с + или -. Если число начинается с 0, то считывается в восьмеричном формате, если с 0x, то в шестнадцатеричном.
d Десятичное целое Произвольное число цифр (0-9), возможно, начинающихся с + или -.
o восьмеричное целое Произвольное число цифр (0-7), возможно, начинающихся с + или -.
x Шестнадцатеричное целое Произвольное число цифр (0-F), возможно, начинающихся с + или - и префикса 0x или 0X.
f, e, g, а Число с плавающей точкой Число, состоящее из набора цифр 0-9, возможно с десятичным разделителем (точкой). Возможно также представление в экспоненциальной форме. C99 позволяет также вводить число в шестнадцатеричном формате.
c Символ Если ширина не передана, то считывает один символ. Если ширина передана, то считывает нужное количество символов и размещает их в массиве БЕЗ терминального символа на конце.
s Строка Считывает все не пробельные символы. Если указана ширина, то не более n символов. Ставит на место n+1 символа терминальный.
p Адрес указателя Последовательность символов, трактуемая как адрес указателя. Формат зависит от реализации, но совпадает с тем, как выводит printf с ключом p
[символы] Множество символов Считывает только те символы, которые записаны в квадратных скобках, С99
[^символы] Множество символов Считывает только те символы, которые не указаны в квадратных скобках, С99
n Ничего не считывает Сохраняет число уже считанных символов по указанному адресу

 

Как и в printf, ширина, заданная символом * ожидает аргумента, который будт задавать ширину. Флаг длина совпадает с таким же флагом функции printf.

Примеры.

 

#include <stdio.h>

#include <conio.h>

 

void main() {

int year, month, day;

char buffer[128];

int count;

//Требует форматированного ввода, например 2013:12:12

printf("Enter data like x:x:x = ");

scanf("%d:%d:%d", &year, &month, &day);

printf("year = %d\nmonth = %d, day = %d\n", year, month, day);

//Считываем строку, не более 127 символов. При считывании в массив писать & не надо,

//так как массив подменяется указателем

printf("Enter string = ");

scanf("%127s", buffer);

printf("%s", buffer);

getch();

}

 

Кроме функций scanf и printf есть ещё ряд функций, которые позволяют получать вводимые данные.

 

int getch()

int getchar()

 

Возвращает введённый символ, при этом не выводит его на консоль.

 

#include <stdio.h>

#include <conio.h>

 

void main() {

char c = 0;

do {

c = getch();

printf("%c", c);

} while (c!= 'q');

}

 

Функция fgets:

 

char * fgets (char * str, int num, FILE * stream)

 

Функция позволяет считывать строку с пробельными символами. Несмотря на то, что она работает с файлом, можно с её помощью считывать и из стандартного потока ввода. Её преимущество относительно gets в том, что она позволяет указать максимальный размер считываемой строки и заканчивает строку терминальным символом.

 

#include <stdio.h>

#include <conio.h>

#include <stdlib.h>

 

void main() {

char buffer[128];

//Считываем из стандартного потока ввода

fgets(buffer, 127, stdin);

printf("%s", buffer);

//Этим можно заменить ожидание ввода символа

scanf("1");

}

 

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

Непечатные символы

В си определён ряд символов, которые не выводятся на печать, но позволяют производить форматирование вывода. Эти символы можно задавать в виде численных значений, либо в виде эскейп-последовательностей:

· символа,

· экранированного обратным слешем.

 

Последовательность Числовое значение Результат
\a 0x07 Звуковой сигнал
\b 0x08 Перевод каретки на одно значение назад
\f 0x0c Новая страница
\n 0x0a Новая строка
\r 0x0d Возврат каретки
\t 0x09 Табуляция
\v 0x0b Вертикальная табуляция
\" 0x22 Двойная кавычка
\\ 0x5с Обратный слеш

 

#include <stdio.h>

#include <conio.h>

 

void main() {

char backspace = 0x08;

//Выводим с использованием символа переноса строки

printf("Hello\nWorld\n");

//Выводим символ переноса строки через его значение

printf("Hello%cWorld\n", 0x0a);

//"Выводим" сигнал

printf("\a");

//Выводим сигнал, как символ

printf("%c", '\a');

//Выводим сигнал через шестнадцатеричное значение

printf("%c", 0x07);

printf("This is sparta!!!\b\b%c", backspace);

printf(" ");

getch();

}

 


Тема №4



Поделиться:


Последнее изменение этой страницы: 2017-01-20; просмотров: 256; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 3.144.140.151 (0.146 с.)