Последние новости

YoungCoder теперь и на Stepikе. Записывайтесь: https://vk.cc/75rISy

Чтобы записаться на курс, необходимо зарегистрироваться на Степике: https://vk.cc/75rIC4

Это моя личная ссылка-приглашение на Stepik для вас. Регистрируясь по этой ссылке, записываясь на курсы и решая задачи, Вы помогаете автору данного сайта принять участие в конкурсе платформы Stepik! Подробности конкурса здесь: https://vk.cc/75rKuS

воскресенье, 18 августа 2013 г.

Занятие 16. Указатели.


Здравствуйте друзья.
Сегодня у нас важная и сложная тема. Возможно, самая сложная тема за все время. Указатели.

Прочитайте более простую версию этого урока "Указатели".

В новой версии:

  • Ещё более доступное объяснение
  • Дополнительные материалы
  • 10 задач на программирование с автоматической проверкой решения


Прежде вспомним основы шестнадцатеричной системы счисления.

Любое число в 16-тиричной системе счисления записывается с помощью символов 0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F. Шестнадцатеричной она называется, потому что в ней для записи различных чисел используются 16 основных цифр. Например, в привычной нами десятичной системе счисления используется десять основных цифр 0,1,2,3,4,5,6,7,8 и 9. 

Первые десять цифр в 16-тиричной системе счисления такие же, как и в десятичной системе, а вот для записи следующих шести используются буквы латинского алфавита. 
A=10
B=11
C=12
D=13
E=14
F=15.

Как перевести число из шестнадцатеричной системы счисления в десятичную.

Пусть у нас есть число 2D5. И мы хотим узнать, сколько это в нашей десятичной системе счисления.
Для этого сначала разделим число на цифры:
2 D 5
Теперь заменим все буквы, на их числовые обозначения:
2 13 5

Пронумеруем эти цифры справа налево начиная с нуля.

2   13   5
2         1      0

Умножим каждую из цифр, на 16 в степени, соответствующей порядковому номеру. И сложим все это между собой.
Рис.1 Перевод шестнадцатеричного числа в десятичную систему счисления
В результате получим 725. Это и есть число 2D5 в десятичной системе счисления.

Перевод числа из десятичной в шестнадцатеричную систему счисления. 

Давайте переведем число 725 обратно в шестнадцатеричную систему счисления. В результате у нас должно получится 2D5.
Рис.2 Перевод из 10-ой в 16-ричную

Для перевода, нам необходимо поделить с остатком число 725 на 16 в столбик. Получим 45 и остаток 5. 

Теперь делим 45 на 16 с остатком. Получим 2 и остаток 13. Оба числа меньше 16 значит на этом можно закончить деление.

Теперь записываем эти числа в обратном порядке и заменяем буквами соответствующие. 

Как вы уже успели заметить, мы получили то, что и ожидали 2D5.






А теперь посмотрите на следующее число 235. Оно может быть как числом в десятичной, так и в шестнадцатеричной системе счисления. А кстати, чему будет равно число 235 в шестнадцатеричной системе счисления  при переводе в десятичную систему? Переведите самостоятельно, посмотрите сколь велико отличие.
Нужно как-то уметь отличать. Для этого используются специальные обозначения. Одним из способов является запись в которой шестнадцатеричному числу предшествует пара символов «0х». Т.е. запись 0х235 говорит нам, что 235 это число, записанное в шестнадцатеричной системе счисления.

Переменные и их адреса.

Как отмечалось во втором уроке, каждая переменная хранится в памяти. Естественно у каждой переменной в памяти есть свой адрес, по которому она записана. Мы уже даже использовали адреса переменных, когда пользовались функцией scanf().
Чтобы получить адрес переменной нам необходимо перед её именем написать символ “&”.

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

Листинг 16.1
#include <stdio.h>

int main (){

printf ("razmer peremennoi tipa char %d\n", sizeof(char));

printf ("razmer peremennoi tipa int %d\n", sizeof(int));

printf ("razmer peremennoi tipa float %d\n", sizeof(float));

printf ("razmer peremennoi tipa double %d\n", sizeof(double));

return(0);

}

Результат её работы:
Рис 4. Программа иллюстрирующая работу sizeof()

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

То есть, если я объявляю в программе переменную типа int, то под нее в памяти выделяется 4 байта (ячейки).

Так как мы уже умеем получать адрес переменной, то давайте посмотрим на него. Для того, чтобы вывести число в шестнадцатеричной системе существует специальный спецификатор “%x”. И для него есть модификатор “#” при его записи, шестнадцатеричное число выводится с символами «0х».

Листинг 16.2
#include <stdio.h>

int main (){

      int a,b;

      printf ("adres peremennoi a %#x\n", &a);

      printf ("adres peremennoi b %#x\n", &b);

return(0);

}

Рис.5 Адреса переменных в памяти

Мы получили два адреса 0x12ff60 и 0x12ff56. По этим адресам в памяти записаны наши переменные a и b. При этом они занимают в памяти по 4 клетки подряд, так как это переменные целого типа и из рисунка 3 видно, что их размер 4 байта. Это выглядит примерно следующим образом.
Рис.6. Пример расположения переменных в памяти
Как вы уже заметили, переменные в память записываются не одна за другой, а в произвольном месте, лишь бы там было пусто и хватило места. Исключение составляют массивы. Они записываются в память последовательно. Посмотрите на результат работы следующей программы.

Листинг 16.3
#include <stdio.h>

int main (){

      int a[3];

      printf ("adres peremennoi a[0] %#x\n", &a[0]);

      printf ("adres peremennoi a[1] %#x\n", &a[1]);

return(0);

}

Рис.7 Расположение массива в памяти

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

Адреса это хорошо, но что с ними делать? На кой чёрт они нам сдались?
Обо всем по порядку. 

Указатели. 

Во-первых, для хранения адресов существует специальные переменные, которые называются указателями.  Таким образом, мы вплотную подобрались к теме нашего урока - к указателям.

Указатель – переменная, предназначенная для хранения адреса в памяти.
Обычно, указатели используют, чтобы хранить адреса других переменных.

Объявление указателя.

Указатель, раз это переменная, должен как-то объявляться. Делается это почти что также как и обычно.
int * p_a;

Сначала указывается тип переменной, которая будет храниться в памяти, по адресу указателя. Далее следует специальный символ «*» (звездочка), которая и указывает на то, что мы собираемся объявить не просто переменную типа int, а указатель на переменную типа int. После звездочки пишут имя указателя. Ну и естественно заключительная точка с запятой.

В нашем примере, мы объявили  указатель с именем p_a, который будет указывать на переменную типа int. Кстати, обычно, чтобы не путать указатели с другими переменными, в их имена добавляют какой-нибудь отличительный знак. Например, я вот добавляю обычно “p_”.  Когда я вижу в своей программе переменную, имя которой начинается с этих символов, я точно знаю что это у меня указатель. Кроме того, если программа большая, я помимо этого указывают после «p» ещё и тип переменной для типа int  - i, для floatf, для char – с и так далее, получается что-то типа pi_a. Это  сразу говорит мне, что это указатель, который ссылается на переменную типа int.
  

Присвоение указателю адреса.

Давайте перепишем Листинг 16.2, используя указатели.

Листинг 16.4
#include <stdio.h>

int main (){

      int a,b,*pi_a, *pi_b;

      pi_a=&a;

      pi_b=&b;

      printf ("adres peremennoi a %#x\n", pi_a);

      printf ("adres peremennoi b %p\n", pi_b);

return(0);

}

Рис.8. Пример хранения адреса переменной в указателе.

Как видите, после объявления с указателем можно работать так же, как и с обычной переменной. Ему можно присвоить некоторый адрес, используя оператор «=».
И заметьте, для  вывода указателя можно использовать специальный спецификатор вывода «p».

Получение значения переменной.

Мы можем получить значение, которое хранится по адресу, записанному в указателе. Для этого используется оператор «*». Ага, снова эта пресловутая звездочка. Догадайтесь, куда она записывается? Ага, именно туда, перед именем указателя. Вот такие чудеса творятся иногда.  Надо посмотреть на примере.

Давайте немного изменим Листинг 16.4.  Добавим переменным значения и попробуем получить их, используя указатели.

Листинг 16.5
#include <stdio.h>

int main (){

      int a=3, b=0, *pi_a;    //объявляем переменную a

//и указатель на переменную типа int

      pi_a=&a; // присваиваем указателю адресс переменной а

      *pi_a=b; // записываем в память, по адресу который хранится в указателе

                   // значение переменной b

      printf ("adres peremennoi a %#x\n", pi_a);

      printf ("znachenie po adresu zapisannomy v pi_a %d\n", *pi_a);

return(0);

}

Рис.9. Использование указателей для обращения к значениям переменных,на которые они ссылаются

Как видите, используя запись *pi_a можно обращаться с указателем, как с переменной соответствующего типа. В нашем случае, как с переменной типа int.

Еще раз обсудим звездочку в указателях.
  • Если  звездочка стоит перед именем в объявлении переменной, то в этом случае она означает, что объявляется указатель.
  • Если звездочка встречается внутри программы, то в данном случае, она указывает на то, что мы обращаемся к ячейкам памяти, на которые ссылается указатель.
Еще раз внимательно перечитайте предыдущий пункт. Он очень важен, вам необходимо в этом разобраться. Задавайте вопросы в комментариях, если вам что-то непонятно. С этим обязательно нужно хорошо разобраться.

Есть еще один специальный указатель. Он имеет свое особое название – нулевой указатель NULL. Нулевой указатель не ссылается никуда. Он используется, чтобы обнулять указатели. Посмотрите на следующую программу.

Листинг 16.6
#include <stdio.h>

int main (){

      int a=3,*pi_a;

      pi_a=&a; // присваиваем указателю адресс переменной а

      printf ("adres peremennoi a %#x\n", pi_a);

      pi_a=NULL;

      printf ("adres peremennoi a %#x\n", pi_a);

return(0);

}

Рис.10 Нулевой указатель

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

И под конец урока. Не зря же мы с вами так долго паримся с этими указателями сегодня. Один небольшой пример. Помните занятие про функции? Или недавнюю небольшую головоломку в группе во Вконтакте.
Кто не помнит, вот вам картинка.

Рис.11 Простенькая головоломка. Что выведет представленная программа?
Нам известно, когда мы передаем переменные в функцию, то передаются не сами переменные, а их копии? Иногда нам это вовсе не нужно. Иногда удобно сделать так, чтобы значения все-таки изменялись. Для этого, нужно передавать в функцию не переменную, а указатель на неё.

Давайте перепишем программу с картинки, чтобы она работала так, как и предполагается.

Листинг 16.7
#include <stdio.h>

void obmen (int *pi_a, int *pi_b){

//принимаем указатели на переменные типа int

      int temp;

      temp=*pi_a;

      *pi_a=*pi_b;

      *pi_b=temp;

}

int main (){

      int x=5, y=10;

      obmen(&x,&y);

// ВНИМАНИЕ!передаем адреса, так как функция obmen принимаем указатели

      printf ("Posle x=%d y=%d\n",x,y);

return(0);

}

Рис.12 Пример работы программы с указателями
Как видите, теперь работает как надо. Если вы внимательно разобрались с началом урока, то проблем с этой программой возникнуть не должно. Если вопросы есть, задавайте в комментариях. Я постараюсь все вам разъяснить.

Подробный урок о том, зачем нужны указатели.

И это еще не все возможности указателей. Следующее занятие снова будет посвящено указателям. Их связью с массивами и строками.

Отдельного домашнего задания не будет. Хорошенько разберитесь с этим занятием. Вам должна быть тут понятна каждая строчка. Если интересно, можете потренироваться переводить числа из десятичной системы счисления в шестнадцатеричную, и обратно.  И раз уж мы учимся программировать, то напишите для этого программу. =)))

Если вам понравился этот или другие уроки, расскажите пожалуйста о них, своим друзьям Вконтакте,Facebook,Google+, используя кнопки социальных сетей расположенные ниже.

52 комментария :

  1. Почти готово
    https://www.dropbox.com/s/6qebenm1zel2rx4/brain.jpg

    ОтветитьУдалить
    Ответы
    1. =D Начиная с какого места?

      Удалить
    2. HEX / DEC то я сразу калькулятор набросал.
      А вот начиная со строчки: Указатели. :)
      Да не на самом деле, несколько раз перечитал, на листочке, что-то нарисовал, более менее понятно стало что они из себя представляют, непонятно "зачем?", ну и после того как поймем зачем, думаю на вопрос "как?" ответить будет легче.

      Удалить
  2. Когда читал раздел, было все понятно, когда уже прочел, стало непонятно, но непонятно, что именно не понятно )))

    ОтветитьУдалить
    Ответы
    1. вот так Аноны и уходят в рекурсию =)

      Удалить
  3. По поводу последней задачи, правильно ли я понял, что указатели можно использовать как своеобразные буфера обмена ?

    ОтветитьУдалить
  4. из десятичной в шестнадцатиричную

    #include "stdio.h"
    int main () {
    int a,m[100];
    scanf("%d", &a);
    for (int i=0; a>0; i++) {
    m[i]=a%16;
    a/=16;
    }
    for (int i=100; i>=0; i--) {
    if (m[i]==10)
    printf("A ", m[i]);
    if (m[i]==11)
    printf("B ", m[i]);
    if (m[i]==12)
    printf("C ", m[i]);
    if (m[i]==13)
    printf("D ", m[i]);
    if (m[i]==14)
    printf("E ", m[i]);
    if (m[i]==15)
    printf("F ", m[i]);
    if ((m[i]<10)&&(m[i]>=0))
    printf("%d ", m[i]);
    }
    printf("\n");
    return(0);
    }

    и наоборот

    #include "stdio.h"
    #include "string.h"
    int main () {
    char m[100];
    gets(m);
    int f[100],n=strlen(m);
    for(int i=0; i='0')&&(m[i]<='9'))
    f[i]=m[i]-48; // < < < < < < <
    }
    int s=0, sum=0;
    for (int s=0; s<n; s++) {
    for (int i=0; i<(n-1-s); i++) {
    f[s]=f[s]*16;
    }
    sum=sum+f[s];
    }
    printf("\n%d\n", sum);
    return(0);
    }

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

    напротив строчки, выделенной "стрелками", отнимал 48, потому что выдавало число кодом ASCII. и я не понимал из-за чего это. объясните, пожалуйста. или укажите на урок, в котором это проясняется, если есть такой

    ОтветитьУдалить
    Ответы
    1. Второй код плохо скопировался. Теперь об вашем замечании.
      Это произошло из-за того, что когда мы собираемся работать с элементами массива типа char. Они хранятся как числа. Отдельно я это не помню говорил или нет, но во втором уроке я указывал диапазон этого типа данных, и там он был в числах. Вот. Хорошо, что вы это подметили. На этом нужно обязательно заострить внимание.

      Удалить
  5. Это калькулятор из десятичной в 16ю;
    Работать то работает, но я не очень понимаю как,
    написал сам, методом "научного тыка" и сам же не понимаю,
    прочитал в интернете как компилятор распознаёт и конвертирует отрицательные числа, но не знаю формулу для перевода.


    #include "stdio.h"
    #include "string.h"

    void f_1(int);
    void f_2(int);

    int main ()
    {
    int b;

    scanf ("%i", &b);

    if (b < 0)
    {
    f_2(b);
    }//if
    else
    {
    f_1(b);
    }//else

    printf ("proverka %#x \n", b);

    return (0);
    }//main

    void f_1(int a)
    {
    int x = 0, dlinna, obmen, y = 0;
    char ost[9];

    do
    {
    ost[x] = a%16;
    a = a/16;

    if ((ost[x] >= 0) && (ost[x] <= 9))
    {
    ost[x] = ost[x]+48;
    }//if
    if ((ost[x] >=10) && (ost[x] <= 15))
    {
    ost[x] = ost[x]+87;
    }//if
    x++;
    }
    while (a != 0);

    ost[x] = '\0';

    dlinna = strlen (ost);

    for(y = 0; y < dlinna/2; y++)
    {
    obmen = ost[y];
    ost[y] = ost[dlinna-1-y];
    ost[dlinna-1-y] = obmen;
    }//while

    printf (" 0x%s \n", ost);

    }//f_1

    void f_2(int a)
    {
    int b, x = 0, obmen, dlinna;
    char ost[] = "FFFFFFFF";

    a = a*(-1);
    b = a;

    do
    {
    ost[x] = a%16+1;
    a = a/16;

    if (ost[x] == 0)
    {
    ost[x] = '0';
    a = a-1;
    }//if
    if (ost[x] == 1)
    {
    ost[x] = 'f';
    }//if
    if (ost[x] == 2)
    {
    ost[x] = 'e';
    }//if
    if (ost[x] == 3)
    {
    ost[x] = 'd';
    }//if
    if (ost[x] == 4)
    {
    ost[x] = 'c';
    }//if
    if (ost[x] == 5)
    {
    ost[x] = 'b';
    }//if
    if (ost[x] == 6)
    {
    ost[x] = 'a';
    }//if
    if (ost[x] == 7)
    {
    ost[x] = '9';
    }//if
    if (ost[x] == 8)
    {
    ost[x] = '8';
    }//if
    if (ost[x] == 9)
    {
    ost[x] = '7';
    }//if
    if (ost[x] == 10)
    {
    ost[x] = '6';
    }//if
    if (ost[x] == 11)
    {
    ost[x] = '5';
    }//if
    if (ost[x] == 12)
    {
    ost[x] = '4';
    }//if
    if (ost[x] == 13)
    {
    ost[x] = '3';
    }//if
    if (ost[x] == 14)
    {
    ost[x] = '2';
    }//if
    if (ost[x] == 15)
    {
    ost[x] = '1';
    }//if
    if (ost[x] == 16)
    {
    ost[x] = '0';
    }//if

    x++;
    }
    while (a != 0);

    if (b%16 == 0)
    {
    ost[0] = 48;
    ost[1] = ost[1]+1;

    }//if
    else
    {
    ost[0] = ost[0]+1;
    }//else
    if (ost[0] == ':')
    {
    ost[0] = 'a';
    }//if

    dlinna = strlen (ost);

    for (int y = 0; y < dlinna/2; y++)
    {
    obmen = ost[y];
    ost[y] = ost[dlinna-1-y];
    ost[dlinna-1-y] = obmen;
    }//for

    printf (" 0x%s \n", ost);

    }//f_2

    Подскажите как упростить программу.

    Забросил все стрелялки и только программирую, тут интереснее.
    Спасибо за уроки.

    ОтветитьУдалить
    Ответы
    1. Ой-ой-ой. Страх-то какой =)))
      Во-первых, прошу вас для таких огромных кодов используйте специальный сервис. Подробности тут https://vk.com/away.php?to=http%3A%2F%2Fyoungcoder.blogspot.ru%2F2014%2F02%2Fispolzuem-pastebin.html

      Во-вторых, научный тык это замечательно, это основа любознательности. Но все равно нужно понимать то, что вы пишите. К такому вопросу, нужно подходить обстоятельно.

      Теперь о программе. Давайте пока-что разберемся с переводом в одном из направлений. Пусть хотим перевести число из десятичной системы в шестнадцатеричную. Как это делается, описано в начале урока.
      Попробуйте этот алгоритм описать обычным языком. Ну например, алгоритм программы сложения двух чисел:
      1. Считываем первое число.
      2. Считываем второе число.
      3. Находим их сумму.
      4. Выводим ответ на экран.

      Удалить
    2. Да нет, с переводом положительных чисел всё просто.
      А как перевести отрицательное из 10ой в 16ю, даже на листочке бумаги я не знаю и в интернете не пишут.

      Удалить
    3. И как уменьшить число if, что-бы было покрасивее я что-то не пойму.

      Удалить
    4. Тут смотрите какое дело. С точки зрения математики, перевод отрицательного числа из 10 в 16 ничем не отличается от обычного способа. Просто минус дописываем и всё.

      Другое дело, что есть так называемый "дополнительный код", который предназначен для записи отрицательных чисел памяти компьютера. Используя его, можно извратиться и перевести из 10 в 16 со знаком. По цепочке 10 -> 2 -> 16. Но зачем это может понадобиться, не знаю.

      Больше мне наверное нечего сказать по данному вопросу. Это все, что я знаю. =))

      Про if-ы, я думал fun2 переводит число из 16 в 10, а это оказывается совсем другая функция. Я её еще не разбирал, посмотрю на днях, что там за хитрый способ. ))

      Удалить
  6. #include
    #include



    int main(){

    int a;
    int arr[5];
    int i;
    printf("from 10th to 16th\n");
    scanf("%d", &a);

    for ( i = 0; a > 0; i++)
    {
    arr[i] = a % 16;
    a /= 16;


    }

    for( i = 5; i >= 0; i--)
    {

    if(arr[i] == 10)
    printf("A");

    if(arr[i] == 11)
    printf("B");

    if(arr[i] == 12)
    printf("C");

    if(arr[i] == 13)
    printf("D");

    if(arr[i] == 14)
    printf("E");

    if(arr[i] == 15)
    printf("F");
    if((arr[i] < 10) && (arr[i] >= 0))
    {
    printf("%d", arr[i]);
    }
    }

    return 0;
    }

    ОтветитьУдалить
  7. из шестнадцатеричной в десятиричную

    #include "stdio.h"

    int main()
    {
    char arr[10];
    int i;
    int j;
    int n = 1;
    int summ = 0;

    gets(arr);

    for( j = 0; j < 10 && (arr[j] != '\0'); j++)
    {}//читаем количество элементов

    // printf("%d j is", j);
    for (i = j - 1; i >= 0 ; i--)
    {
    if ((arr[i] > 64) && (arr[i] < 71))
    {
    arr[i] = arr[i] - 55;
    }
    else
    {
    arr[i] = arr[i] - 48;
    }
    // почему -то читает целочисленное значение вместо стринга, это класс!
    // начнем с последнего члена, умножаем его на 16 в 0 степени
    n = 16 * n;
    summ += (arr[i]) * n;


    }

    printf("summ - %d\n", summ / 16);




    return 0;
    }

    ОтветитьУдалить
    Ответы
    1. Хороший трюк с использованием переменной счетчика j, для хранения длинны строки. )
      А как же регистр? С маленькими буквами не работает - не порядок.
      Из каких соображений вы выбрали 10 в качестве максимального размера массива?
      Размещайте пожалуйста свой код на pastebin.ru

      Удалить
    2. Спасибо ) с регистром не сложно исправить, я даже ее заведомо не увеличивал, так как сам слежу за тем, чтобы вводить в верхнем регистре. Насчет размерности, если брать размерность, скажем, 100, то появляется бред, пока объяснить не могу.

      Удалить
    3. просто подумайте сами, 16^100 это очень большое число, его нельзя записать в целый тип. поэтому получается ерунда. Т.е. мы записываем туда мусор какой-то и потом из-за этого все ломается. Регистр нужно добавить.

      Удалить
    4. http://pastebin.ru/Jcb1sqbd
      теперь хоть как вводить можно.

      Удалить
  8. Пожалуйста можете объяснить что это значить и нельзя ли заменить чем то проще м понятнее
    if ((arr[i] > 64) && (arr[i] < 71))
    {
    arr[i] = arr[i] - 55;
    }
    else
    {
    arr[i] = arr[i] - 48;
    }

    ОтветитьУдалить
    Ответы
    1. Могу. с 65 по 70 записаны буквы a b c d e f. Когда мы переводим числа из 16ричной системы нам нужно эти буквы заменить сначала на числа 10 11 12 13 14 15 и 16 соответственно.
      Вот мы этим здесь и занимаемся. Например, получаем "e". Это 69 согласно таблице ASCII. Т.е. у нас считается не буква e, а число 69.
      69 попадает по условие f ((arr[i] > 64) && (arr[i] < 71))
      значит для перевода из 69 вычитаем 55 получаем 14. Как раз то, что и хотелось.
      Теперь по поводу блока else Когда мы считываем числа, они у нас тоже считываются как коды из таблицы ASCII там они расположены начиная с 48 места. Поэтому когда мы считываем "0" с клавиатуры в переменную считывает 48. И чтобы сделать её нулем, вычитаем эти 48.) Вот так вот это работает.

      Удалить
  9. а где же тема структуры?

    ОтветитьУдалить
  10. присоединяюсь к Дмитрию Троцкому с вопросом, "зачем всё это?"...забиваю полочку в голове под названием указатели и не вижу ни начала,ни конца..где это применять и зачем?

    как работает вроде более мение понятно, а вот зачем?

    ведь когда я объявляю переменную int а=3, я ведь могу и с ней и с присвоенным числом работать, без всяких указателей. Зачем мне нужно знать адресса переменных и еще сохранять их в указателях, которые присваивают тоже самое Оо..голова кругом

    Прошу помощи разобраться с кашей в голове))

    ОтветитьУдалить
    Ответы
    1. Отличный вопрос.=))
      Предлагаю вам следующую задачу. Посмотрите на картинку и жду вашего ответа.
      https://pp.vk.me/c424523/v424523218/378d/MV7B4IQl12c.jpg

      Потом продолжим. Как-то я ввел сущность, без видимой необходимости.

      Удалить
    2. Посмотрел картинку)

      написана функция, которая меняет значения местами. В майне вызвана функция с Х и У, тем самым меняя их значения местами.)

      ...

      Удалить
    3. Вы наверное забыли особенности работы функций. Ну да ладно, перепишите попробуйте запустить. Посмотрите как оно работает, немножко удивитесь, я думаю. )

      Удалить
  11. ну да, разобрался вроде..

    но ответа на "зачем" так и не нашел. Один вариант ответа который меня удовлетворил, нашел в интернете. Ответ был таков: что указатели это, как ссылки на переменные. То есть, чтобы при написании большой программы, всегда можно было написать ссылку/указатель на переманную и пользоваться ее, через конкретный адресс, а не так чтобы программа сама искала эту переменную. При миллионом обрабатывании это было бы затруднительно.

    Вот как то так я это понял. Правильно?(

    ОтветитьУдалить
    Ответы
    1. Если разобрались, то по-крайней мере один ответ на вопрос зачем нужны указатели должны были найти.
      Он таков: "Без указателей некоторые вещи просто невозможно написать на Си." Данная программа хорошо иллюстрирующий это пример. Функцию меняющую местами значения двух переменных написать без указателей невозможно.))

      Удалить
  12. я не могу понять зачем нам указатель если можно обратится напрямую)
    * - указатель ? & - адрес ?

    ОтветитьУдалить
  13. Из 10 в 16:

    #include

    int main(void) {
    // your code goes here
    int a;
    scanf ("%d", &a);
    printf ("Vy vveli %d\n", a);
    printf ("V 16 sisteme eto budet %x\n", a);
    return 0;
    }

    Из 16 в 10:

    #include

    int main(void) {
    // your code goes here
    int a;
    scanf ("%x", &a);
    printf ("Vy vveli %x\n", a);
    printf ("V 10 sisteme eto budet %d\n", a);
    return 0;
    }

    ОтветитьУдалить
  14. В листинге 16,4 Когда происходит запрос через %#x получается 0x18fee4.А когда через %p то 0018FED8.Почему так?Спасибо

    ОтветитьУдалить
    Ответы
    1. Добрый день, Александр.
      Так ведь там адреса двух разных переменных выводятся, поэтому и разные значения.

      Удалить
    2. Аа, вы вот о чем. 0x это просто префикс, он говорит лишь о том, что дальше следует число в 16-ричной системе счисления. А когда мы выводим использую %p мы заранее подразумеваем, что это адрес и он будет в 16ричной системе.

      Кроме того, вы могли об это дагдааться учитывая тот факт, что в 16 СС из букв используются только a, b, c, d, e, f. Никакого x там быть не может. )

      Удалить
  15. Этот комментарий был удален автором.

    ОтветитьУдалить
  16. Этот комментарий был удален автором.

    ОтветитьУдалить
  17. //Вечный цикл и выход из него, эксперемент
    #include "iostream"
    #include "locale.h"

    using namespace std;

    void main()
    {
    ____setlocale(LC_ALL, "rus");
    ____char stroka/*Могу ли я не вводить размерность при создании с помощью указателя, а просто считать количество введёных мной данных как размер моего массива?*/[20];
    ____cout<<"Вас преветствует вечное цукуёми\n";
    ____while (true)// вечный цикл
    ____{
    ________cout<<"Попробуйте выйти из цукуёми всеми методами\n";
    ________fgets(stroka, 20, stdin);
    ________if (strncmp(strlwr(stroka), "exit",4)==0)
    ________{
    ____________break;
    ________}
    ____}
    ____cout<<"Поздравляю вы победили Мадару\n";// круть сработало
    }
    //потенциально могу наверное, как то так вероятно
    /*
    ____char temp[1000];
    ____srand(time(0));
    ____for (int i=0;i<srand()%100+1;i++)
    ____{
    ________temp[i]=srand()%26+65;
    ____}
    ____char *stroka = new(char [strlen(temp)]);
    ____delete []temp;
    //// дальще творим добро которое планировали????? ану ща скомпилирую
    */

    ОтветитьУдалить
    Ответы
    1. ещё б я мог цветом выделить нужное

      Удалить
    2. сравнивает строку
      exit
      с веденной строкой переведенной в нижний регистр
      с помощью функции strncmp ( сравнивает определённое количество символов )
      если равно 0
      прописывает break; - выход из цикла
      почему то в задании с сокровищами флинта я это осуществить не смог, по видиму намудрил

      Удалить
  18. #include "iostream"
    #include "string.h"
    #include "time.h"
    #include "stdlib.h"

    using namespace std;

    int main()
    {
    ____char temp[100];//массив для определения
    ____srand(time(NULL));// инициализация рандома
    ____int N=rand()%50+1;// ввод случайного кол-ва символов
    ____for (int i=0;i<N;i++)//ввели
    ____{
    ________temp[i]=rand()%26+65;//случайно заполнили
    ________if(i==N-1)
    ________{
    ____________temp[N+1]='\0';
    ____________cout<<"Znakov ="<<strlen(temp);//вывод количества знаков
    ________}
    ____}
    ____cout<<strlen(temp)<<endl;
    ____puts(temp);

    ____char *stroka = new(char [N]);
    ____cout<<strlen(stroka);//понятия не имею почему, но ни ____разу он не вывел мне число совпадающее с N

    ____for(int i=0;i<N; i++)
    ____{
    ________stroka[i]=temp[i];
    ____}
    ____cout<<strlen(stroka)<<endl;
    ____puts(stroka);
    ____delete []temp;
    ____return 0;
    }
    //Не работает, как сделать чтобы заработало?

    ОтветитьУдалить
    Ответы
    1. strlen(temp) выдаёт порой 3000-5000........

      Удалить
    2. точнее как выводить то он выводит stroka.... но дописывает иногда свои значения и стабильно 4 сюрикена в конце. и ошибку выдаёт из за строки
      delete []temp;//если удаляю строку ошибка исчезает

      Удалить
  19. Этот комментарий был удален автором.

    ОтветитьУдалить
  20. вот это работает идеально
    Функция delete, она удаляет только то что было создано внутри программы? нигде не сработала((
    #include "iostream"
    #include "stdlib".h
    #include "time.h"

    using namespace std;

    int main()
    {
    ____int temp[100];//массив для определения
    ____srand(time(NULL));// инициализация рандома
    ____int N=rand()%100+1;// ввод случайного кол-ва символов
    ____int konec;
    ____for (int i=0;i<N;i++)//ввели
    ____{
    ________temp[i]=rand()%25+1;//случайно заполнили
    ________if(temp[i]==1)
    ________{
    ____________konec=i;
    ____________break;
    ________}
    ________if (i==N-1)
    ________{
    ____________konec=i;
    ________}
    ____}

    ____int *stroka = new(int [konec]);

    ____for(int i=0;i<konec; i++)
    ____{
    ________stroka[i]=temp[i];
    ____}
    ____for(int i=0;i<konec;i++)
    ____{
    ________cout<<"№"<<i+1<<" temp"<<temp[i]<<" stroka"<<stroka[i]<<endl;
    ____}
    ____if(stroka[N+1]==true)// не знаю работает или нет, так ни разу ошибку и не обнаружило
    ____{
    ________cout<<stroka[N+1];
    ____}
    ____return 0;
    }
    хотел было вывести надписи в одну строку temp и stroka по вертикали но iostream не умеет походу......
    зато cin лишних символов не воспринимает

    ОтветитьУдалить
    Ответы
    1. код вышел слегка не коротким, не знаю понадобится ли такой кому можно в принципе по удалять вывод и проверку , плюс убрать рандом и сделать ввод до 0

      Удалить
    2. в любом случае если delete не удаляет записанную в самом коде переменную в этом всём нету смысла
      укоротил код;

      #include

      using namespace std;

      void main()
      {
      ____int temp[100];//массив для определения
      ____int ch=0;
      ____do
      ____{
      ________cin>>temp[ch];
      ________ch++;
      ____}while(temp[ch-1]!=0);

      ____int *stroka = new(int [ch-1]);

      ____for(int i=0;i<ch; i++)
      ____{
      ________stroka[i]=temp[i];
      ____}
      }

      Удалить

Примечание. Отправлять комментарии могут только участники этого блога.