» Главная
eXcode.ru » Статьи » Другие » Основы программирования
» Новости
» Опросы
» Файлы
» Журнал



Пользователей: 0
Гостей: 9





Основы языка Си




Основы языка Си

В настоящее время язык Си и объектно-ориентированные языки его группы (прежде всего C++, а также Java и C#) являются основными в практическом программировании. Достоинство языка Си - это, прежде всего, его простота и лаконичность. Язык Си легко учится. Главные понятия языка Си, такие, как статические и локальные переменные, массивы, указатели, функции и т.д., максимально приближены к архитектуре реальных компьютеров. Так, указатель - это просто адрес памяти, массив - непрерывная область памяти, локальные переменные - это переменные, расположенные в аппаратном стеке, статические - в статической памяти. Программист, пишущий на Си, всегда достаточно точно представляет себе, как созданная им программа будет работать на любой конкретной архитектуре. Другими словами, язык Си предоставляет программисту полный контроль над компьютером.

Первоначально язык Си задумывался как заменитель Ассемблера для написания операционных систем. Поскольку Си - это язык высокого уровня, не зависящий от конкретной архитектуры, текст операционной системы оказывался легко переносимым с одной платформы на другую. Первой операционной системой, написанной практически целиком на Си, была система Unix. В настоящее время почти все используемые операционные системы написаны на Си. Кро- ме того, средства программирования, которые операционная система предоставляет разработчикам прикладных программ (так называемый API - Application Program Interface), - это наборы системных функций на языке Си.

Тем не менее, область применения языка Си не ограничилась разработкой операционных систем. Язык Си оказался очень удобен в программах обработки текстов и изображений, в научных и инженерных расчетах. Объектно-ориентированные языки на основе Си отлично подходят для программирования в оконных средах.

В данном разделе будут приведены лишь основные понятия языка Си (и частично C++). Это не заменяет чтения полного учебника по Си или C++, например, книг [6] и [8].

Мы будем использовать компилятор C++ вместо Cи. Дело в том, что язык Си почти целиком входит в C++, т.е. нормальная программа, написанная на Си, является корректной C++ программой. Слово "нормальная" означает, что она не содержит неудачных конструкций, оставшихся от ранних версий Си и не используемых в настоящее время. Компилятор C++ предпочтительнее, чем компилятор Си, т.к. он имеет более строгий контроль ошибок. Кроме того, некоторые конструкции C++, не связанные с объектно-ориентированным программированием, очень удобны и фактически являются улучшением языка Си. Это, прежде всего, комментарии //, возможность описывать локальные переменные в любой точке программы, а не только в начале блока, и также задание констант без использования оператора #define препроцесора. Мы будем использовать эти возможности C++, оставаясь по существу в рамках языка Си.

Структура Си-программы

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

В языке Си исходные файлы бывают двух типов:

  • заголовочные, или h-файлы;
  • файлы реализации, или Cи-файлы.

Имена заголовочных файлов имеют расширение ".h". Имена файлов реализации имеют расширения ".c" для языка Си и ".cpp", ".cxx" или ".cc" для языка C++.

К сожалению, в отличие от языка Си, программисты не сумели договориться о едином расширении имен для файлов, содержащих программы на C++. Мы будем использовать расширение ".h" для заголовочных файлов и расширение ".cpp" для файлов реализации.

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

int x;

определяющая целочисленную переменную x, является ошибкой. Вместо этого следует использовать описание

    
extern int x;

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

Файлы реализации, или Cи-файлы, содержат тексты функций и определения глобальных переменных. Говоря упрощенно, Си-файлы содержат сами программы, а h-файлы - лишь информацию о программах.

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

Файлы реализации могут подключать описания, содержащиеся в заголовочных файлах. Сами заголовочные файлы также могут использовать другие заголовочные файлы. Заголовочный файл подключается с помощью директивы препроцессора #include. Например, описания стандартых функций ввода-вывода включаются с помощью строки

    
#include <stdio.h>

(stdio - от слов standard input/output). Имя h-файла записывается в угловых скобках, если этот h-файл является частью стандартной Си-библиотеки и расположен в одном из системных каталогов. Имена h-файлов, созданных самим программистом в рамках разрабатываемого проекта и расположенных в текущем каталоге, указываются в двойных кавычках, например,

    
#include "abcd.h"

Препроцессор - это программа предварительной обработки текста непосредственно перед трансляцией. Команды препроцессора называются директивами. Директивы препроцессора содержат символ диез # в начале строки. Препроцессор используется в основном для подключения h-файлов. В Си также очень часто используется директива #define для задания символических имен констант. Так, строка

#define PI 3.14159265

задает символическое имя PI для константы 3.14159265. После этого имя PI можно использовать вместо числового значения. Препроцессор находит все вхождения слова PI в текст и заменяет их на константу. Таким образом, препроцессор осуществляет подмену одного текста другим. Необходимость использования препроцессора всегда свидетельствует о недостаточной выразительности языка. Так, в любом Ассемблере средства препроцессирования используются довольно интенсивно. В Си по возможности следует избегать чрезмерного увлечения командами препроцессора - это затрудняет понимание текста программы и зачастую ведет к трудно исправляемым ошибкам. В C++ можно обойтись без использования директив #define для задания констант. Например, в C++ константу PI можно задать с помощью нормального описания

const double PI = 3.14159265;

Это является одним из аргументов в пользу применения компилятора C++ вместо Си даже при трансляции программ, не содержащих конструкции класса.

Функции

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

double sin(double x);

имеет один аргумент x типа double (вещественное число). Результат функции также имеет тип double. При вызове фукция sin вычисляет синус числа, переданного ей в качестве фактического аргумента, и возвращает вычисленное значение в вызывающую программу.

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

После вызова функции значение, возвращенное в результате ее выполнения, используется в выражении (имя функции как бы заменяется возвращенным значением). Примеры:

x = sin(1.0);

Здесь в результата вызова функции sin вычисляется синус числа 1.0, затем вычисленное значение записывается в переменную x при выполнения оператора присваивания "=". Другой пример:

    
f();

Вызывается функция f, не имеющая параметров. Значение, возвращенное в результате выполнения функции f, не используется. Программа на Си состоит из функций. Работа программы всегда начинается с функции с именем main. Рассмотрим минимальный пример Си-программы

Программа "Hello, World!"

Приведенная ниже программа печатает фразу "Hello, World!" на экране терминала.

 
    #include <stdio.h>

    int main() {
        printf("Hello, World
");
        return 0;
    }

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

Тело любой функции заключается в фигурные скобки. В теле функции main вызывается функция printf. В данном случае ее единственным аргументом является строка, которая выводится в стандартный поток вывода. Строковые константы в Си заключаются в двойные апострофы. Строка заканчивается символом перевода курсора в начало следующей строки (читается как "new line", новая строка). Желательно любую печать завершать этим символом, иначе при следующей печати новая строка будет дописана в конец предыдущей.

Строка

     
return 0;

завершает выполнение функции main и возвращает нулевой результат ее выполнения. Операционная система трактует нулевой результат как признак успешного завершения программы.

Для выполнения данной программы надо сначала ввести ее текст в файл "hello.cpp", используя любой текстовый редактор. Затем надо скомпилировать и собрать готовую программу. Конкретные команды зависят от операционной системы и установленного Си-компилятора. В системе Unix с компилятором gcc из пакета GNU это делается с помощью команды

     
g++ hello.cpp

В результате создается выполняемый файл с именем "a.out". Для запуска программы следует выполнить команду

     
./a.out

Если необходимо, чтобы в результате компиляции и сборки создавался выполняемый файл с именем "hello", то надо выполнить следующую команду:

 
g++ -o hello hello.cpp

Здесь в командной строке используется ключ "-o hello" (от слова "output"), задающий имя "hello" для выходного файла. В этом случае программа запускается с помощью команды

     
./hello

Заметим, что в системе Unix имена выполняемых файлов обычно не имеют никакого расширения. В системах MS DOS и MS Windows выполняемые файлы имеют расширение ".exe".

Типы переменных

При рассмотрении типов переменных в Си и C++ следует различать понятия базового типа и конструкции, позволяющей строить новые типы на основе уже построенных. Базовых типов совсем немного - это целые и вещественные числа, которые могут различаться по диапазону возможных значений (или по длине в байтах) и, в случае языка C++, логический тип. К конструкциям относятся массив, указатель и структура, а также класс в C++.

Базовые типы

В языке Си используются всего два базовых типа: целые и вещественные числа. Кроме того, имеется фиктивный тип void ("пустота"), который применяется либо для функции, не возвращающей никакого значения, либо для описания указателя общего типа (когда неизвестна информации о типе объекта, на который ссылается указатель).

В C++ добавлен логический тип.

Целочисленные типы

Целочисленные типы различаются по длине в байтах и по наличию знака. Их четыре - char, short, int и long. Кроме того, к описанию можно добавлять модификаторы unsigned или signed для беззнаковых (неотрицательных) или знаковых целых чисел.

Тип int

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

В современных архитектурах элемент типа int занимает 4 байта, т.е. m = 232. Элементы типа int трактуются в Си как числа со знаком. Минимальное отрицательное число равно -231 = -2147483648, максимальное положительное равно 231-1 = 2147483647.

При описании переменной сначала указывается базовый тип, затем - имя переменной или список имен, разделенных запятыми, например,

int x;
int y, z, t;

При описании переменных можно присваивать им начальные значения:

    
int maxind = 1000;
int a = 5, b = 7;

Кроме типа int, существуют еще три целочисленных типа: char, short и long.

Тип char

Тип char представляет целые числа в диапазоне от -128 до 127. Элементы типа char занимают один байт памяти. Слово "char" является сокращением от character, что в переводе означает "символ". Действительно, традиционно символы представляются их целочисленными кодами, а код символа занимает один байт (см. раздел ). Тем не менее, подчеркнем, что элементы типа char - это именно целые числа, с ними можно выполнять все арифметические операции. С математической точки зрения, элементы типа char - это элементы кольца вычетов m = Z256. Стандарт Си не устанавливает, трактуются ли элементы типа char как знаковые или беззнаковые числа, но большинство Си-компиляторов считают char знаковым типом. Примеры описаний переменных типа char:

char c;
char eof = (-1);
char letterA = ′A′;

В последнем случае значение переменной "letterA" инициализируется кодом латинской буквы ′A′, т.е. целым числом 65. В Си символьные константы записываются в одинарных апострофах и означают коды соответствующих символов в кодировке ASCII. Рассмотрим следующий пример:

    
char c = 0;
char d = ′0′;

Здесь переменная c инициализируется нулевым значением, а переменная d - значением 48, поскольку символ ′0′ имеет код 48.

Типы short и long

Слова short и long означают в Си короткое и длинное целое число со знаком. Стандарт Си не устанавливает конкретных размеров для типов short и long. В самой распространенной в настоящее время 32-разрядной архитектуре переменная типа short занимает 2 байта (диапазон значений - от -32768 до 32767), а тип long совпадает с типом int, размер его равен четырем байтам. Примеры описаний:

short s = 30000;
long x = 100000;
int y = 100000;

В 32-разрядной архитектуре переменные x и y имеют один и тот же тип.

Модификатор unsigned

Типы int, short и long представляют целые числа со знаком. Для типа char стандарт Си не устанавливает явно наличие знака, однако большинство компиляторов трактуют элементы типа char как целые числа со знаком в диапазоне от -128 до 127. Если необходимо трактовать целые числа как неотрицательные, или беззнаковые, следует добавить модификатор unsigned при описании переменных. Примеры:

unsigned char c = 255;
unsigned short s = 65535;
unsigned int i = 1000000000;
unsigned j = 1;

При описании типа "unsigned int" слово "int" можно опускать, что и сделано в последнем примере.

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

Имеется также модификатор signed (знаковый). Его имеет смысл использовать на тех платформах, в которых тип char является беззнаковым. Пример описания:

signed char d = (-1);
Вещественные типы

Вещественных типов два: длинное вещественное число double (переводится как "двойная точность") и короткое вещественное число float (переводится как "плавающее"). Вещественные типы были подробно рассмотрены в разделе 1.4.2. Вещественное число типа double занимает 8 байтов, типа float - 4 байта.

Тип double является основным для компьютера. Тип float - это, скорее, атавизм, оставшийся от ранних версий языка Си. Компьютер умеет производить арифметические действия только с элементами типа double, элементы типа float приходится сначала преобразовывать к double. Точность, которую обеспечивает тип float, низка и не достаточна для большинства практических задач. Все стандартные функции математической библиотеки работают только с типом double. Рекомендуем вам никогда не использовать тип float!

Примеры описаний вещественных переменных:

double x, y, z;
double a = 1.5, b = 1e+6, c = 1.5e-3;

В последних двух случаях использовалось задание вещественных констант в экспоненциальной форме (см. раздел 1.4.2).

Логический тип

В языке Си специального логического типа нет, вместо него используются переменные целого типа. Значению "истина" соответствует любое ненулевое целое число, значению "ложь" - ноль. Например, в Си допустим такой фрагмент программы:

int b;
double s;
. . .
if (b) {
    s = 1.0;
}

Здесь целочисленная переменная b используется в качестве условного выражения в операторе if ("если"). Если значение b отлично от нуля, то выполняется тело оператора if, т.е. переменной s присваивается значение 1.0; если значение b равно нулю, то тело оператора if не выполняется.

На самом деле, приведенный пример представляет собой дурной стиль программирования. Гораздо яснее выглядит следующий фрагмент, эквивалентный приведенному выше:

if (b != 0) {
    s = 1.0;
}

В более строгом языке Java второй фрагмент корректен, а первый нет.

Язык C++ вводит логический тип bool в явном виде (отметим, что этот тип появился в C++ далеко не сразу!). Переменные типа bool принимают два значения: false и true (истина и ложь). Слова false и true являются ключевыми словами языка C++.

Примеры описания логических переменных в C++:

bool a, b;
bool c = false, d = true;
Оператор sizeof

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

int i; char c; short s; long l;
double d; float f; bool b;

Тогда приведенные ниже выражения в 32-разрядной архитектуре имеют следующие значения:

размер переменнойразмер типазначение
sizeof(i)sizeof(int)4
sizeof(c)sizeof(char)1
sizeof(s)sizeof(short)2
sizeof(l)sizeof(long)4
sizeof(d)sizeof(double)8
sizeof(f)sizeof(float)4
sizeof(b)sizeof(bool)1
Тип void

Слово void означает "пустота". Тип void в Си обозначает отсутствие чего-либо там, где обычно предполагается описание типа. Например, функция, не возвращающая никакого значения, в Си описывается как возвращающая значение типа void:

void f(int x);

Другое применение ключевого слова void состоит в описании указателя общего типа, когда заранее не известен тип объекта, на который он будет ссылаться.

Конструирование новых типов

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

Массивы

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

int a[10];
char c[256];
double d[1000];

В первой строке описан массив целых чисел из 10 элементов. Подчеркнем, что нумерация в Си всегда начинается с нуля, так что индексы элементов массива изменяются в пределах от 0 до 9. Во второй строке описан массив символов из 256 элементов (индексы в пределах 0...255), в третьей - массив вещественных чисел из 1000 элементов (индексы в пределах 0...999). Для доступа к элементу массива указывается имя массива и индекс элемента в квадратных скобках, например,

    
a[0], c[255], d[123].

Оператор sizeof возвращает размер всего массива в байтах, а не в элементах массива. В данном примере

sizeof(a) = 10*sizeof(int) = 40,  
sizeof(c) = 256*sizeof(char) = 256,  
sizeof(d) = 1000*sizeof(double) = 8000. 
Указатели

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

При описании указателя надо задать тип объектов, адреса которых будут содержаться в нем. Перед именем указателя при описании ставится звездочка, чтобы отличить его от обычной переменной. Примеры описаний указателей:

int *a, *b, c, d;
char *e;
void *f;

В первой строке описаны указатели a и b на тип int и простые переменныe c и d типа int (c и d - не указатели!).

С указателями возможны следующие два действия:

  1. присвоить указателю адрес некоторой переменной. Для этого используется операция взятия адреса, которая обозначается амперсендом &. Например, строка
    a = &c;
    
    указателю a присваивает значение адреса переменной c;
  2. получить объект, адрес которого содержится в указателе; для этого используется операция звездочка ′*′, которая записывается перед указателем. (Заметим, что звездочкой обозначается также операция умножения.) Например, строка
    d = *a;
    
    присваивает переменной d значение целочисленной переменной, адрес которой содержится в a. Так как ранее указателю a был присвоен адрес переменной c, то в результате переменной d присваивается значение c, т.е. данная строка эквивалентна следующей:
    d = c;
    

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

Сложные описания

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

  • для группировки можно использовать круглые скобки, например, описание
    int *(x[10]);
    
    означает "массив из 10 элементов типа указатель на int";
  • при отсутствии скобок приоритеты конструкций описания распределены следующим образом:
    • - операция * определения указателя имеет самый низкий приоритет. Например, описание
      int *x[10];
      
      означает "массив из 10 элементов типа указатель на int". Здесь к имени переменной x сначала применяется операция определения массива [] (квадратные скобки), поскольку она имеет более высокий приоритет, чем звездочка. Затем к полученному массиву применяется операция определения указателя. В результате получается "массив указателей", а не указатель на массив! Если нам нужно определить указатель на массив, то следует использовать круглые скобки при описании:
      int (*x)[10];
      
      Здесь к имени x сначала применяется операция * определения указателя;
    • операции определения массива [] (квадратные скобки после имени) и определения функции (круглые скобки после имени) имеют одинаковый приоритет, более высокий, чем звездочка. Примеры:
      int f();
      
      Описан прототип функции f без аргументов, возвращающей значение типа int.
      int (*f())[10];
      
      Описан прототип функции f без аргументов, возвращающей значение типа указатель на массив из 10 элементов типа int;
  • последний пример уже не является очевидным. Общий алгоритм разбора сложного описания можно охарактеризовать как чтение изнутри. Сначала находим описываемое имя. Затем определяем, какая операция применяется к имени первой. Если нет круглых скобок для группировки, то это либо определение указателя (звездочка слева от имени), либо определение массива (квадратные скобки справа от имени), либо определение функции (круглые скобки справа от имени). Таким образом получается первый шаг сложного описания. Затем находим следующую операцию описания, которая применяется к уже выделенной части сложного описания, и повторяем это до тех пор, пока не исчерпаем все описание. Проиллюстрируем этот алгоритм на примере:
    void (*a[100])(int x);
    
    Описывается переменная a. К ней сначала применяется операция описания массива из 100 элементов, далее - определение указателя, далее - функция от одного целочисленного аргумента x типа int, наконец - определение возвращаемого типа int. Описание читается следующим образом:
    1. a - это
    2. массив из 100 элементов типа
    3. указатель на
    4. функцию с одним аргументом x типа int, возвращающую значение типа
    5. void.

    Ниже расставлены номера операций в порядке их применения в описании переменной a:

    void (*  a  [100])(int x);
    5)    3) 1) 2)    4)
    
Строки

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

char str[10];
str[0] = ′e′; str[1] = ′2′;
str[2] = ′e′; str[3] = ′4′;
str[4] = 0;

Описан массив str из 10 символов, который может представлять строку длиной не более 9, поскольку один элемент должен быть зарезервирован для терминирующего нуля. Далее в массив str записывается строка "e2e4". Строка терминируется нулевым символом. Всего запись строки использует 5 первых элементов массива str с индексами 0...4. Последние 5 элементов массива не используются. Массив можно инициализировать непосредственно при описании, например

    
char t[] = "abc";

Здесь мы не указываем в квадратных скобках размер массива t, компилятор его вычисляет сам. После операции присваивания записана строковая константа "abc", которая заносится в массив t. В результате компилятор создает массив t из четырех элементов, поскольку на строку отводится 4 байта, включая терминирующий ноль.

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

const char *s = "abcd";

создается указатель s, а также строка символов "abcd", строка помещается в область статической памяти, защищенную от изменения, а в указатель s помещается адрес начала строки. Строка содержит 5 элементов: коды символов abcd и терминирующий нулевой байт.

Модификатор const

Константы в Си можно задавать двумя способами:

  • с помощью директивы #define препроцессора. Например, строка
        
    #define MILLENIUM 1000
    
    задает символическое имя MILLENIUM для константы 1000. Препроцессор всюду в тексте заменяет это имя на константу 1000, используя текстовую подстановку. Это не очень хороший способ, поскольку при таком задании отсутствует контроль типов;
  • с помощью модификатора const. При описании любой переменной можно добавить модификатор типа const. Например, вместо #define можно использовать следующее описание:
        
    const int MILLENIUM = 1000;
    
    Модификатор const означает, что переменная MILLENIUM является константой, т.е. менять ее значение нельзя. Попытка присвоить новое значение константе приведет к ошибке компиляции:
        
    MILLENIUM = 100; // Ошибка: константу
                         //         нельзя изменять
    

При описании указателя модификатор const, записанный до звездочки, означает, что описан указатель на константный объект, т.е. на объект, менять который нельзя или запрещено. Например, в строке

const char *p;

описан указатель на константную строку (массив символов, менять который запрещено).

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

Константный указатель ссылается на константный объект, однако, содержимое самого указателя может изменяться. Например, следующий фрагмент вполне корректен:

const char *str = "e2e4";
. . .
str = "c7c5";

Здесь константный указатель str сначала содержит адрес константной строки "e2e4". Затем в него записывается адрес другой константной строки "c7c5".

В Си можно также описать указатель, значение которого не может быть изменено; для этого модификатор const указывается после звездочки. Например, фрагмент кода

int i;
int * const p = &i;

навечно записывает в указатель p адрес переменной i, перенаправить указатель p на другую переменную уже нельзя. Строка

    
p = &n;

является ошибкой, т.к. указатель p - константа, а константе нельзя присвоить новое значение. Указатели, значения которых изменять нельзя, используются в Си значительно реже, в основном при заполнении константных таблиц.

Модификатор volatile

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

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

Пример описания переменной:

volatile int inputPort;

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

Оператор typedef

В языке Си можно задать имя типа, если его описание достаточно громоздко и его не хочется повторять много раз. В дальнейшем можно использовать имя типа при описании переменных. Для определения типа применяется оператор typedef. Синтаксически оператор typedef аналогичен обычному описанию переменной, к которому в самом начале добавлено слово typedef. При этом вместо переменной определяется имя нового типа. Сравните следующее описание переменной "real" и определение нового типа "Real":

double real;         // Описание переменной real
typedef double Real; // Определение нового типа Real,
                         // эквивалентного типу double.

Мы как бы описываем переменную, добавляя к описанию слово typedef. При этом описываемое имя становится именем нового типа. Его можно использовать затем для задания переменных:

Real x, y, z;

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

typedef bool (*callback)(int);

Строка, описывающая три переменные p, g, r,

callback p, q, r;

эквивалентна строке

bool (*p)(int), (*q)(int), (*r)(int);

но первая строка, конечно, понятнее и нагляднее.

Еще одна цель использования оператора typedef состоит в том, чтобы сделать текст программы менее зависимым от особенностей конкретной архитектуры (разрядности процессора, конкретного Си-компилятора и т.п.). Например, в старых Си-компиляторах, которые использовались для 16-разрядных процессоров Intel 80286, существовали так называемые близкие (near) и далекие (far) указатели. В эталонном языке Си ключевых слов near и far нет, они использовались лишь в Си-компиляторах для Intel 80286 как расширение языка. Поэтому, чтобы тексты программ не зависели от компилятора, в системных h-файлах с помощью оператора typedef определялись имена для типов указателей, а в текстах программ использовались не типы эталонного языка Си, а введенные имена типов. Например, тип "далекий указатель на константную строку" в соответствии с соглашениями фирмы Microsoft называется LPCTSTR (Long Pointer to Constant Text STRing). При использовании 16-разрядного компилятора он определяется в системных h-файлах как

typedef const char far *LPCTSTR;

в 32-разрядной архитектуре он определяется без ключевого слова far (поскольку в ней все указатели "далекие"):

typedef const char *LPCTSTR;

Во всех программах указатели на константные строки описываются как имеющие тип LPCTSTR:

    
LPCTSTR s;

благодаря этому программы Microsoft можно использовать как в 16-разрядной, так и в 32-разрядной архитектуре.

Выражения

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

Отметим, что, помимо обычных операций, таких, как сложение или умножение, в Си существует ряд операций, несколько непривычных для начинающих. Например, запятая и знак равенства (оператор присваивания) являются операциями в Си; помимо операции сложения +, есть еще операция увеличить на += и операция увеличения на единицу ++. Зачастую они позволяют писать эстетически красивые, но не очень понятные для начинающих программы.

Впрочем, эти непривычные операции можно не использовать, заменяя их традиционными.

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

Оператор присваивания является основой любого алгоритмического языка (см. лекцию 3). В Си он записывается с помощью символа равенства, например, строка

x = 100;

означает присвоение переменной x значения 100. Для сравнения двух значений используется двойное равенство ==, например, строка

bool f = (2 + 2 == 5);

присваивает логической переменной f значение false (поскольку 2+2 не равно пяти, логическое выражение в скобках ложно).

Непривычным для начинающих может быть то, что оператор присваивания "=" в Си - бинарная операция, такая же, как, например, сложение или умножение. Значением операции присваивания = является значение, которое присваивается переменной, стоящей в левой части. Это позволяет использовать знак присваивания внутри выражения, например,

x = (y = sin(z)) + 1.0;

Здесь в скобках стоит выражение y = sin(z), в результате вычисления которого переменной y присваивается значение sin z. Значением этого выражения является значение, присвоенное переменной y, т.е. sin z. К этому значению затем прибавляется единица, т.е. в результате переменной x присваивается значение sin z+1.

Выражения, подобные приведенному в этом примере, иногда используются, когда необходимо запомнить значение подвыражения (в данном случае sin (z)) в некоторой переменной (в данном случае y), чтобы затем не вычислять его повторно. Еще один пример:

n = (k = 3) + 2;

В результате переменной k присваивается значение 3, а переменной n - значение 5. Конечно, в нормальных программах такие выражения не встречаются.

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

К четырем обычным арифметическим операциям сложения +, вычитания -, умножения * и деления / в Си добавлена операция нахождения остатка от деления первого целого числа на второе, которая обозначается символом процента %. Приоритет у операции вычисления остатка % такой же, как и у деления или умножения. Отметим, что операция % перестановочна с операцией изменения знака (унарным минусом), например, в результате выполнения двух строк

x = -(5 % 3);
y = (-5) % 3;

обеим переменным x и y присваивается отрицательное значение -2.

Операции увеличения и уменьшения

В Си добавлены операции увеличения и уменьшения на единицу, которые, к примеру, очень удобно применять к счетчикам. Операция увеличения записывается с помощью двух знаков сложения ++, операция уменьшения - с помощью двух минусов --. Например, операция ++, примененная к целочисленной переменной i, увеличивает ее значение на единицу:

++i;   эквивалентно i = i+1 

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

double x;
. . .
++x;  // Ошибка! Операция ++ неприменима
      //         к вещ. переменной

Операция ++ увеличивает значение переменной на "минимальный атом". Так как для вещественных переменных такого "атомарного" значения нет, операции увеличения и уменьшения для них запрещены.

Для указателей операция ++ увеличивает значение переменной на размер одного элемента того типа, на который ссылается указатель. Для указателя "атомом" является один элемент заданного типа, поэтому размер одного элемента и является шагом изменения значения указателя. Это очень естественно, т.к. после увеличения указатель будет содержать адрес следующего элемента данного типа, а после уменьшения - адрес предыдущего элемента. Пример:

double a[100];
double *p = &(a[15]); // в p записывается адрес
                      // элемента массива a[15]
++p; // в p будет адрес элемента a[16]
     // (адрес увеличивается на sizeof(double) == 8)

Описаны массив a вещественных чисел типа double и указатель p на элементы типа double. При описании указателя p в него заносится начальное значение, равное адресу элемента a[15] массива a. После выполнения операции увеличения ++ в переменной p будет содержаться адрес следующего элемента a[16]. Физически содержимое переменной p увеличивается на размер одного элемента типа double, т.е. на 8.

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

++x;    // Префиксная форма
x--;    // Суффиксная форма

Разница между префиксной и суффиксной формами проявляется только при вычислении сложных выражений. Если используется префиксная форма операции ++, то сначала переменная увеличивается, и только после этого ее новое значение используется в выражении. При использовании суффиксной формы значение переменной сначала используется в выражении и только затем увеличивается. Примеры:

int x = 5, y = 5, a, b;
a = (++x) + 2; // переменной a присваивается значение 8
b = (y++) + 2; // переменной b присваивается значение 7

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

Дадим два совета (возможно, не бесспорные) по использованию операций ++ и --:

  • никогда не применяйте эти операции в сложных выражениях! Ничего, кроме путаницы, это не дает. Например, вместо фрагмента
    double *p, x, y;
    . . .
    y = *p++ + x;
    
    лучше использовать фрагмент
     
    double *p, x, y;
    . . .
    y = *p + x;
    ++p;
    
    С точки зрения компилятора, они абсолютно эквивалентны, но второй фрагмент проще и понятнее (и, значит, вероятность ошибки программирования меньше);
  • всегда отдавайте предпочтение префиксной форме операций ++ и --. Например, вместо фрагмента
    int x, y;
    . . .
    x++; y--; // Используется суффиксная форма
    
    лучше использовать фрагмент
    int x, y;
    . . .
    ++x; --y; // Лучше применять префиксную форму
    

Операции "увеличить на", "домножить на" и т.п.

В большинстве алгоритмов при выполнении операции сложения чаще всего переменная-результат операции совпадает с первым аргументом:

x = x + y;

Здесь складываются значения двух переменных x и y, результат помещается в первую переменную x. Таким образом, значение переменной x увеличивается на значение y. Подобные фрагменты встречаются в программах гораздо чаще, чем фрагменты вида

x = y + z;

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

double a[100];
double s;
int i;
. . .
s = 0.0;
i = 0;
while (i < 100) {
    s = s + a[i];
    ++i;
}

Здесь сумма элементов массива накапливается в переменной s. В строке

s = s + a[i];

к сумме s прибавляется очередной элемент массива a[i], т.е. значение s увеличивается на a[i]. В Си существует сокращенная запись операции увеличения:

s += a[i];

Оператор += читается как "увеличить на". Строка

x += y;    // Увеличить значение x на y

эквивалентна в Си строке

x = x + y; // x присвоить значение x + y,

но короче и нагляднее.

Оператор вида ?= существует для любой операции ?, допустимой в Си. Например, для арифметических операций +, -, *, /, % можно использовать операции

+= увеличить на
-= уменьшить на
*= домножить на
/= поделить на
%= поделить с остатком на

к примеру, строка

x *= 2.0;

удваивает значение вещественной переменной x.

Операторы вида ?= можно использовать даже для операций ?, которые записываются двумя символами. Например, операции логического умножения и сложения (см. раздел 1.4.4) записываются в Си как && (двойной амперсенд) и || (двойная вертикальная черта). Соответственно, логические операторы "домножить на" и "увеличить на" записываются в виде &&= и ||=, например,

bool x, y;
x &&= y;    // эквивалентно x = x && y;
x ||= y;    // эквивалентно x = x || y;

Логические операции

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

|| логическое "или" (логическое сложение)
&& логическое "и" (логическое умножение) 
! логическое "не" (логическое отрицание)

Логические константы "истина" и "ложь" обозначаются через true и false (это ключевые слова языка). Примеры логических выражений:

bool a, b, c, d;
int x, y;

a = b || c;            // логическое "или"
d = b && c;            // логическое "и"
a = !b;                // логическое "не"
a = (x == y);          // сравнение в правой части
a = false;             // ложь
b = true;              // истина
c = (x > 0 && y != 1); // c истинно, когда
                           // оба сравнения истинны

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

Чрезвычайно важной особенностью операций логического сложения и умножения является так называемое "сокращенное вычисление" результата. А именно, при вычислении результата операции логического сложения или умножения всегда сначала вычисляется значение первого аргумента. Если оно истинно в случае логического сложения или ложно в случае логического умножения, то второй аргумент операции не вычисляется вовсе! Результат операции полагается истинным в случае логического сложения или ложным в случае логического умножения. Подробно это рассмотрено в разделе 1.4.4.

Операции сравнения

Операция сравнения сравнивает два выражения. В результате вырабатывается логическое значение - true или false (истина или ложь) в зависимости от значений выражений. Примеры:

bool res;
int x, y;
res = (x == y); // true, если x равно y, иначе false
res = (x == x); // всегда true
res = (2 < 1);  // всегда false

Операции сравнения в Си обозначаются следующим образом:

== равно,  != не равно,
> больше,  >= больше или равно,
< меньше,  <= меньше или равно.

Побитовые логические операции

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

& побитовое логическое сложение ("и")
| побитовое логическое умножение ("или")
~ побитовое логическое отрицание ("не")
^ побитовое сложение по модулю 2 (исключающее "или")

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

Ни в коем случае не используйте побитовые операции в качестве логических условий, это может приводить к непредсказуемым ошибкам!

В основном побитовые операции применяются для манипуляций с битовыми масками. Например, пусть целое число x описывает набор признаков некоторого объекта, состоящий из четырех признаков. Назовем их условно A, B, C, D. Пусть за признак A отвечает нулевой бит слова x (биты в двоичном представлении числа нумеруются справа налево, начиная с нуля). Если бит равен единице (программисты говорят бит установлен), то считается, что объект обладает признаком A. За признаки B, C, D отвечают биты с номерами 1, 2, 3. Общепринятая практика состоит в том, чтобы определить константы, отвечающие за соответствующие признаки (их обычно называют масками):

const int MASK_A = 1;
const int MASK_B = 2;
const int MASK_C = 4;
const int MASK_D = 8;

Эти константы содержат единицу в соответствующем бите и нули в остальных битах. Для того чтобы проверить, установлен ли в слове x бит, соответствующий, к примеру, признаку D, используется операция побитового логического умножения. Число x умножается на константу MASK_D; если результат отличен от нуля, то бит установлен, т.е. объект обладает признаком D, если нет, то не обладает. Такая проверка реализуется следующим фрагментом:

 
if ((x & MASK_D) != 0) {
    // Бит D установлен в слове x, т.е.
    // объект обладает признаком D
    . . .
} else {
    // Объект не обладает признаком D
    . . .
}

При побитовом логическом умножении константа MASK_D обнуляет все биты слова x, кроме бита D, т.е. как бы вырезает бит D из x. В двоичном представлении это выглядит пК началу статьи





Добавил: MadvEXДата публикации: 2006-02-28 01:45:41
Рейтинг статьи:5.00 [Голосов 5]Кол-во просмотров: 14612

Комментарии читателей

Всего комментариев: 7

2017-06-19 15:39:28
GeorgeKax
Получить первые результаты от применения вы сможете уже через несколько процедур Кожа станет заметно более упругой, гладкой, шелковистой Кроме того, вас покинут неприятный зуд и жжение, уйдут покраснения Препарат Tinedol, отзывы на который вы найдете в сети, поможет вам не только избавится от уже имеющейся проблемы, но и предотвратить заражение в принципе вы можете использовать крем в качестве профилактики В инструкции по применению все подробно описано.
Чтобы крем оказывал нужное воздействие на пораженную грибком кожу, его следует правильно хранить Это должно быть темное сухое место с температурой не выше 25 градусов Следует предотвратить попадания на упаковку прямых солнечных лучей.
4 6 Решение о выдаче не выдаче персональных данных, принимается Администрацией, лишь на основании запроса, посланного лицом Администрации, в установленном нормами действующего законодательства порядке.
Покраснение стоп, сильный зуд, неприятный запах все это указывает на грибковую инфекцию От подобного паразита тяжело избавиться, и многие медикаменты не справляются с этой задачей Быстро устранить все неприятные изменения на ногах при заражении грибком поможет инновационное средство Tinedol Препарат содержит только природные компоненты Что представляет собой Tinedol Мазь Tinedol hellip.
Глазные капли с гиалуроновой кислотой в полимерном контейнере, объемом 0,4 мл содержат Гиалуронат натрия 0,4 мг; Сукцинатный буфер рН 7,3 до 0,4 мл.
Tinedol представляет собой инновационную разработку ведущих специалистов, которая позволяет не просто удалить грибок, но и устранить все проблемы с кожей, возникшие из-за развития недуга Данная проблема, при возникновении, довольно быстро поражает большие участки дермы, ухудшает состояние ногтей и вызывает другие неприятные процессы.
2 165 Крем Сидор Тихонов 04 04 2017 15 14.
7 Время года.
Чтобы быстро достичь желаемого результата, использовать крем от грибка Tinedol следует регулярно и в соответствии с инструкцией по применению Средство предназначено исключительно для наружного применения и наноситься должно дважды в сутки на чистую и сухую кожу стоп При этом совсем не обязательно наносить большое количество крема по всей поверхности стопы, достаточно будет лишь покрыть тонким слоем только пораженные грибком участки кожи и ногтей.
Препарат не имеет противопоказаний, так как содержащиеся в нем компоненты не вызывают побочных эффектов В инструкции к средству можно найти информацию о том, что от крема следует отказаться лишь в одном случае при индивидуальной непереносимости одного или нескольких компонентов Также стоит обратить внимание на то, что Тинедол нужно хранить в сухом месте, куда не попадают прямые солнечные лучи Температура хранения должна составлять не менее 5 С и не более 25 С.


Официальный сайт: http://tinedol.1stbest.info/

2017-06-11 20:33:49
Derekbot
Red Machine (Рэд Мэщин капсулы для потенции. Global-Trend.Club Лучшие мировые трендовые товары. 365 отзывов, 85 положительных. Сообщить . biomanix56.moykrest.ru

2017-06-09 19:10:51
Мебель каталог
Каждый из нас когда-нибудь сталкивается с необходимостью выбора новой мебели. Простой на первый взгляд, этот шаг очень важный и ответственный, особенно если дело касается выбора спальни, купить которую в отличном качестве - значит обеспечить себе полноценный здоровый отдых. Многие ограничиваются посещением одного магазина, расположенного по соседству с домом и делают выбор исходя из ценовой политики продавца, покупая мебель подешевле. Но, покупая спальни недорого, нужно обязательно ориентироваться на качество мебели. В магазине не каждый обращает внимание на отделку, на присутствие постороннего запаха от ДСП, некоторые забывают измерить размеры комнаты, где будет стоять мебель.
Поэтому, чтобы покупка радовала вас долгие годы и дарила только положительные эмоции, нужно заранее продумать, какой должна быть спальня. Москва - город, позволяющий найти в достаточном количестве добросовестных производителей и поставщиков мебели, продукция которых соответствует самым строгим требованиям современного покупателя.В их магазинах можно получить гарантии качества и квалифицированную консультацию по поводу правильного подбора мебели, уходу за ней.


Официальный сайт: http://1stbest.info/

2017-06-09 19:07:45
JosefTromy
Немного о составе препарата.
В отличие от многих конкурирующих препаратов противогрибкового и антимикробного действия, Тинедол не содержит вредных химикатов, канцерогенов и консервантов Применение препарата полностью безопасно благодаря его запатентованной растительной формуле Все ингредиенты активно взаимодействуют друг с другом, оказывая стойкий противогрибковый эффект на ногти и кожу стоп.
6 5 Контактные данные администрации.
Продукт предлагается по доступной цене 990 рублей за тюбик, которого хватит на полноценный лечебный курс Однако расход средства определяется областью поражения Также рекомендуется использовать препарат в профилактических целях тем, у кого в окружении находится человек, зараженный грибком стопы.
Lanolin ланолин способствует питанию кожи, а также ее смягчению и увлажнению, защищает дерму от воздействия вредных бактерий и микроорганизмов, препятствует их распространению.
Однажды после очень жаркого лета я заметил, что кожа между пальцами покраснела Мне подумалось, что повода для беспокойства нет Но вскоре я очень пожалел об этом Стопы стали покрываться трещинами, кожа начала шелушиться Постоянный зуд не давал покоя Особенно тяжело было на работе целый день в носках и туфлях это настоящее испытание Дома я тоже не мог расслабиться, ведь у меня есть два сына и я не хотел их заразить Перепробовал огромное число кремов и присыпок, и ничего не помогало Однажды встретил школьную подругу, которая посоветовала мне Tinedol Хоть я и сомневался, но все же купил мазь Целый месяц я исправно наносил ее на ноги и, к счастью, проблема стала уходить Решил взять еще одну упаковку, чтобы завершить лечение полностью Через два месяца моя кожа выглядела чистой, без воспалений И никакого грибка больше нет.
Климбазол устраняет такие симптомы инфекции, как воспаления, зуд, а также покраснения, убивает вредные бактерии Гипоаллергенный.
ООО ЮРиЯ-ФАРМ , Украина, 03141, г Киев, ул Н Амосова, 10 на заводе ООО ЮРиЯ-ФАРМ , Украина, 18030, г Черкасы, ул Вербовецкого, 108 Тел 044 281-01-01.
Основные причины возникновения инфекции на ногах.
витамин Е усиливает регенерацию тканей, избавляет от шелушения, смягчает.


Официальный сайт: http://tinedol.1stbest.info/

2011-12-18 17:58:15
cookybreed
очень доступно,спасибо большое)

2010-11-01 22:24:38
ЯЯЯЯ
Да спасибо большое, правда очень помогло. Кратко, четко и все понятно даже для меня)))

2010-02-11 11:38:32
БагИра
Супер. Готовые лекции для моих студентов!!!!! кратко, емко и всем понятно!!!!!! Спасибо!!!
Ваше имя: *
Текст записи: *
Имя:

Пароль:



Регистрация

Как вы относитесь к интернет-магазинам?
Не доверяю
18% (16)
Ни разу не пользовался и не буду
16% (15)
Пользовался единожды
12% (11)
Пользуюсь регулярно
26% (24)
Пользовался бы, но нет денег
18% (16)
А что такие бывают?
2% (2)
Обычные магазины лучше!
8% (7)

Проголосовало: 91
Берегись дьявольской дыры в киберпро..в;.??/":?;/..............
Рейтинг: 5.5/10 (2)
Посмотреть все анекдоты