Создать объект, который обрабатывает переменную целого типа максимальной длин
Создать объект, который обрабатывает переменную целого типа максимальной длины.
У объекта есть закрытое свойство n целого типа максимальной длины.
Объект обладает следующей функциональностью:
— определяет значение свойства n;
— вычисляет количество цифр значения свойства n;
— возвращает количество цифр значения свойства n.
Написать программу, которая:
1. Создает объект.
2. Вводит значение переменной целого типа.
3. Определяет значение свойства n по значению переменной целого типа.
4. Вычисляет количество цифр свойства n.
5. Выводит значение свойства n.
6. Выводит количество цифр значения свойства n.
Входные данные
Первая строка:
Целое число в десятичном формате.
Выходные данные
Первая строка:
n = значение свойства n
Вторая строка:
N = количество цифр свойства n
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
Ответы с готовыми решениями:
Создать указатель, который сможет указывать объект любого типа.
Привет всем, я в програмировании ни чего не смыслю поэтому обращаюсь к вам, помогите составить.
Как в QMessageBox вывести переменную целого типа?
Имеется некоторая переменная типа INT; Как с помощью QMessageBox вывести её на экран? Пробовал.
Как в переменную получить объект UI на который направлена мышь?
Есть переменная типа GameObject.Нужно задать ей UI объект на который наведена мышь
Не удаётся создать безымянный объект типа vector моего типа
Собсно #include <vector> using namespace std; //Вот мой австорский тип struct kletka < .
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
Помогаю со студенческими работами здесь
Создать переменную типа, хранящегося в переменной типа Type
Здравствуйте. Гугл не помог, потому обращаюсь за помощью к вам. Пишу загрузку (не важно чего) из.
Как бы завести свою переменную(или объект?) типа TComboBox?
Есть несколько ComboBox-ов, от котрых требуется буквально одно и тоже, возникает желание написать.
Реализовать открытый метод, который генерирует Kol_el случайных чисел целого типа из диапазона
В Class1 реализовать открытый метод MyNext, который генерирует Kol_el случай-ных чисел целого типа.
Создать файл F Целого типа
Создать файл F Целого типа.Получить два файла :F1,F2.В файл F1 последовательно писать четные.
Создать динамический массив целого типа
Создать динамический массив целого типа на 100 элементов
Создать объект, который обрабатывает массив целых чисел
Начали недавно изучать ООП в c++
Поставлена следующая задача:
Создать объект, который обрабатывает массив целых чисел не более 10 элементов.
Количество элементов определяются в момент конструирования объекта.
Объект обладает следующей функциональностью:
— в конструкторе считывает количество элементов массива, выводит количество элементов;
— считывает элементы массива;
— выводит элементы массива;
— разворачивает последовательность элементов массива.
Написать программу, которая:
1. Создает объект и в конструкторе считывает количество элементов массива;
2. Считывает элементы массива;
3. Выводит значения элементов массива согласно исходной последовательности;
4. Разворачивает элементы массива;
5. Выводит значения элементов массива согласно новому их порядку следования.
Входные данные
Первая строка:
целое число в десятичном формате.
Вторая строка:
последовательность целых чисел в десятичном формате разделенных пробелом.
Выходные данные
Первая строка:
N = количество элементов
Вторая строка (исходный порядок следования элементов):
Значения элементов массива, значение каждого элемента занимает 5 позиции, выравнивание по правому краю.
Третья строка (порядок следования элементов после разворота):
Значения элементов массива, значение каждого элемента занимает 5 позиции, выравнивание по правому краю.
Мне не понятны некоторые пункты:
Во первых мне нужно юзать динамический массив т.к. кол-во элементов не известно на момент компиляции, соответственно при передачи его в класс, я количество уже никак посчитать не смогу, ниже я скинул конечную программу, там я просто одним из параметров указал то самое введённое число и просто его вывожу, является ли это решением, или нужно делать что-то другое?
Во вторых мне совсем не понятен вывод — какие 5 позиций? Какое выравнивание? Что это вообще всё значит?
Вот мой код:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
#include using namespace std; class cl1 { public: cl1(int arr1[], int l) { cout "N = " l endl; for(int a = 0; al; a++) { cout arr1[a] " "; } cout endl; for( ;l>0;l--) { cout arr1[l-1] " "; } cout endl; } }; int main() { int k; int *arr = new int[k]; cin >> k; for(int a = 0; ak ; a++) { cin >> arr[a]; } cl1 obj(arr,k); return 0; }
С++ для тех кто не шарит
Около трех лет назад, когда я пошел в девятый класс, остро встал вопрос о том, что мне делать дальше. Было несколько вариантов, но в итоге я выбрал Киевский Колледж Связи, который, по словам знакомого, лучший в Киеве из области программирования. Ожидая потока знаний в мою голову, я стал сам потихоньку углубляться в IT и ко второму курсу, когда в учебной программе наконец начали появляться специализированные предметы, уже имел некоторый, относительно неплохой багаж знаний. Окрыленный надеждой получить если не все знания этого мира, то хотя бы половину, я с разбегу ударился в стену разумных доводов о том, что не стоит ожидать от этого места чего-то заоблачного. И вот я сижу на очередной паре и вставляю спички в глаза, ибо все это я успел выучить за первые две недели целенаправленного погружения в это «болото». К концу года, когда вот-вот должна начаться сессия и практика, я окончательно осознал, что на всю группу найдется максимум 3 человека, которые все поняли и могут спокойно оперировать полученными знаниями. (Двое из этих людей и так варились в этом программистском котле, а третий очень заинтересовался где-то к концу первой четверти и достиг очень неплохого уровня за минимальный промежуток времени). Тогда у меня и родилась идея написать максимально подробную «шпаргалку» для тех, кто заинтересован в успешной сдаче сессии но не понял что происходило весь прошлый год. К сожалению мало кто пользовался ею, по этому я решил предоставить ее более широкой общественности, ибо жалко добру пропадать.
Теперь по сути
Для начала стоило бы рассказать как работает любая программа на элементарном уровне.
Любая программа, будь то на телефоне или компьютере, строится на взаимодействии с оперативной памятью устройства. Вся RAM делится на ячейки по 1 байту каждая. Но для удобного использования все эти ячейки, когда до них доходит очередь, зачастую группируются для хранения большего объема данных. Так например целое число, если не вдаваться в подробности, храниться в блоке из четырех ячеек памяти, то есть занимает 4 байта памяти. В результате 32-битное целое число, а в 4 байтах именно 32 бита, может достигать значений от -2^31 до (2^31)-1. Степень 31, а не 32 потому что первый бит отвечает за знак числа, если 0 то +, если 1 то -.
Перейдем непосредственно к тому, как это выглядит на практике. Простейший пример:
int a; a = 1;
Сразу стоит оговориться что в конце почти каждой строки, за некоторыми исключениями, следует писать ;
Строка int a; это непосредственно выделение тех четырех байт памяти под целое число. Мы взяли где-то в памяти 4 байта, присвоили им имя а и теперь можем обращаться к этой памяти как к чему-то единому, называя это целочисленной переменной.
Строка a = 1; задает нашей переменной значение равное 1 , фактически это значит, что в те 4 байта памяти запишется нечто вот такое: 00000000 00000000 00000000 00000001 . Тут 31 ноль и 1 единица в конце, это двоичное представление числа, что в нашем случае не сильно отличается от десятичного. Это действие, присвоение переменной начального значения, называется инициализацией.
Теперь немного упростим эту запись:
int a = 1;
Это абсолютно то же самое, только короче. Более того, многие среды разработки для С++ не дадут запустить код, если вы попытаетесь прочитать значение у переменной, которой не было задано значения. По этому присваивать переменным какое-то значение перед основными действиями являеться хорошим тоном.
Теперь попробуем немного похимичить:
int a = 1; int b = 2; int c = a + b;
Не сложно догадаться чему будет равно c . Это, на самом деле, и есть вся суть программирования, на основе одних данных получать другие. Все что тут произошло, можно переписать в виде 3 = 1 + 2 .
А сейчас покажу как довести учителя математики до истерики.
int c = 1; c = c + 1; c += 1; c++;
Строки со второй по четвертую на самом деле делают одно и тоже: просто к значению переменной c прибавляют 1 . Первый способ это прировнять c к этой же c только + 1 . Старая двойка, которая там была, перезапишется новым значением, в данном случае тройкой. То же самое, что мы делали в первом блоке, но теперь не просто число, а математическое выражение. Второй способ называется сложение с присваиванием. Это действие прибавляет к изменяемой переменной то число, которое написано в правой части, и потом записывает, получившийся результат, в эту же переменную — тройка перезапишется четверкой. Есть так же -= , *= , /= , думаю достаточно очевидно, что они сделают. Ну и третий способ: два плюса под ряд возле переменной — четверка перезапишется пятеркой.
++ / — меняет значение ТОЛЬКО на единицу ( — отнимает единицу).
Все три способа на программном уровне работают одинаково, и один не является более правильным, а другой менее, просто где-то удобно так, а где-то по другому.
Попробуем записать в перменную результат математического выражения 5 / 2 :
int a = 5 / 2;
На выходе мы ожидаем получить 2.5 , поскольку 5 / 2 = 2.5 Но компилятор уже спешит нас обламать и выводит в консоль ровно 2. Вернемся к битам и байтам. Наше число 5 (если обрезать левые 24 нолика) выглядит вот так: 00000101 а число 2 выглядит так: 00000010
Поговорим о типах данных. Мы все время использовали целое число, соответственно, если мы попытаемся присвоить целочисленной переменной дробное число, то компьютеру ничего не останется, кроме как записать в переменную целую часть числа, а дробную просто отбросить.
В следующем примере попробуем получить дробное число. Для этого воспользуемся типом float при создании переменной а :
float a = 5. / 2;
После 5 стоит точка для того чтобы указать компилятору что нам требуеться дробный ответ. Если же оставить там 5 / 2 то ответ все равно будет целым числом.
Ура, дробное число получено, можешь отметить этот день в своем календаре)))
Попробуем разные способы установки значений для дробных чисел:
float a = 3; a = 3.5; a = 1.;
Все выглядит вполне логично, за исключением последнего варианта. В нем мы просто не указывали дробную часть, но указали что это число дробное, тут это не особо полезно, но в других случаях это может пригодиться.
И так, мы уже знаем как создавать и работать с числовыми переменными. Но на этом много не напрограммируешь, необходимо как-то получать и обрабатывать данные из вне. Наподобие команды cout есть функция cin , которая принимает данные и записывает их в переменную.
int a; cout > a; cout bool a = true; a = 1 == 1; a = 1 == 2; a = a == false; a = !a;
Строка h = 1 == 1; . Что здесь происходит? Мы сравниваем 1 и 1 , равна ли единица единице. Разумеется что ответ правда, по этому на выходе мы получим значение true .
Вот сейчас очень важно, = это приравнивание, с помощью этого оператора мы задаем значение, а == это сравнивание, мы сравниваем два значения.
И так, переходим на третью строку: a = 1 == 2; 1 не равно 2 (как неожиданно), по этому результат сравнения будет false . Четвертая строка демонстрирует возможность сравнивать переменную с каким то значением или другой переменной. Поскольку на прошлой строке у нас получилась ложь, то сравнение false и false вернет, ожидаемо, правду. Ну и последняя строка, на которой мы берем значение обратное значению переменной a . Простыми словами это выглядит как не правда , что по другому является ложью .
Что ж, мы теперь полностью готовы к познанию того, как обрабатывать данные.
if(true)
Это, так называемое, условие if(условие) < действие >. Дословно это можно перевести так: «Если утверждение в круглых скобках правдиво, то выполнить действие в фигурных скобках». В нашем случае утверждение в скобках всегда правдиво, поскольку мы туда передаем заведомо правдивое значение.
Теперь немного усложним предыдущее условие.
bool a = false; if(a) < cout else
Вторая часть это else < действие >, которая дословно обозначает "Иначе, если условие не выполнилось, то. ". В данном случае, поскольку наша переменная a равна false , то выполниться блок else
Продолжаем познавать все прелести условий.
int a; cout > a; if(a < 5)< cout else if(i > 5) < cout 5" else
На этот раз у нас добавился блок else if(условие) < действие >, который будет выполнен в случае если первое условие не сработало. Дословно это значит "Иначе если первое условие не сработало, то проверить другое условие". Стоит заметить, что таких блоков с дополнительными условиями может быть сколько угодно, главное соблюдать порядок: if -> else if -> else .
Условия это конечно превосходно, но вся их сила раскрывается в тандеме с циклами. Всего есть три вида циклов, что на самом деле ложь, так как два из них в своей основе содержат третий. Сейчас станет понятнее.
Разберем фундаментальный вид циклов. Выглядит он так: while(условие) < действие >, и дословно значит: "Повторять действие в фигурных скобках, до тех пор, пока правдиво условие в круглых".
int a = 0; while(a != 5)< cout > a; >
Правда вот есть один нюанс, если мы поместим на место условия true , то цикл будет повторяться вечно. В нашем случае цикл будет повторяться до тех пор, пока не будет введено число 5.
А что делать если нам нужно четко установить количество повторений цикла? Нужна дополнительная переменная, которая будет выступать в роли счетчика.
int k = 0; while(k < 5)< cout for (int i = 0; i < 5; i++) < cout int i = 0; do < cout int i = 5; do < cout int A[10]; cout
В начале, как и при создании переменных, у нас стоит тип данных, в нашем случае int . После идет название массива и в квадратных скобках его длинна, то есть то, сколько значений в него можно поместить. Попробуем не вводить никаких значений, а просто вывести массив в консоль. Что ж, это немного не то, чего мы ожидали. Вместо набора из десяти случайных чисел мы получили нечто, что называется адресом. Про адреса мы поговорим чуть позже, а сейчас просто перепишем вывод так, чтоб на экран вывелисль значения из ячеек массива
Нумерация в массивах, и вообще в программировании в целом, начинается с нуля, по этому первый элемент массива это 0, а последний - его длинна минус один.
Все выглядит вполне естественно, за исключением того, что мы не задали значения ячейкам массива, и вместо них вывелось то, что лежало в тех местах памяти которую занимают ячейки массива, то есть просто мусор.
То же самое произойдет если мы выведем обычную перменную, не присваивая ей значения. Правда вот не везде программа запуститься, ибо довольно часто компиляторы запрещают использовать неинициализированные переменные.
Вот и настал тот момент, когда мы будем использовать циклы для каких-то осмысленных действий. В данном случае используем цикл for . Значения переменной i будут меняться от 0 до 9, что нам идеально подходит для использования в массиве.
for (int i = 0; i < 10; i++) < A[i] = i; >for (int i = 0; i
Первый цикл задает значения ячейкам массива, а второй их читает и выводит в консоль. Теперь это проще в написании и чтении.
Теперь вернемся к адресам. Но для начала расскажу что такое ссылки и как их использовать. Ссылочная переменная, хранит в себе исключительно ссылку на первый байт переменной, на которую он ссылается (Каждая ячейка памяти имеет свой адрес, записанный в шестнадцатеричной системе. Для доступа к значению переменной по ссылке необходимо лишь знать ее тип данных и ссылку на первый байт). Объявляется путем прибавления к имени ссылочной переменной знака & .
int a = 2; int &ref = a; cout int a = 2; int *ptr = &a; cout int a = 2; cout int A[10]; A[0] = 3; A[1] = 1; cout int const а = 6; cout int const а = 6; float const pi = 3.14; int В[a]; B[0] = 2; cout
Создавать массив можно только с целым количеством ячеек, по этому передать туда pi не получиться, хоть это и константа. Заполним массив степенями двойки и выведем его на экран. Как видим, элементов у нас 6, как и было записано в константе m .
Но что если мы попробуем при создании массива передать туда не константу, а переменную? У нас ничего не получиться, потому что при запуске нашего кода компилятору - программе, которая превращает наш текст в понятный для компьютера вид, нужно выделить под массив какое-то место, но если там стоит значение, которое может поменяться по ходу выполнения кода, то компилятор не будет знать сколько места ему выделять.
Если же нам все-таки нужен массив, с заранее не известным количеством ячеек, то мы можем воспользоваться динамическим массивом. Для его объявления воспользуемся уже изученным указателем.
int a; cout > a; int *C = new int[a]; for (int i = 0; i < a; i++) < cout > C[i]; > for (int i = 0; i
Мы создаем переменную-указатель, и помещаем в нее новый массив целых чисел (по структуре объявления думаю понятно что происходит). В квадратные скобки мы помещаем переменную a , которую мы вводили с клавиатуры. Ну и теперь можем вручную задать значения для ячеек.
Теперь давай подумаем, что если нам нужен не просто ряд чисел, а, скажем, таблица. Например, если нам нужна таблица 5 на 5, то мы можем взять массив из 25 ячеек и считать что первые пять элементов это первый ряд, вторые 5 это второй и так далее. Ну, или мы можем создать массив из 5 ячеек, а в качестве значений поместить в каждую ячейку еще по одному массиву из 5 элементов. Такая структура называется двумерный массив (таких итераций может быть сколько угодно, но на практике больше чем две не делают). Сейчас разберем как это рабоатет.
int D[5][5]; cout int D[5][5]; for (int i = 0; i < 5; i++) < for (int j = 0; j < 5; j++) < D[i][j] = (i + 1)*(j + 1); >> for (int i = 0; i < 5; i++) < for (int j = 0; j < 5; j++) < cout cout
Тут мы можем заметить кое-что новое, вложенные циклы. То есть мы запустили один цикл, внутри него запустился еще один, и уже внутри второго мы что-то делаем с массивом. Когда внутренний цикл закончиться, внешний выйдет на второй круг и опять запустит второй цикл. Вложенность у циклов и условий может быть любого уровня и сложности.
Но вернемся к массиву. Чтобы получить доступ к ряду, нам нужно в первых скобках указать номер ряда, а для доступа уже к элементу ряда, или же можно назвать это колонкой, нам нужно указать число во вторых скобках.
Что считать рядом, а что столбиком решает сам программист, но зачастую за ряды принимается первый индекс.
Ну и напоследок, заканчивая тему двумерных массивов поговорим про динамические двумерные массивы.
int a; cout > a; int **E = new int *[i]; for (int i = 0; i < a; i++)< E[i] = new int [a]; >for (int i = 0; i < a; i++) < for (int j = 0; j < a; j++) < cout cout
Мы создаем переменную E и указываем на двойную вложенность указателей.
Вообще, на самом деле, эти звездочки должны быть рядом с названием типа данных, вот так: int* , int** и тп, так как указатель это отдельный тип данных (двумерный динамический массив, это указательный тип данных от указательного типа данных, от int ), но писать * можно как рядом с названием типа данных, так и с названием переменной.
Но мы создали только внешний массив, а ведь в его ячейки нужно вложить другие динамические массивы. Сделаем это с помощью цикла.
Если мы на этом этапе установим для каждого вложенного массива свою индивидуальную длину, то такой массив будет называться зубчатым
К элементам динамических массивов так же можно обращаться с помощью ссылок и разыменовывания.
Теперь разберемся с таким понятием как функция. Вообще, все это время наш код запускался из функции которая называется main . С этой функции начинается выполнение любой программы на C++.
Разберем синтаксис функций. Функция создается почти так же как и переменная. В начале тип данных который будет возвращать функция, после идет имя функции, которое, кстати, не может совпадать с именами переменных и других функций, количество аргументов которых одинаково. Ну и рядом с именем круглые скобки с этими самыми аргументами. Тело функции окружено фигурными скобками, что создает новую область видимости.
Страшно и сложно. Посмотрим на пример
int function(int a)
Теперь посмотрим как это использовать
int function(int a) < int b = a * a; return b; >int main()< int a = function(5); cout int a = 0; std::cout
- модуль со строковым типом данных, которого в "чистом" С++ нету.
Небольшой экскурс. Строки, это на самом деле массивы символов, и чтоб не возникало возни с этим, создали модуль, который упрощает работу с ними. Если интересно как это устроенно на программном уровне, погугли "С-строки".
#include - Модуль, который позволяет взаимодействовать с WinAPI, проще говоря позволяет управлять системой виндовс. В нашем случае его можно использовать для этих двух строк.
SetConsoleCP(CP_UTF8); SetConsoleOutputCP(CP_UTF8);
Первая строка задает кодировку для вывода консоли, вторая для ввода. Проще говоря, позволяет печатать на других языках по мимо английского.
Файл с кодом, приведенным в этой статье, ты можеш найти по этой ссылке на гитхаб
Заключение
Это далеко не полное руководство, ни по С++, ни по программированию в целом. Это всего лишь небольшой обзор самых основ программирования, и структуры выполнения кода в компьютере. Очень надеюсь, что мне удалось заинетерсовать тебя в дальнейшем изучении программирования.
Для дальнейшего изучения рекомендую сайт metanit. А так же можешь отдельно погуглить что-то из того что описано в этой статье, для более глубокого понимания темы.
- начинающим
- с++
- как научиться программировать
1.2. Типы Данных И Их Объявление
Важное отличие языка СИ от других языков (PL1, FORTRAN, и др.) является отсутствие принципа умолчания, что приводит к необходимости объявления всех переменных используемых в программе явно вместе с указанием соответствующих им типов.
Объявления переменной имеет следующий формат:
[спецафикатор-класа-памяти] спецификатор-типа описатель [=инициатор] [,описатель [= инициатор] ].
Описатель - идентификатор простой переменной либо более сложная конструкция с квадратными скобками, круглыми скобками или звездочкой (набором звездочек).
Спецификатор типа - одно или несколько ключевых слов, определяющие тип объявляемой переменной. В языке СИ имеется стандартный набор типов данных, используя который можно сконструировать новые (уникальные) типы данных.
Инициатор - задает начальное значение или список начальных значений, которые (которое) присваивается переменной при объявлении.
Спецификатор класса памяти - определяется одним из четырех ключевых слов языка СИ: auto, extern, register, static, и указывает,каким образом будет распределяться память под объявляемую переменную, с одной стороны, а с другой, область видимости этой переменной, т.е., из каких частей программы можно к ней обратиться.
1.2.1 Категории типов данных
Ключевые слова для определения основных типов данных
Целые типы : Плавающие типы: char float int double short long double long signed unsigned
Переменная любого типа может быть объявлена как немодифицируемая. Это достигается добавлением ключевого слова const к спецификатору-типа. Объекты с типом const представляют собой данные используемые только для чтения, т.е. этой переменной не может быть присвоено новое значение. Отметим, что если после слова const отсутствует спецификатор-типа, то подразумевается спецификатор типа int. Если ключевое слово const стоит перед объявлением составных типов (массив, структура, смесь, перечисление), то это приводит к тому, что каждый элемент также должен являться немодифицируемым, т.е. значение ему может быть присвоено только один раз.
const double A=2.128E-2; const B=286; (подразумевается const int B=286)
Примеры объявления составных данных будут рассмотрены ниже.
1.2.2. Целый тип данных
Для определения данных целого типа используются различные ключевые слова, которые определяют диапазон значений и размер области памяти, выделяемой под переменные (табл. 6).
Тип | Размер памяти в байтах | Диапазон значений |
---|---|---|
char | 1 | от -128 до 127 |
int | Для IBM XT,AT,SX,DX 2 | |
short | 2 | от -32768 до 32767 |
long | 4 | от -2 147 483 648 до 2 147 483 647 |
unsigned shar | 1 | oт 0 до 255 |
unsigned int | Для IBM XT,AT,SX,DX 2 | |
unsigned short | 2 | от 0 до 65535 |
unsigned long | 4 | от 0 до 4 294 967 295 |
Отметим, что ключевые слова signed и unsigned необязательны. Они указывают, как интерпретируется нулевой бит объявляемой переменной, т.е., если указано ключевое слово unsigned, то нулевой бит интерпретируется как часть числа, в противном случае нулевой бит интерпретируется как знаковый. В случае отсутствия ключевого слова unsigned целая переменная считается знаковой. В том случае, если спецификатор типа состоит из ключевого типа signed или unsigned и далее следует идентификатор переменной, то она будет рассматриваться как переменная типа int. Например:
unsigned int n; unsigned int b; int c; (подразумевается signed int c ); unsigned d; (подразумевается unsigned int d ); signed f; (подразумевается signed int f ).
Отметим, что модификатор-типа char используется для представления символа (из массива представление символов) или для объявления строковых литералов. Значением объекта типа char является код (размером 1 байт), соответствующий представляемому символу. Для представления символов русского алфавита, модификатор типа идентификатора данных имеет вид unsigned char, так как коды русских букв превышают величину 127.
Следует сделать следующее замечание: в языке СИ не определено представление в памяти и диапазон значений для идентификаторов с модификаторами-типа int и unsigned int. Размер памяти для переменной с модификатором типа signed int определяется длиной машинного слова, которое имеет различный размер на разных машинах. Так, на 16-ти разрядных машинах размер слова равен 2-м байтам, на 32-х разрядных машинах соответственно 4-м байтам, т.е. тип int эквивалентен типам short int, или long int в зависимости от архитектуры используемой ПЭВМ. Таким образом, одна и та же программа может правильно работать на одном компьютере и неправильно на другом. Для определения длины памяти занимаемой переменной можно использовать операцию sizeof языка СИ, возвращающую значение длины указанного модификатора-типа.
a = sizeof(int); b = sizeof(long int); c = sizeof(unsigned long); d = sizeof(short);
Отметим также, что восьмеричные и шестнадцатеричные константы также могут иметь модификатор unsigned. Это достигается указанием префикса u или U после константы, константа без этого префикса считается знаковой.
0xA8C (int signed ); 01786l (long signed ); 0xF7u (int unsigned );
1.2.3. Данные плавающего типа
Для переменных, представляющих число с плавающей точкой используются следующие модификаторы-типа : float, double, long double (в некоторых реализациях языка long double СИ отсутствует).
Величина с модификатором-типа float занимает 4 байта. Из них 1 байт отводится для знака, 8 бит для избыточной экспоненты и 23 бита для мантиссы. Отметим, что старший бит мантиссы всегда равен 1, поэтому он не заполняется, в связи с этим диапазон значений переменной с плавающей точкой приблизительно равен от 3.14E-38 до 3.14E+38.
Величина типа double занимает 8 бит в памяти. Ее формат аналогичен формату float. Биты памяти распределяются следующим образом: 1 бит для знака, 11 бит для экспоненты и 52 бита для мантиссы. С учетом опущенного старшего бита мантиссы диапазон значений равен от 1.7E-308 до 1.7E+308.
float f, a, b; double x,y;
1.2.4. Указатели
Указатель - это адрес памяти, распределяемой для размещения идентификатора (в качестве идентификатора может выступать имя переменной, массива, структуры, строкового литерала). В том случае, если переменная объявлена как указатель, то она содержит адрес памяти, по которому может находится скалярная величина любого типа. При объявлении переменной типа указатель, необходимо определить тип объекта данных, адрес которых будет содержать переменная, и имя указателя с предшествующей звездочкой (или группой звездочек). Формат объявления указателя:
спецификатор-типа [ модификатор ] * описатель .
Спецификатор-типа задает тип объекта и может быть любого основного типа, типа структуры, смеси (об этом будет сказано ниже). Задавая вместо спецификатора-типа ключевое слово void, можно своеобразным образом отсрочить спецификацию типа, на который ссылается указатель. Переменная, объявляемая как указатель на тип void, может быть использована для ссылки на объект любого типа. Однако для того, чтобы можно было выполнить арифметические и логические операции над указателями или над объектами, на которые они указывают, необходимо при выполнении каждой операции явно определить тип объектов. Такие определения типов может быть выполнено с помощью операции приведения типов.
В качестве модификаторов при объявлении указателя могут выступать ключевые слова const, near, far, huge. Ключевое слово const указывает, что указатель не может быть изменен в программе. Размер переменной объявленной как указатель, зависит от архитектуры компьютера и от используемой модели памяти, для которой будет компилироваться программа. Указатели на различные типы данных не обязательно должны иметь одинаковую длину.
Для модификации размера указателя можно использовать ключевые слова near, far, huge.
unsigned int * a; /* переменная а представляет собой указатель на тип unsigned int (целые числа без знака) */ double * x; /* переменная х указывает на тип данных с плавающей точкой удвоенной точности */ char * fuffer ; /* объявляется указатель с именем fuffer который указывает на переменную типа char */ double nomer; void *addres; addres = & nomer; (double *)addres ++; /* Переменная addres объявлена как указатель на объект любого типа. Поэтому ей можно присвоить адрес любого объекта (& - операция вычисления адреса). Однако, как было отмечено выше, ни одна арифмитическая операция не может быть выполнена над указателем, пока не будет явно определен тип данных, на которые он указывает. Это можно сделать, используя операцию приведения типа (double *) для преобразования addres к указателю на тип double, а затем увеличение адреса. */ const * dr; /* Переменная dr объявлена как указатель на константное выражение, т.е. значение указателя может изменяться в процессе выполнения программы, а величина, на которую он указывает, нет. */ unsigned char * const w = &obj. /* Переменная w объявлена как константный указатель на данные типа char unsigned. Это означает, что на протяжение всей программы w будет указывать на одну и ту же область памяти. Содержание же этой области может быть изменено. */
1.2.5. Переменные перечислимого типа
Переменная, которая может принимать значение из некоторого списка значений, называется переменной перечислимого типа или перечислением.
Объявление перечисления начинается с ключевого слова enum и имеет два формата представления.
Формат 1. enum [имя-тега-перечисления] описатель[,описатель. ];
Формат 2. enum имя-тега-перечисления описатель [,описатель..];
Объявление перечисления задает тип переменной перечисления и определяет список именованных констант, называемый списком-перечисления. Значением каждого имени списка является некоторое целое число.
Переменная типа перечисления может принимать значения одной из именованных констант списка. Именованные константы списка имеют тип int. Таким образом, память соответствующая переменной перечисления, это память необходимая для размещения значения типа int.
Переменная типа enum могут использоваться в индексных выражениях и как операнды в арифметических операциях и в операциях отношения.
В первом формате 1 имена и значения перечисления задаются в списке перечислений. Необязательное имя-тега-перечисления, это идентификатор, который именует тег перечисления, определенный списком перечисления. Описатель именует переменную перечисления. В объявлении может быть задана более чем одна переменная типа перечисления.
Список-перечисления содержит одну или несколько конструкций вида:
идентификатор [= константное выражение]
Каждый идентификатор именует элемент перечисления. Все идентификаторы в списке enum должны быть уникальными. В случае отсутствия константного выражения первому идентификатору соответствует значение 0, следующему идентификатору - значение 1 и т.д. Имя константы перечисления эквивалентно ее значению.
Идентификатор, связанный с константным выражением, принимает значение, задаваемое этим константным выражением. Константное выражение должно иметь тип int и может быть как положительным, так и отрицательным. Следующему идентификатору в списке присваивается значение, равное константному выражению плюс 1, если этот идентификатор не имеет своего константного выражения. Использование элементов перечисления должно подчиняться следующим правилам:
1. Переменная может содержать повторяющиеся значения.
2. Идентификаторы в списке перечисления должны быть отличны от всех других идентификаторов в той же области видимости, включая имена обычных переменных и идентификаторы из других списков перечислений.
3. Имена типов перечислений должны быть отличны от других имен типов перечислений, структур и смесей в этой же области видимости.
4. Значение может следовать за последним элементом списка перечисления.
enum week < SUB = 0, /* 0 */ VOS = 0, /* 0 */ POND, /* 1 */ VTOR, /* 2 */ SRED, /* 3 */ HETV, /* 4 */ PJAT /* 5 */ >rab_ned ;
В данном примере объявлен перечислимый тег week, с соответствующим множеством значений, и объявлена переменная rab_ned имеющая тип week.
Во втором формате используется имя тега перечисления для ссылки на тип перечисления, определяемый где-то в другом месте. Имя тега перечисления должно относится к уже определенному тегу перечисления в пределах текущей области видимости. Так как тег перечисления объявлен где-то в другом месте, список перечисления не представлен в объявлении.
В объявлении указателя на тип данных перечисления и объявляемых typedef для типов перечисления можно использовать имя тега перечисления до того, как данный тег перечисления определен. Однако определение перечисления должно предшествовать любому действию используемого указателя на тип объявления typedef. Объявление без последующего списка описателей описывает тег, или, если так можно сказать, шаблон перечисления.
1.2.6. Массивы
Массивы - это группа элементов одинакового типа (double, float, int и т.п.). Из объявления массива компилятор должен получить информацию о типе элементов массива и их количестве. Объявление массива имеет два формата:
спецификатор-типа описатель [константное - выражение];
Описатель - это идентификатор массива .
Спецификатор-типа задает тип элементов объявляемого массива. Элементами массива не могут быть функции и элементы типа void.
Константное-выражение в квадратных скобках задает количество элементов массива. Константное-выражение при объявлении массива может быть опущено в следующих случаях:
- при объявлении массив инициализируется,
- массив объявлен как формальный параметр функции,
- массив объявлен как ссылка на массив, явно определенный в другом файле.
В языке СИ определены только одномерные массивы, но поскольку элементом массива может быть массив, можно определить и многомерные массивы. Они формализуются списком константных-выражений следующих за идентификатором массива, причем каждое константное-выражение заключается в свои квадратные скобки.
Каждое константное-выражение в квадратных скобках определяет число элементов по данному измерению массива, так что объявление двухмерного массива содержит два константных-выражения, трехмерного - три и т.д. Отметим, что в языке СИ первый элемент массива имеет индекс равный 0.
int a[2][3]; /* представлено в виде матрицы a[0][0] a[0][1] a[0][2] a[1][0] a[1][1] a[1][2] */ double b[10]; /* вектор из 10 элементов имеющих тип double */ int w[3][3] = < < 2, 3, 4 >, < 3, 4, 8 >, < 1, 0, 9 >>;
В последнем примере объявлен массив w[3][3]. Списки, выделенные в фигурные скобки, соответствуют строкам массива, в случае отсутствия скобок инициализация будет выполнена неправильно.
В языке СИ можно использовать сечения массива, как и в других языках высокого уровня (PL1 и т.п.), однако на использование сечений накладывается ряд ограничений. Сечения формируются вследствие опускания одной или нескольких пар квадратных скобок. Пары квадратных скобок можно отбрасывать только справа налево и строго последовательно. Сечения массивов используются при организации вычислительного процесса в функциях языка СИ, разрабатываемых пользователем.
Если при обращении к некоторой функции написать s[0], то будет передаваться нулевая строка массива s.
При обращении к массиву b можно написать, например, b[1][2] и будет передаваться вектор из четырех элементов, а обращение b[1] даст двухмерный массив размером 3 на 4. Нельзя написать b[2][4], подразумевая, что передаваться будет вектор, потому что это не соответствует ограничению наложенному на использование сечений массива.
Пример объявления символьного массива.
char str[] = "объявление символьного массива";
Следует учитывать, что в символьном литерале находится на один элемент больше, так как последний из элементов является управляющей последовательностью '\0'.
1.2.7. Структуры
Cтруктуры - это составной объект, в который входят элементы любых типов, за исключением функций. В отличие от массива, который является однородным объектом, структура может быть неоднородной. Тип структуры определяется записью вида:
В структуре обязательно должен быть указан хотя бы один компонент. Определение структур имеет следующий вид:
где тип-данных указывает тип структуры для объектов, определяемых в описателях. В простейшей форме описатели представляют собой идентификаторы или массивы.
struct < double x,y; >s1, s2, sm[9]; struct < int year; char moth, day; >date1, date2;
Переменные s1, s2 определяются как структуры, каждая из которых состоит из двух компонент х и у. Переменная sm определяется как массив из девяти структур. Каждая из двух переменных date1, date2 состоит из трех компонентов year, moth, day. >p>Существует и другой способ ассоциирования имени с типом структуры, он основан на использовании тега структуры. Тег структуры аналогичен тегу перечислимого типа. Тег структуры определяется следующим образом:
где тег является идентификатором.
В приведенном ниже примере идентификатор student описывается как тег структуры:
struct student < char name[25]; int id, age; char prp; >;
Тег структуры используется для последующего объявления структур данного вида в форме:
struct тег список-идентификаторов;
struct studeut st1,st2;
Использование тегов структуры необходимо для описания рекурсивных структур. Ниже рассматривается использование рекурсивных тегов структуры.
struct node < int data; struct node * next; >st1_node;
Тег структуры node действительно является рекурсивным, так как он используется в своем собственном описании, т.е. в формализации указателя next. Структуры не могут быть прямо рекурсивными, т.е. структура node не может содержать компоненту, являющуюся структурой node, но любая структура может иметь компоненту, являющуюся указателем на свой тип, как и сделано в приведенном примере.
Доступ к компонентам структуры осуществляется с помощью указания имени структуры и следующего через точку имени выделенного компонента, например:
st1.name="Иванов"; st2.id=st1.id; st1_node.data=st1.age;
1.2.8. Объединения (смеси)
Объединение подобно структуре, однако в каждый момент времени может использоваться (или другими словами быть ответным) только один из элементов объединения. Тип объединения может задаваться в следующем виде:
union < описание элемента 1; . описание элемента n; >;
Главной особенностью объединения является то, что для каждого из объявленных элементов выделяется одна и та же область памяти, т.е. они перекрываются. Хотя доступ к этой области памяти возможен с использованием любого из элементов, элемент для этой цели должен выбираться так, чтобы полученный результат не был бессмысленным.
Доступ к элементам объединения осуществляется тем же способом, что и к структурам. Тег объединения может быть формализован точно так же, как и тег структуры.
Объединение применяется для следующих целей:
- инициализации используемого объекта памяти, если в каждый момент времени только один объект из многих является активным;
- интерпретации основного представления объекта одного типа, как если бы этому объекту был присвоен другой тип.
Память, которая соответствует переменной типа объединения, определяется величиной, необходимой для размещения наиболее длинного элемента объединения. Когда используется элемент меньшей длины, то переменная типа объединения может содержать неиспользуемую память. Все элементы объединения хранятся в одной и той же области памяти, начиная с одного адреса.
union < char fio[30]; char adres[80]; int vozrast; int telefon; >inform; union < int ax; char al[2]; >ua;
При использовании объекта infor типа union можно обрабатывать только тот элемент который получил значение, т.е. после присвоения значения элементу inform.fio, не имеет смысла обращаться к другим элементам. Объединение ua позволяет получить отдельный доступ к младшему ua.al[0] и к старшему ua.al[1] байтам двухбайтного числа ua.ax .
1.2.9. Поля битов
Элементом структуры может быть битовое поле, обеспечивающее доступ к отдельным битам памяти. Вне структур битовые поля объявлять нельзя. Нельзя также организовывать массивы битовых полей и нельзя применять к полям операцию определения адреса. В общем случае тип структуры с битовым полем задается в следующем виде:
struct
длинна - поля задается целым выражением или константой. Эта константа определяет число битов, отведенное соответствующему полю. Поле нулевой длинны обозначает выравнивание на границу следующего слова.
struct < unsigned a1 : 1; unsigned a2 : 2; unsigned a3 : 5; unsigned a4 : 2; >prim;
Структуры битовых полей могут содержать и знаковые компоненты. Такие компоненты автоматически размещаются на соответствующих границах слов, при этом некоторые биты слов могут оставаться неиспользованными.
Ссылки на поле битов выполняются точно так же, как и компоненты общих структур. Само же битовое поле рассматривается как целое число, максимальное значение которого определяется длиной поля.
1.2.10. Переменные с изменяемой структурой
Очень часто некоторые объекты программы относятся к одному и тому же классу, отличаясь лишь некоторыми деталями. Рассмотрим, например, представление геометрических фигур. Общая информация о фигурах может включать такие элементы, как площадь, периметр. Однако соответствующая информация о геометрических размерах может оказаться различной в зависимости от их формы.
Рассмотрим пример, в котором информация о геометрических фигурах представляется на основе комбинированного использования структуры и объединения.
struct figure < double area,perimetr; /* общие компоненты */ int type; /* признак компонента */ union /* перечисление компонент */ < double radius; /* окружность */ double a[2]; /* прямоугольник */ double b[3]; /* треугольник */ >geom_fig; > fig1, fig2 ;
В общем случае каждый объект типа figure будет состоять из трех компонентов: area, perimetr, type. Компонент type называется меткой активного компонента, так как он используется для указания, какой из компонентов объединения geom_fig является активным в данный момент. Такая структура называется переменной структурой, потому что ее компоненты меняются в зависимости от значения метки активного компонента (значение type).
Отметим, что вместо компоненты type типа int, целесообразно было бы использовать перечисляемый тип. Например, такой
enum figure_chess < CIRCLE, BOX, TRIANGLE >;
Константы CIRCLE, BOX, TRIANGLE получат значения соответственно равные 0, 1, 2. Переменная type может быть объявлена как имеющая перечислимый тип :
enum figure_chess type;
В этом случае компилятор СИ предупредит программиста о потенциально ошибочных присвоениях, таких, например, как
В общем случае переменная структуры будет состоять из трех частей: набор общих компонент, метки активного компонента и части с меняющимися компонентами. Общая форма переменной структуры, имеет следующий вид:
struct < общие компоненты; метка активного компонента; union < описание компоненты 1 ; описание компоненты 2 ; . описание компоненты n ; >идентификатор-объединения ; > идентификатор-структуры ;
Пример определения переменной структуры с именем helth_record
struct < /* общая информация */ char name [25]; /* имя */ int age; /* возраст */ char sex; /* пол */ /* метка активного компонента */ /* (семейное положение) */ enum merital_status ins; /* переменная часть */ union < /* холост */ /* нет компонент */ struct < /* состоит в браке */ char marripge_date[8]; char spouse_name[25]; int no_children; >marriage_info; /* разведен */ char date_divorced[8]; > marital_info; > health_record; enum marital_status < SINGLE, /* холост */ MARRIGO, /* женат */ DIVOREED /* разведен */ >;
Обращаться к компонентам структуры можно при помощи ссылок:
helth_record.neme, helth_record.ins, helth_record.marriage_info.marriage_date .
1.2.11. Определение объектов и типов
Как уже говорилось выше, все переменные используемые в программах на языке СИ, должны быть объявлены. Тип объявляемой переменной зависит от того, какое ключевое слово используется в качестве спецификатора типа и является ли описатель простым идентификатором или же комбинацией идентификатора с модификатором указателя (звездочка), массива (квадратные скобки) или функции (круглые скобки).
При объявлении простой переменной, структуры, смеси или объединения, а также перечисления, описатель - это простой идентификатор. Для объявления указателя, массива или функции идентификатор модифицируется соответствующим образом: звездочкой слева, квадратными или круглыми скобками справа.
Отметим важную особенность языка СИ, при объявлении можно использовать одновременно более одного модификатора, что дает возможность создавать множество различных сложных описателей типов.
Однако надо помнить, что некоторые комбинации модификаторов недопустимы:
- элементами массивов не могут быть функции,
- функции не могут возвращать массивы или функции.
При инициализации сложных описателей квадратные и круглые скобки (справа от идентификатора) имеют приоритет перед звездочкой (слева от идентификатора). Квадратные или круглые скобки имеют один и тот же приоритет и раскрываются слева направо. Спецификатор типа рассматривается на последнем шаге, когда описатель уже полностью проинтерпретирован. Можно использовать круглые скобки, чтобы поменять порядок интерпретации на необходимый.
Для интерпретации сложных описаний предлагается простое правило, которое звучит как "изнутри наружу", и состоит из четырех шагов.
1. Начать с идентификатора и посмотреть вправо, есть ли квадратные или круглые скобки.
2. Если они есть, то проинтерпретировать эту часть описателя и затем посмотреть налево в поиске звездочки.
3. Если на любой стадии справа встретится закрывающая круглая скобка, то вначале необходимо применить все эти правила внутри круглых скобок, а затем продолжить интерпретацию.
4. Интерпретировать спецификатор типа.
int * ( * comp [10]) (); 6 5 3 1 2 4
В данном примере объявляется переменная comp (1), как массив из десяти (2) указателей (3) на функции (4), возвращающие указатели (5) на целые значения (6).
char * ( * ( * var ) () ) [10]; 7 6 4 2 1 3 5
Переменная var (1) объявлена как указатель (2) на функцию (3) возвращающую указатель (4) на массив (5) из 10 элементов, которые являются указателями (6) на значения типа char.
Кроме объявлений переменных различных типов, имеется возможность объявить типы. Это можно сделать двумя способами. Первый способ - указать имя тега при объявлении структуры, объединения или перечисления, а затем использовать это имя в объявлении переменных и функций в качестве ссылки на этот тег. Второй - использовать для объявления типа ключевое слово typedef.
При объявлении с ключевым словом typedef, идентификатор стоящий на месте описываемого объекта, является именем вводимого в рассмотрение типа данных, и далее этот тип может быть использован для объявления переменных.
Отметим, что любой тип может быть объявлен с использованием ключевого слова typedef, включая типы указателя, функции или массива. Имя с ключевым словом typedef для типов указателя, структуры, объединения может быть объявлено прежде чем эти типы будут определенны, но в пределах видимости объявителя.
typedef double (* MATH)( ); /* MATH - новое имя типа, представляющее указатель на функцию, возвращающую значения типа double */ MATH cos; /* cos указатель на функцию, возвращающую значения типа double */ /* Можно провести эквивалентное объявление */ double (* cos)( ); typedef char FIO[40] /* FIO - массив из сорока символов */ FIO person; /* Переменная person - массив из сорока символов */ /* Это эквивалентно объявлению */ char person[40];
При объявлении переменных и типов здесь были использованы имена типов (MATH FIO). Помимо этого, имена типов могут еще использоваться в трех случаях: в списке формальных параметров, в объявлении функций, в операциях приведения типов и в операции sizeof (операция приведения типа).
Именами типов для основных типов, типов перечисления, структуры и смеси являются спецификаторы типов для этих типов. Имена типов для типов указателя массива и функции задаются при помощи абстрактных описателей следующим образом:
Абстрактный-описатель - это описатель без идентификатора, состоящий из одного или более модификаторов указателя, массива или функции. Модификатор указателя (*) всегда задается перед идентификатором в описателе, а модификаторы массива [] и функции () - после него. Таким образом, чтобы правильно интерпретировать абстрактный описатель, нужно начать интерпретацию с подразумеваемого идентификатора.
Абстрактные описатели могут быть сложными. Скобки в сложных абстрактных описателе задают порядок интерпретации подобно тому, как это делалось при интерпретации сложных описателей в объявлениях.
1.2.12. Инициализация данных
При объявлении переменной ей можно присвоить начальное значение, присоединяя инициатор к описателю. Инициатор начинается со знака " привет";
Инициализируется массив символов из 7 элементов, последним элементом (седьмым) будет символ '\0', которым завершаются все строковые литералы.
В том случае, если задается размер массива, а строковый литерал длиннее, чем размер массива, то лишние символы отбрасываются.
Следующее объявление инициализирует переменную stroka как массив, состоящий из семи элементов.
char stroka[5] = "привет";
В переменную stroka попадают первые пять элементов литерала, а символы 'Т' и '\0' отбрасываются.
Если строка короче, чем размер массива, то оставшиеся элементы массива заполняются нулями.
Отметим, что инициализация переменной типа tab может иметь следующий вид:
union tab pers1 = "Антон";
и, таким образом, в символьный массив попадут символы:
а остальные элементы будут инициализированы нулем.