Языки программирования. отчёт 1. Указатели
Скачать 2.35 Mb.
|
Балтийский государственный технический университет «ВОЕНМЕХ» им. Д. Ф. Устинова Кафедра О7 «Информационные системы и программная инженерия» Практическая работа №3 по дисциплине «Информатика: Основы программирования» на тему «Указатели» Выполнил: Студент Логинов К.В. Группа О712Б Преподаватель: Матвеев Т.А. Санкт-Петербург 2021 г. Задание 1. Проанализировать текст представленной программы и выдаваемые программой результаты. Объяснить, почему результаты именно такие. Результаты работы программы: Текст программы: intmain() { /* «Обычные» переменные */ int a = 1; float b = 2; double c = 3; /* Указатели */ int *p1 = &a; float *p2 = &b; double *p3 = &c; void *p4; /* Адреса «обычных» переменных и размер выделяемой памяти */ printf("a: int: start address %p extent %d\n",&a,sizeof(a)); printf("b: float: start address %p extent %d\n",&b,sizeof(b)); printf("c: double: start address %p extent %d\n\n",&c,sizeof(c)); /* Адреса указателей и размер выделяемой памяти */ printf("p1: pointer: start address %p extent %d\n",&p1,sizeof(p1)); printf("p2: pointer: start address %p extent %d\n",&p2,sizeof(p2)); printf("p3: pointer: start address %p extent %d\n",&p3,sizeof(p3)); printf("p4: pointer: start address %p extent %d\n\n",&p4,sizeof(p4)); /* Значения, на которые ссылаются указатели */ printf("p1: %p related value %d\n",p1,*p1); printf("p2: %p related value %f\n",p2,*p2); printf("p3: %p related value %lf\n\n",p3,*p3); Размещение переменных в памяти: Под переменную а выделяется 4 байта в памяти, потому что переменная а имеет тип int, под переменную b выделяется 4 байта в памяти, потому что переменная а имеет тип float, Под переменную c выделяется 8 байт в памяти, потому что переменная а имеет тип double. Под указатели p1 (указатель на переменную a), p2 (указатель на переменную b), p3 (указатель на переменную c), p4 (не указывает не на какую переменную, тип void) выделяется по 8 байт в памяти (потому что OC64-x разрядная). /* Использование указателей в выражениях */ printf("a=%d\tb=%f\tc=%lf\n",a,b,c); *p1 = 5; *p2 = *p2 * *p1; *p3 = sqrt(*p3); printf("a=%d\tb=%f\tc=%lf\n",a,b,c); printf("*p1=%d\t*p2=%f\t*p3=%lf\n\n",*p1,*p2,*p3); /* Присваивание указателей */ p1 = (int*)p2; p3 = (double*)p2; p4 = p2; printf("p1=%p\tp2=%p\tp3=%p\tp4=%p\n",p1,p2,p3,p4); printf("*p1=%d\t*p2=%f\t*p3=%f\t*(float*)p4=%f\n\n",*p1,*p2,*p3,*(float*)p4); Размещение переменных в памяти: К разыменованному указателю p1 (который указывает на переменную a) присваивается значение 5 – после этого переменная а имеет значение 5 К разыменованному указателю p2 (который указывает на переменную b) присваивается значение произведения разыменованных указателей p2 и p1 – после этого переменная b имеет значение 10.000000 К разыменованному указателю p3 (который указывает на переменную с) присваивается значение корня из значения разыменованного указателя p3 – после этого переменная cимеет значение 1.732051 После этого к указателю p1 присваивается указатель p2, приведённый к типу int К указателю p3 присваивается указатель p2, приведённый к типу double К указателю p4 присваивается указатель p2 В итоге все указатели имеют адрес FDD4 (адрес указателя p2) После присваивания к p1 указателя p2 значение, которое было в p2 = (10) = 01000001 00100000 00000000 00000000 переводится в 10-чную СС, потому что p1 – указатель на int, поэтому получается такое значение = 1092616192 После присваивания к p3 указателя p2, выводит очень большое отрицательное значение, из-за того, что к типу double добавляются первые 32 цифры из переменной b (10) = 01000001 00100000 00000000 00000000, а далее добавляются ещё 32 первые цифры из переменной с (1.732051) = 00111111 11111011 10110110 01111011 00011100 00000000 00010000 11000111, берем первые 32 цифры = 00111111 11111011 10110110 01111011, получается 01000001 00100000 00000000 00000000 00111111 11111011 10110110 01111011 = 4692750812793517691 /* Изменение значений указателей */ p1++; p3--; printf("p1=%p\tp2=%p\tp3=%p\n",p1,p2,p3); printf("*p1=%d\t\t*p2=%f\t*p3=%lf\n",*p1,*p2,*p3); Размещение переменных в памяти: После операции p1++ указатель p1 указывает на переменную c, потому что к адресу FDD4 (адрес первого байта переменной b) прибавили 4 байта (потому что указатель имеет тип int = (1*(sizeof(int)), получили адрес FDD8 (адрес первого байта переменной c) После операции p3-- указатель p3 не указывает ни на какую из объявленных переменных, потому что из адреса FDD4 (адрес первого байта переменной b) вычли 8 байт (потому что указатель имеет тип double = (1*(sizeof(double)), получили адрес FDCC Значение переменной, хранящейся в указателе p1 = -396866390 Значение переменной, хранящейся в указателе p2 = 10.000000 (потому что указатель p2 указывает на переменную b, значение которой равно 10.000000) Значение разыменованного указателя p3 = 0, потому что в данном случае он не указывает ни на какую объявленную переменную p1 -= 4; p3 = (double*)&a - 1; printf("p1=%p\tp2=%p\tp3=%p\n",p1,p2,p3); printf("*p1=%d\t*p2=%f\t*p3=%lf\n",*p1,*p2,*p3); return0; } Размещение переменных в памяти: После операции p1 -= 4 указатель p1 не указывает ни на какую из объявленных переменных, потому что из адреса FDD8 (адрес первого байта переменной c) вычли 16 байт (потому что указатель имеет тип int = (4*sizeof(int)), получили адрес FDC8 К указателю p3 присваивается значение адреса переменной а, который приведен к типу double, затем вычитается 8 байт (1*sizeof(double)) – сначала адрес указателя становится FDDO (адрес первого байта переменной а), а после вычитания 8 байт адрес становится FDC8 Значение разыменованного указателя p1 = 0, потому что в данном случае он не указывает ни на какую объявленную переменную Значение переменной, хранящейся в указателе p2 = 10.000000 (потому что указатель p2 указывает на переменную b, значение которой равно 10.000000) Значение разыменованного указателя p3 = 0, потому что в данном случае он не указывает ни на какую объявленную переменную Выводы: Если к указателю применяются операции инкремента и декремента, то адрес указателя сдвигается на следующее значение (1*sizeof(базовый_тип)) Под указатели выделяется 4 или 8 байт памяти, в зависимости от архитектуры ОС Указатели можно приводить к разным типам, но это опасно, могут получиться не те значения, которые мы ожидаем Задание 2. Операционная система Windows 10, среда разработки Code::Blocks Проанализировать текст представленной программы, найти в нем синтаксические ошибки и исправить их, в начало программы добавить вывод на экран адресов всех переменных, а в конец – значений всех переменных, проанализировать полученные результаты и объяснить, почему они именно такие. Заменить инструкцию «m+=2;» инструкцией «m++;», проанализировать результат. Текст измененной программы: #include #include int main() { char *p, c; int *a, b; float *x, y = 3.5; double *m, n; printf("char: adress *p = %p, adress c = %p\n",&p,&c); printf("int: adress *a = %p, adress b = %p\n",&a,&b); printf("float: adress *x = %p,adress y = %p\n",&x,&y); printf("double: adress *m = %p, adress n = %p\n\n",&m,&n); a = &b; printf("Enter b = "); scanf("%d", a); printf("a=%p\t*a=%d\tb=%d\n", a, *a, b); p = a; c = *p; *p = *(p+3); *(p+3) = c; printf("p=%p\tc=%d\ta=%p\tb=%d\n", p, c, a, b); x = &y; printf("x=%p\t*x=%f\ty=%f\n", x, *x, y); a = x; *a = *x; printf("a=%p\t*a=%d\tx=%p\t*x=%f\ty=%f\n", a, *a, x, *x, y); a = &b; y = 12345.6789; printf("x=%p\t*x=%f\ty=%f\n", x, *x, y); p = x; c = *p; *p = *(p+3); *(p+3) = c; printf("p=%p\tc=%d\tx=%p\ty=%f\n", p, c, x, y); m = &n; printf("m=%p\t*m=%lf\tn=%lf\n", m, *m, n); n = 5.5; printf("m=%p\t*m=%lf\tn=%lf\n", m, *m, n); b = n = y = 1.7; printf("b=%d\ty=%f\tn=%lf\n", b, y, n); printf("*a=%d\t*x=%f\t*m=%lf\n", *a, *x, *m); m += 2; printf("n=%lf\tn=%p\tm=%p\n", n, &n, m); *m = (float)*a - n + (int)*x; printf("m=%p\t*m=%lf\n", m, *m); printf("char: *p = %d, c = %d\n",*p,c); printf("int: *a = %d, b = %d\n",*a,&b); printf("float: *x = %f, y = %f\n",*x,y); printf("double: *m = %lf, n = %lf\n\n",*m,n); return 0; } Результаты работы программ: первый вариант (m+=2;) второй вариант (m++;) Размещение переменных в памяти: Чем меньше байт в памяти занимает переменная – тем меньший адрес она имеет. Если переменные занимают одинаковое кол-во байт, то меньший адрес у той переменной, которая объявлена ранее. p = a; c = *p; *p = *(p+3); *(p+3) = c; printf("p=%p\tc=%d\ta=%p\tb=%d\n", p, c, a, b); В указатель p присваивается адрес переменной b, на которую указывает указатель а В переменную с (тип char) присваивается значение первого байта переменной b, потому что указатель p указывает на переменную b В первый байт переменной b записывается четвертый байт переменной b В четвертый байт переменной b присваивается значение переменной c (первый байт b), таким образом поменялись первый и четвертый байт переменной b В выводе указатель p имеет адрес FDE0 (адрес переменной b), потому что (см.№1), а указатель а имеет значение переменной b В выводе значение переменной с = 2, потому что (см.№2), затем (см.№5). В выводе указатель а имеет адрес FDE0 (адрес переменной b), потому что ещё в начале программы мы сделали указатель а на адрес переменной b В выводе значение переменной b = 33554432, потому что мы меняли 1 и 4 байты = 00000010 00000000 00000000 00000000 при переводе в 10-сс = 33554432 Размещение переменных в памяти: x = &y; printf("x=%p\t*x=%f\ty=%f\n", x, *x, y); a = x; *a = *x; printf("a=%p\t*a=%d\tx=%p\t*x=%f\ty=%f\n", a, *a, x, *x, y); В указатель x (тип float) присваивается адрес переменной y (тип float), адрес x теперь FDE4 В выводе адрес указателя x = FDE4, потому что (см.№1) В выводе значение указателя x = 3.500000, потому что (см.№1), а переменная y равна 3.500000, потому что мы ранее присваивали ей такое значение В выводе значение переменной y = 3.500000, потому что мы ранее присваивали ей такое значение В указатель а (тип int) присваивается указатель x (тип float) Значение указателя a теперь равно значению указателю x В выводе адрес указателя а = FDE4, потому что (см.№5), затем (см.№1) В выводе значение указателя а = 3, потому что (см.№6), но т.к. а – это тип int, то дробная часть отбрасывается и остаётся только целая В выводе адрес указателя x = FDE4, потому что (см.№1) В выводе значения переменной y, на который указывает указатель x = 0.000000, потому что если выводить через спецификатор e значение будет равно 4.2*10^-45, а это очень близко к 0, 3 – это 11 в 2-сс, в типе float у нас 32 значения – будет = 00000000000000000000000000000011, это и есть число 4.20389539297445121277118874987E-45 В выводе значения переменной y = 3.500000, потому что мы ранее присваивали ей такое значение p = x; c = *p; *p = *(p+3); *(p+3) = c; printf("p=%p\tc=%d\tx=%p\ty=%f\n", p, c, x, y); В указатель p (тип char) присваивается адрес указателя x (тип float), а указатель x указывает на переменную y и имеет адрес FDE4 В переменную с (тип char) присваивается значение первого байта переменной b, потому что указатель p указывает на переменную b В первый байт переменной b записывается четвертый байт переменной b В четвертый байт переменной b присваивается значение переменной c (первый байт b), таким образом поменялись первый и четвертый байт переменной b В выводе адреса указателя p = FDE4, потому что (см.№1) В выводе значения переменной c = -73, потому что берется первый байт 10110111 (обратный порядок), это в доп.коде переводим в прямой код и потом переводим в 10-сс = 11001001 – это и есть -73 В выводе адрес указателя x = FDE4, а указатель x указывает на переменную y и имеет адрес FDE4 В выводе значения переменной y = -0.000011, потому что мы ранее меняли 1 и 4 байт - 10110111010000001110011001000110 , переводим в 10-сс - это и есть число -0.000011 m += 2; printf("n=%lf\tn=%p\tm=%p\n", n, &n, m); *m = (float)*a - n + (int)*x; printf("m=%p\t*m=%lf\n", m, *m); printf("…………); Поясняем, что происходит в этом блоке, иллюстрируем на схеме
После замены инструкции «m+=2;» инструкцией «m++;»что изменилось и почему. Иллюстрируем на схеме.
Задание 3. Объявить по две переменные типов char, intи double, а также указатель на char. Вывести на экран размеры и адреса всех переменных, начертить схему расположения переменных в памяти. Поменять порядок объявления переменных (например, int,char,double, char*, char, double, int). Запустить программу повторно, проанализировать, что изменилось. Задать переменной типа int такое значение, чтобы значение каждого байта было уникальным, использовать для этого шестнадцатеричную константу. Записать адрес этой переменной в указатель на char и с его помощью вывести на экран содержимое каждого байта (тоже в шестнадцатеричной системе счисления). Проанализировать, прямой или обратный порядок расположения байт при записи числа применяется в используемой системе. Повторить выполнение этого задания на компьютере с другой операционной системой и/или другой IDE (можно использовать домашний компьютер или онлайн - компилятор). Сравнить результаты работы программы на разных платформах, сделать выводы. Операционная системаWindows 10, среда разработки Code::Blocks Текст программы (первый порядок объявления переменных): #include #include #include int main() { char c1, c2; int i1, i2; double d1, d2; char* uc = &c1; printf("c1: char: start address %p extent %d\n",&c1,sizeof(c1)); printf("c2: char: start address %p extent %d\n\n",&c2,sizeof(c2)); printf("i1: int: start address %p extent %d\n",&i1,sizeof(i1)); printf("i2: int: start address %p extent %d\n\n",&i2,sizeof(i2)); printf("d1: double: start address %p extent %d\n",&d1,sizeof(d1)); printf("d2: double: start address %p extent %d\n\n",&d2,sizeof(d2)); printf("uc: pointer: start address %p extent %d\n",&uc,sizeof(uc)); return 0; } Результаты работы программы: Размещение переменных в памяти: Текст программы (второй порядок объявления переменных): #include #include #include #include int main() { setlocale(LC_ALL, "Russian"); int i1 = 0xFFB48CAA; char c1; double d1; char* uc; uc = (char*)&i1; char c2; double d2; int i2; printf("i1: int: start address %p extent %d\n",&i1,sizeof(i1)); printf("c1: char: start address %p extent %d\n",&c1,sizeof(c1)); printf("d1: double: start address %p extent %d\n",&d1,sizeof(d1)); printf("uc: pointer: start address %p extent %d\n",&uc,sizeof(uc)); printf("c2: char: start address %p extent %d\n",&c2,sizeof(c2)); printf("d2: double: start address %p extent %d\n",&d2,sizeof(d2)); printf("i2: int: start address %p extent %d\n",&i2,sizeof(i2)); for (int i = 0; i < 4; i++) { printf("%d байт = %hhx\n",i+1,*uc); uc += 1; } return 0; } Результаты работы программы: Размещение переменных в памяти: Выводы: Чем меньше байт в памяти занимает переменная – тем меньший адрес она имеет. Если переменные занимают одинаковое кол-во байт, то меньший адрес у той переменной, которая объявлена ранее. В используемой системе применяется обратный порядок расположения байт. Порядок следования байт при записи числа 0xFFB48CAA такой: 1 байт = ffaa 2 байт = ff8c 3 байт = ffb4 байт = ffff Операционная системаWindows 10, среда разработки онлайн - компилятор GDB. Результаты работы программы при первом порядке объявления переменных: Размещение переменных в памяти: Результаты работы программы при втором порядке объявления переменных: Размещение переменных в памяти: Выводы: Чем меньше байт в памяти занимает переменная – тем меньший адрес она имеет. Если переменные занимают одинаковое кол-во байт, то меньший адрес у той переменной, которая объявлена ранее. В используемой системе применяется обратный порядок расположения байт. Порядок следования байт при записи числа 0xFFB48CAA такой: 1 байт = aa 2 байт = 8c 3 байт = b4 4 байт = ff Выводы: Отличий не выявил, только что в онлайн – компиляторе в выводе порядка следования байт нет префикса «ff», в отличие от Code::Blocks |