Бритуля - Богиня
Здрасте))))
решение домашнего задания с предыдущей лекцииУсловие:
Напишите программу, которая получает от пользователя 2 параметра:
1. стартовое число
2. количество чисел
Программа сохранит ряд чисел, начиная со стартового, а если количество чисел равно нулю, то программа напишет "Нечего делать" и выйдет. Предположите, что пользователь ввел правильное число, то есть не нужно проверять, не выходит ли число за рамки, установленные размером типа int
Пример:
1. Пользователь ввел:
стартовое число == 12
количество чисел == 3
Программа сохранит такие числа: 12, 13, 14
2. Пользователь ввел:
стартовое число == -2
количество чисел == 5
Программа сохранит такие числа: -2, -1, 0, 1, 2
В конце можно распечатать сохраненные числа, чтобы убедиться, что все сделано правильно.
Удачи!
решениеКод:





Результат:

лекцияНа этой лекции мы поговорим о 2-х вещах. В начале я расскажу про преобразование типов, а после - основная тема лекции - неопределенный указатель. Не будем много разглагольствовать, а сразу перейдем к делу.
Для начала, давайте вспомним такой факт: на прошлом домашнем задании я выполнил такое, пока еще не совсем понятное действие: я отнял char от char, а результат положил в int. Я сказал, что это можно делать потому, что int хранится в большем количестве байт, чем char, значит нет потери данных, и поэтому язык позволяет это сделать.
Что же произошло, когда я отнял char от char? Создался новый char и программа сохранила его в переменной из типа int, заполнив "лишнее" место нулями. То есть произошло преобразование одного типа в другой.
Преобразование (англ. "casting") это возможность "переделать" один тип в другой. Так как все, в конечном итоге, кусок памяти, заполненный единицами и нулями, а все зависит лишь от нашего взгляда на этот кусок, то такое теоретически возможно
Хочу еще немного объяснить это. Если мы заглянем в таблицу ASCII, то мы увидим, что буква "а" это 97. То есть, в определенном куске памяти, в бинарном виде написано "97". Стоит вопрос: как смотреть на это число? Как на число или как на букву "а"? Программист, выполняя преобразование "говорит" программе каков правильный взгляд на этот, конкретный кусок памяти.
НО! во время операции в домашнем задании, когда я отнял char от char, программа сделала преобразование сама, вместо меня, поняв мои намерения потому, что я положил результат в int. Такой тип преобразования называется "неявный".
Ну, раз существует "неявный тип", то должен быть и "явный", не правда ли? И он существует! На самом деле, в С++ есть несколько способов выполнять преобразование, но я всегда пользуюсь только "простым" и на это есть причины: все остальные способы более "защищенные", то есть, программа не выполняет тупо то, что говорит ей программист, а проверяет "можно так делать или нельзя".
Почему же я не пользуюсь этим? Ответ прост: я не выполняю преобразование каждые две строки кода. Я стараюсь делать это редко и тогда, когда это ОЧЕНЬ оправдано. То есть - без этого нельзя, НО! Если уж я делаю преобразование, то я на миллион процентов уверен в том, что я делаю, поэтому дополнительные проверки со стороны программы мне не нужны. То есть, выполняя такой тип преобразования, программер как бы говорит: "Сядь и не переживай! Это - было float, а теперь int. Потому что я так хочу! Не спорь, а выполняй, тупая железяка!"
Допустим, у нас есть такой код:
int num = 97; // ascii код буквы "а"
char ch = 0;
ch = (char) num; // я "вкладываю" в int в char, выполнив "явное" преобразование
cout << ch; // на экран распечатается буква "а", а не 97.
Точно так же, можно заменить 3-ю строку таким кодом:
ch = char(num); // это считается более С++ формой преобразования
Но, в итоге - оба варианта делают одно и тоже. Лично я предпочитаю первый, просто по-привычке.
Примечание: преобразование типов - потенциально опасное место в коде, поэтому, повторюсь еще раз: оно производится только при одном условии: программист УВЕРЕН в своих действиях!
Для интереса запустите такой код:
char ch = 'a';
cout << ch << endl;
cout << (int) ch << endl;
cout << (float) ch << endl;
программа распечатает один раз "а", второй раз "97", в третий раз "97.0".
И напоследок, еще одно напоминание. Как мы помним, одно из первых домашних заданий выглядело так:
"Напишите программу, которая получает от пользователя 2 положительных числа и высчитывает среднее арифметическое между ними." (Лекция 5. Типы примитивных переменных)
Что я сделал? Я объявил переменные как float, сказав, что сделал это потому, что результат может быть дробным. Но я так же сказал, что не обязательно объявлять так:
float first_num = 0;
float second_num = 0;
float average = 0;
Кроме переменной average, можно объявить еще только одну переменную, как float, допустим переменную first_num, то есть:
float first_num = 0;
int second_num = 0;
float average = 0;
тогда код: average = (first_num + second_num) / 2; ТОЖЕ отработал бы правильно! Почему? Потому что программа, совершая действие над разными типами int и float, совершает неявное преобразование в пользу бОльшего типа, то есть в этой ситуации в float.
Однако, можно было бы поступить и так:
int first_num = 0;
int second_num = 0;
float average = 0;
То есть, изначально объявить все переменные как int (кроме average) и тогда, строка арифметического действия, ниже, выглядела бы так:
average = (float) ((first_num + second_num) / 2); ИЛИ так: average = float ((first_num + second_num) / 2);
То есть, выполнив явное преобразование.
Вы вполне можете проверить мою правоту, запустив код с теми изменениями, которые я предложил.
Для начала, теория, чтобы понять о чем мы говорим. Думаю, что лучше всего я продемонстрирую это так:
int num = 0;
int * p_num = new int;
char ch = 'c';
char * str = "hello";
float * p_f = new float;
void * universal_pointer = NULL;
universal_pointer = & num;
universal_pointer = p_num;
universal_pointer = & ch;
universal_pointer = str;
universal_pointer = p_f;
Насколько видно из кода, можно объявить указатель на void и он обладает интересным качеством: ему можно присвоить адрес любой переменной! Однако, такой тип указателя обладает и минусом, а именно: им можно пользоваться только для того, чтобы хранить что-то, то есть код:
int a = 5;
int b = 7;
int c = 0;
void * p = &a;
c = (*p) + b; // так писать нельзя!
но можно так:
int * p_num = (int *) p; // преобразование в указатель на int
c = (*p_num) + b; // а вот так - уже можно.
То есть, как мы видим, что указатель на void используется только в качестве хранилища. Для того, чтобы совершить с данными какое-либо действие, нужно преобразовать указатель на void в указатель конкретного типа.
У этого трюка есть очень обширное применение, но привести сейчас пример такого применения я не могу, потому что мне придется задеть кое-какие области знаний, которых у нас пока нет. Я расскажу лишь в теории.
Такой способ применяется для создания "контейнеров", которые хранят данные. Мы можем с помощью такого указателя создать контейнер, в который можно положить абсолютно любой тип переменной и передавать или хранить его, пока он не понадобится. А в функции, в которой нужно использовать эти данные - уже написать правильное преобразование и нужную обработку.
Все это немного "висит в воздухе", но дайте время))))
Резюме: на этой лекции мы выучили способ преобразовывать один тип в другой, а так же познакомились (совсем чуть-чуть) с указателем на неопределенный тип. Так как оба знания - пока чистая теория, то домашнего задания на этот раз не будет.
решение домашнего задания с предыдущей лекцииУсловие:
Напишите программу, которая получает от пользователя 2 параметра:
1. стартовое число
2. количество чисел
Программа сохранит ряд чисел, начиная со стартового, а если количество чисел равно нулю, то программа напишет "Нечего делать" и выйдет. Предположите, что пользователь ввел правильное число, то есть не нужно проверять, не выходит ли число за рамки, установленные размером типа int
Пример:
1. Пользователь ввел:
стартовое число == 12
количество чисел == 3
Программа сохранит такие числа: 12, 13, 14
2. Пользователь ввел:
стартовое число == -2
количество чисел == 5
Программа сохранит такие числа: -2, -1, 0, 1, 2
В конце можно распечатать сохраненные числа, чтобы убедиться, что все сделано правильно.
Удачи!
решениеКод:





Результат:

лекцияНа этой лекции мы поговорим о 2-х вещах. В начале я расскажу про преобразование типов, а после - основная тема лекции - неопределенный указатель. Не будем много разглагольствовать, а сразу перейдем к делу.
Преобразование типов
Для начала, давайте вспомним такой факт: на прошлом домашнем задании я выполнил такое, пока еще не совсем понятное действие: я отнял char от char, а результат положил в int. Я сказал, что это можно делать потому, что int хранится в большем количестве байт, чем char, значит нет потери данных, и поэтому язык позволяет это сделать.
Что же произошло, когда я отнял char от char? Создался новый char и программа сохранила его в переменной из типа int, заполнив "лишнее" место нулями. То есть произошло преобразование одного типа в другой.
Преобразование (англ. "casting") это возможность "переделать" один тип в другой. Так как все, в конечном итоге, кусок памяти, заполненный единицами и нулями, а все зависит лишь от нашего взгляда на этот кусок, то такое теоретически возможно
Хочу еще немного объяснить это. Если мы заглянем в таблицу ASCII, то мы увидим, что буква "а" это 97. То есть, в определенном куске памяти, в бинарном виде написано "97". Стоит вопрос: как смотреть на это число? Как на число или как на букву "а"? Программист, выполняя преобразование "говорит" программе каков правильный взгляд на этот, конкретный кусок памяти.
НО! во время операции в домашнем задании, когда я отнял char от char, программа сделала преобразование сама, вместо меня, поняв мои намерения потому, что я положил результат в int. Такой тип преобразования называется "неявный".
Ну, раз существует "неявный тип", то должен быть и "явный", не правда ли? И он существует! На самом деле, в С++ есть несколько способов выполнять преобразование, но я всегда пользуюсь только "простым" и на это есть причины: все остальные способы более "защищенные", то есть, программа не выполняет тупо то, что говорит ей программист, а проверяет "можно так делать или нельзя".
Почему же я не пользуюсь этим? Ответ прост: я не выполняю преобразование каждые две строки кода. Я стараюсь делать это редко и тогда, когда это ОЧЕНЬ оправдано. То есть - без этого нельзя, НО! Если уж я делаю преобразование, то я на миллион процентов уверен в том, что я делаю, поэтому дополнительные проверки со стороны программы мне не нужны. То есть, выполняя такой тип преобразования, программер как бы говорит: "Сядь и не переживай! Это - было float, а теперь int. Потому что я так хочу! Не спорь, а выполняй, тупая железяка!"
Допустим, у нас есть такой код:
int num = 97; // ascii код буквы "а"
char ch = 0;
ch = (char) num; // я "вкладываю" в int в char, выполнив "явное" преобразование
cout << ch; // на экран распечатается буква "а", а не 97.
Точно так же, можно заменить 3-ю строку таким кодом:
ch = char(num); // это считается более С++ формой преобразования
Но, в итоге - оба варианта делают одно и тоже. Лично я предпочитаю первый, просто по-привычке.
Примечание: преобразование типов - потенциально опасное место в коде, поэтому, повторюсь еще раз: оно производится только при одном условии: программист УВЕРЕН в своих действиях!
Для интереса запустите такой код:
char ch = 'a';
cout << ch << endl;
cout << (int) ch << endl;
cout << (float) ch << endl;
программа распечатает один раз "а", второй раз "97", в третий раз "97.0".
И напоследок, еще одно напоминание. Как мы помним, одно из первых домашних заданий выглядело так:
"Напишите программу, которая получает от пользователя 2 положительных числа и высчитывает среднее арифметическое между ними." (Лекция 5. Типы примитивных переменных)
Что я сделал? Я объявил переменные как float, сказав, что сделал это потому, что результат может быть дробным. Но я так же сказал, что не обязательно объявлять так:
float first_num = 0;
float second_num = 0;
float average = 0;
Кроме переменной average, можно объявить еще только одну переменную, как float, допустим переменную first_num, то есть:
float first_num = 0;
int second_num = 0;
float average = 0;
тогда код: average = (first_num + second_num) / 2; ТОЖЕ отработал бы правильно! Почему? Потому что программа, совершая действие над разными типами int и float, совершает неявное преобразование в пользу бОльшего типа, то есть в этой ситуации в float.
Однако, можно было бы поступить и так:
int first_num = 0;
int second_num = 0;
float average = 0;
То есть, изначально объявить все переменные как int (кроме average) и тогда, строка арифметического действия, ниже, выглядела бы так:
average = (float) ((first_num + second_num) / 2); ИЛИ так: average = float ((first_num + second_num) / 2);
То есть, выполнив явное преобразование.
Вы вполне можете проверить мою правоту, запустив код с теми изменениями, которые я предложил.
Неопределенный указатель или void *
Для начала, теория, чтобы понять о чем мы говорим. Думаю, что лучше всего я продемонстрирую это так:
int num = 0;
int * p_num = new int;
char ch = 'c';
char * str = "hello";
float * p_f = new float;
void * universal_pointer = NULL;
universal_pointer = & num;
universal_pointer = p_num;
universal_pointer = & ch;
universal_pointer = str;
universal_pointer = p_f;
Насколько видно из кода, можно объявить указатель на void и он обладает интересным качеством: ему можно присвоить адрес любой переменной! Однако, такой тип указателя обладает и минусом, а именно: им можно пользоваться только для того, чтобы хранить что-то, то есть код:
int a = 5;
int b = 7;
int c = 0;
void * p = &a;
c = (*p) + b; // так писать нельзя!
но можно так:
int * p_num = (int *) p; // преобразование в указатель на int
c = (*p_num) + b; // а вот так - уже можно.
То есть, как мы видим, что указатель на void используется только в качестве хранилища. Для того, чтобы совершить с данными какое-либо действие, нужно преобразовать указатель на void в указатель конкретного типа.
У этого трюка есть очень обширное применение, но привести сейчас пример такого применения я не могу, потому что мне придется задеть кое-какие области знаний, которых у нас пока нет. Я расскажу лишь в теории.
Такой способ применяется для создания "контейнеров", которые хранят данные. Мы можем с помощью такого указателя создать контейнер, в который можно положить абсолютно любой тип переменной и передавать или хранить его, пока он не понадобится. А в функции, в которой нужно использовать эти данные - уже написать правильное преобразование и нужную обработку.
Все это немного "висит в воздухе", но дайте время))))
Резюме: на этой лекции мы выучили способ преобразовывать один тип в другой, а так же познакомились (совсем чуть-чуть) с указателем на неопределенный тип. Так как оба знания - пока чистая теория, то домашнего задания на этот раз не будет.
@темы: C++