Ошибка выражение должно быть допустимым для изменения левосторонним значением

Ошибка: Выражение должно быть допустимым для изменения левосторонним значением C++ Решение и ответ на вопрос 804099

kukhtikov

2 / 2 / 0

Регистрация: 16.12.2012

Сообщений: 97

1

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

09.03.2013, 19:19. Показов 132447. Ответов 9

Метки нет (Все метки)


C++
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
class ts_ch
{
    int zn;
public:
    ts_ch()
    {
        zn=0;
    }
    ts_ch(int a)
    {
        zn=a;
    }
    int proverka(int a)
    {
        zn=a;
        if (zn % 2 = 0)
        {
            std::cout << "Число четное";
            return 1;
        }
        else
        {
            std::cout << "Число не четное";
            return 0;
        }
    }
    void print()
    {
        std::cout << "Значение числа: " << zn << ". Проверка: " << proverka(zn);
    }
};

Условие задачи:

Кликните здесь для просмотра всего текста

Создать класс «целое число».
Элементы класса:
поле задает значение целого числа (статус доступа private);
конструктор для инициализации поля;
метод, возвращающий 1, если число четное, и 0, в противном случае.
Создать 1 объект данного класса, проверить четное ли число, вывести соответствующее сообщение.

В строке if (zn % 2 = 0) компилятор указывает на zn и пишет: Выражение должно быть допустимым для изменения левосторонним значением.
В чем у меня ошибка?

__________________
Помощь в написании контрольных, курсовых и дипломных работ, диссертаций здесь



0



Черный ворон

137 / 131 / 57

Регистрация: 31.01.2012

Сообщений: 435

09.03.2013, 19:21

2

C++
1
if (zn % 2 == 0)



4



2 / 2 / 0

Регистрация: 16.12.2012

Сообщений: 97

09.03.2013, 19:48

 [ТС]

3

Черный ворон, спасибо) Тупая ошибка..



0



Kruti

0 / 0 / 0

Регистрация: 30.12.2019

Сообщений: 1

30.12.2019, 12:32

4

Добрый день у мене такая проблема:
E0137 выражение должно быть допустимым для изменения левосторонним значением TreeC++

C:UsersAdminsourcereposTreeC++TreeC++Main.c pp

вот код

C++
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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include <iostream>
#include <vector>
using namespace std;
 
struct node
{
int key[1];
node* l, * r;
int day, month, year;
};
node p;
struct Porushena
{
int day, month, year;
};
Porushena pr;
void Enter()
{
cout << "Enter day:";
cin >> pr.day;
cout << "Enter month:";
cin >> pr.month;
cout << "Enter year:";
cin >> pr.year;
p.day = pr.day;
 
p.month = pr.month;
p.year = pr.year;
}
void Add(int a[4], node** t[5])
{
if ((*t) == NULL)
{
(*t[1]) = new node;
(*t[2])->l = (*t[2])->r;
(*t[2])->key = a[1];
(*t[3])->l = (*t[3])->r;
(*t[3])->day = a[2];
(*t[4])->l = (*t[4])->r;
(*t[4])->month = a[3];
(*t[5])->l = (*t[5])->r;
(*t[5])->day = a[4];
}
}
int main()
{
Enter();
system("pause");
return 0;
}

подскажите почему оно указывает на 36 строчку «(*t[2])->key = a[1];» ?



0



zayats80888

5696 / 3134 / 1307

Регистрация: 07.02.2019

Сообщений: 7,878

30.12.2019, 12:35

5

Цитата
Сообщение от Kruti
Посмотреть сообщение

(*t[2])->key = a[1];

C++
1
(*t[2])->key[0] = a[1];



0



1642 / 1091 / 487

Регистрация: 17.07.2012

Сообщений: 5,345

30.12.2019, 12:50

6

Kruti,
1) Официальные языки форума русский и английский. По правилам форума общаться можно только на этих языках.
2) Не надо вручную перед каждой строкой писать ее номер, достаточно выделить код, нажать на кнопку «C++» в редакторе сообщений и нумерация сама появится. + будет подсветка синтаксиса. Такой код читать намного проще. Не надо кидать код без тегов(когда нажимаете на кнопку, код автоматически обернется в теги [CPP]ваш код[/CPP]).
3) Для решения своей проблемы лучше создавать новую тему, а не писать в теме 6-летней давности.
Я понимаю, что на форуме вы первый день, но в следующий раз рекомендую действовать по правилам.



0



Вездепух

Эксперт CЭксперт С++

10458 / 5725 / 1556

Регистрация: 18.10.2014

Сообщений: 14,135

30.12.2019, 12:53

7

Цитата
Сообщение от Kruti
Посмотреть сообщение

подскажите почему оно указывает на 36 строчку «(*t[2])->key = a[1];» ?

Потому что (*t[2])->key — это массив. Массивы не разрешается присваивать.



0



0 / 0 / 0

Регистрация: 01.12.2020

Сообщений: 1

01.12.2020, 22:39

8

У меня такая же проблема

Вот код:

#include <iostream>
#include <stdlib.h>
#include <time.h>
using namespace std;
int main()
{
srand(time(NULL));
setlocale(LC_ALL, «RUSSIAN»);
int n = 0;
int first_massiv[21];
int a[6][6];
cout << » 1) Рандомные числаn 2) Ручной ввод с клавиатуры n что хочешь: «;
cin >> n; // ЗДЕСЬ ПРОВЕРКА
switch (n)
{
case 1:
for (int i = 0; i < 21; i++)
{
a[i] = -10 + rand() % 21;
}
break;
case 2:
for (int i = 0; i < 11 && i > -11; i++)
{
cout << «Вводи число «;
cin >> n; // ЗДЕСЬ ПРОВЕРКА
first_massiv[i] = n;
}
break;
}

cout << endl;
for (int i = 0; i < 21; i++)
{
cout << first_massiv[i] << » «;
}
cout << endl;
cout << endl;

for (int i = 0, q = 0; i < 6; i++)
{
for (int j = 0; j < 6; j++)
{
if (i == j || i > j)
{
a[i][j] = first_massiv[q];
q++;
}
else a[i][j] = 0;
}
}

for (int i = 0; i < 6; i++)
{
for (int j = 0; j < 6; j++)
{
if (a[i][j] < 10) cout << a[i][j] << » «;
else cout << a[i][j] << » «;
}
cout << endl;
}

return 0;
}

Почему появилась ошибка в этом месте a[i] = -10 + rand() % 21?



0



41 / 26 / 17

Регистрация: 11.11.2019

Сообщений: 92

01.12.2020, 22:58

9

Цитата
Сообщение от Xeronchik
Посмотреть сообщение

Почему появилась ошибка в этом месте a[i] = -10 + rand() % 21?

Взгляните на массив a, какой он у вас при объявлении, а затем на массив a в цикле. Вы наверное хотели использовать массив first_massiv, но не заметили.



0



Вездепух

Эксперт CЭксперт С++

10458 / 5725 / 1556

Регистрация: 18.10.2014

Сообщений: 14,135

01.12.2020, 22:58

10

Цитата
Сообщение от Xeronchik
Посмотреть сообщение

Почему появилась ошибка в этом месте a[i] = -10 + rand() % 21?

Почему код не оформлен тегами?

Что такое a[i] и почему вы пытаетесь что-то присваивать в a[i]?



0



Видео урок.

Прежде чем говорить о структурах, вспомним массивы. Как вы, наверное, помните, массивы предназначены для хранения однотипных данных. Другими словами каждый элемент массива представляет собой значение определенного типа: целое число, символ, строка. Но зачастую, в программах, требуется хранить в одном месте данные разных типов. В качестве примера, в этом уроке будем рассматривать программу каталог книг. Про каждую книгу нам известно: название, автор, год издания, количество страниц, стоимость.
Типы переменных, используемые для хранения подобных данных очевидны:
char[] – автор, название.
int – год издания, количество страниц.
float – стоимость.

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

int book_date[100];        // дата издания
int book_pages[100];       // количество страниц
char book_author[100][50]; // автор
char book_title[100][100]; // название книги
float book_price[100];     //стоимость

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

printf(«%s-%s %d page», book_author[3], book_title[3], book_pages[3]);

Оставим пока что эту реализацию, и посмотрим, как выполнить такую же задачу с использованием структур. Но прежде всего, определим, что такое структура.

Что такое структура.

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

Возвращаясь к нашему примеру, опишем структуру book с полями date, pages, author, title, price соответствующих типов.

struct book {
    int date;        // дата издания
    int pages;       // количество страниц
    char author[50]; // автор
    char title[100]; // название книги
    float price;     // стоимость
};

Попутно отметим, что в качестве полей структуры могут выступать любые встроенные типы данных и даже другие структуры. Подробнее об этом я расскажу в другом уроке. На имена полей накладываются те же ограничения, что и на обычные переменные. Внутри одной структуры не должно быть полей с одинаковыми именами. Имя поля не должно начинаться с цифры. Регистр учитывается.
После того, как мы описали внутреннее устройство структуры, можно считать, что мы создали новый тип данных, который устроен таким вот образом. Теперь этот тип данных можно использовать в нашей программе.
ПРИМЕЧАНИЕ:  Обычно структуры описываются сразу после подключения заголовочных файлов. Иногда, для удобства, описание структур выносят в отдельный заголовочный файл.

Как объявить структурную переменную (структуру).

Объявление структурной переменной происходит по обычным правилам.
 struct book kniga1;

Такое объявление создает в памяти переменную типа book, с соответствующими полями.

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

Отмечу, что я сознательно не касаюсь вопроса о том, как хранится структура в памяти, так как считаю, что для новичков эти тонкости будут излишни.
Кроме того, еще одну удобную интерпретацию структуры дает нам книга K&R. Можно думать о ней, как о строчке в таблице, где столбцами выступают поля структуры.

Если кто-то имел дело с реляционными базами данных (MySQL, Oracle), то вам эта такая интерпретация будет очень знакома.

Как инициализировать структуру.

Хорошо, переменную мы объявили. Самое время научиться сохранять в неё данные, иначе, зачем она нам вообще нужна. Мы можем присвоить значения сразу всем полям структуры при объявлении. Это очень похоже на то, как мы присваивали значения массиву при его объявлении.

struct book kniga1 = {1998, 230, «D. Ritchi»,«The C programming language.», 540.2};

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

struct book kniga1 = {1998, 230, «D. Ritchi»};

Как обращаться к полям структуры.

Для обращения к отдельным полям структуры используется оператор доступа «.». Да-да, обычная точка.
Примеры:

kniga1.pages = 250; // записываем в поле pages переменной kniga1
                    // значение 250.
printf(«%s-%s %d page», kniga1.author, kniga1.title, kniga1.pages);

Задание: Проверить, что хранится в полях структуры до того, как им присвоено значение.
Сейчас, в одной переменной типа book храниться вся информация об одной книге. Но так как в каталоге вряд ли будет всего одна книга, нам потребуется много переменных одного и того же типа book. А значит что? Правильно, нужно создать массив таких переменных, то есть массив структур. Делается это аналогично, как если бы мы создавали массив переменных любого стандартного типа.

struct book kniga[100];

Каждый элемент этого массива это переменная типа book. Т.е. у каждого элемента есть свои поля date, pages, author, title, price.  Тут-то и удобно вспомнить о второй интерпретации структуры. В ней массив структур будет выглядеть как таблица.

Используя массив структур получить информацию об отдельной книге можно следующим образом.

printf(«%s-%s %d page», kniga[3].author, kniga[3].title, kniga[3].pages);

Обращаясь к kniga[3].author мы обращаемся к четвертой строке нашей таблицы и столбику с именем author. Удобная интерпретация, не правда ли?
На данном этапе мы научились основам работы со структурами. Точнее мы переписали с использованием структур тот же код, который использовал несколько массивов. Кажется, что особой разницы нет.  Да, на первый взгляд это действительно так. Но дьявол, как обычно, кроется в мелочах.
Например,  очевидно, что в нашей программе нам часто придется выводить данные о книге на экран. Разумным решением будет написать для этого отдельную функцию.
Если бы мы пользуемся несколькими массивами, то  эта функция выглядела бы примерно так:

void print_book ( int date, int pages, char *author, 
                  char *title, float price){
      printf(«%s-%s %d page.nDate: %d nPRICE: %f rub.n»,
             author, title, pages,date, price);
}

А её вызов выглядел как-то вот так:

print_book (book_date[3], book_pages[3], book_author[3],
            book_title[3], book_price[3]);

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

void sprint_book (book temp){
      printf(«%s-%s %d page.nDate: %d nPRICE: %f rub.»,
             temp.author,temp.title, temp.pages, 
             temp.date, temp.price);
}

И вызов, выглядел бы приятнее:

sprint_book (kniga[3]);

Вот это я понимаю, быстро и удобно, и не нужно  каждый раз писать пять параметров. А представьте теперь, что полей у структуры  бы их было не 5, а допустим 10? Вот-вот, и я о том же.
Стоит отметить, что передача структурных переменных в функцию, как и в случае обычных переменных осуществляется по значению. Т.е. внутри функции мы работаем не с самой структурной переменной, а с её копией. Чтобы этого избежать, как и в случае переменных стандартных типов используют указатель на структуру. Там есть небольшая особенность, но об этом я расскажу в другой раз.
Кроме того, мы можем присваивать  структурные переменные, если они относятся к одному и тому же шаблону. Зачастую это очень упрощает программирование.
Например, вполне реальная задача для каталога книг, упорядочить книги по количеству страниц.
Если бы мы использовали отдельные массивы, то сортировка выглядела бы примерно так.

for (int i = 99; i > 0; i—)
    for (int j = 0; j < i; j++)
       if (book_pages[j] > book_page[j+1]){
            //меняем местами значения во всех массивах
            int temp_date;
            int temp_pages;
            char temp_author[50];
            char temp_title[100];
            float temp_price;
  
            temp_date = book_date[i];
            book_date[i] = book_date[j];
            book_date[j] = temp_date;


            temp_pages = book_pages[i];
            book_pages[i] = book_pages[j];
            book_pages[j] = temp_pages;


            //и так далее для остальных трех массивов
       }

Совсем другой дело, если мы используем структуры.

for (int i = 99; i > 0; i—)
   for (int j = 0; j < i; j++)
      if (knigi[j].pages > knigi[j+1].pages){
          struct book temp;
          temp = knigi[j];        //присваивание структур
          knigi[j] = knigi[j+1];
          knigi[j+1] = temp;
      }

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

Практическое задание:

1. Добавить в структуру поле количество прочитанных страниц.
2. Напишите несколько дополнительных функций для описанной программы.

  • Чтение данных  в структуру из файла. В файле запись о каждой книге хранится в следующем формате:

Автор||Название||Год издания||Прочитано||Количество страниц||Стоимость

Примеры:

Khnut||Art of programming. T.1||1972||129||764||234.2
Ritchie||The C Programming Language. 2 ed.||1986||80||512||140.5
Cormen||Kniga pro algoritmy||1996||273||346||239

Количество записей в файле не превышает 50 штук.

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

Версия урока для просмотра  offline
Исходные коды программ

    msm.ru

    Нравится ресурс?

    Помоги проекту!

    >
    C++ AMP — не комплируется пример из книги

    • Подписаться на тему
    • Сообщить другу
    • Скачать/распечатать тему

      


    Сообщ.
    #1

    ,
    22.03.17, 16:22

      Member

      **

      Рейтинг (т): нет

      Теперь пытаюсь поработать с C++ AMP. Windows 7, VS 2012. В программе из книги C++ AMP

      ExpandedWrap disabled

        #include <string>

        #include <iostream>

        #include <Windows.h>

        #include <memory.h>

        #include <stdio.h>

        #include <time.h>

        #include <amp.h>

        #include <ppl.h>

        using namespace std;

        using namespace concurrency;

        void AddArrays(int n, const double* const pA, const double* const pB, double* const pC)

        {

          clock_t start, finish, duration;

          start = clock();

          for(int i = 0; i < n; ++i)

          {

            pC[i] = pA[i] + pB[i];

          }

          finish = clock();

          duration = finish — start;

          cout << duration << » pC[i] = pA[i] + pB[i]» << » Время расчета без OpenMP» << endl;

        }

        void AddArraysAmp(int n, const int* const pA, const int* const pB, int* const pC)

        {

          clock_t start, finish, duration;

          start = clock();

          array_view<int, 1> a(n, pA);

          array_view<int, 1> b(n, pB);

          array_view<int, 1> c(n, pC);

          parallel_for_each(c.extent, [=](index<1> idx) restrict(amp)

          {

            c[idx] = a[idx] + b[idx];

          });

          finish = clock();

          duration = finish — start;

          cout << duration << » pC[i] = pA[i] + pB[i]» << » Время расчета C++ AMP» << endl;

        }

        int main()

        {

          setlocale(LC_ALL, «»);

          int n = 15000;

          double* pA = (double*) malloc(sizeof(double)*n);

          double* pB = (double*) malloc(sizeof(double)*n);

          double* pC = (double*) malloc(sizeof(double)*n);

          for(int i = 0; i < n; ++i)

          {

            pA[i] = (double) i;

            pB[i] = (double) i;

            pC[i] = (double) 0;

          }

          AddArrays(n, pA, pB, pC);

          AddArraysAmp(n, pA, pB, pC);

          free(pA);

          free(pB);

          free(pC);

          system(«npause»);

        }

      получаю ошибки:
      error C2228: выражение слева от «.data» должно представлять класс, структуру или объединение c:program filesmicrosoft visual studio 11.0vcincludeamp.h 2250 1 CpuGpu
      error C2228: выражение слева от «.data» должно представлять класс, структуру или объединение c:program filesmicrosoft visual studio 11.0vcincludeamp.h 2253 1 CpuGpu
      error C2338: container element type and array view element type must match c:program filesmicrosoft visual studio 11.0vcincludeamp.h 2253 1 CpuGpu
      error C2228: выражение слева от «.size» должно представлять класс, структуру или объединение c:program filesmicrosoft visual studio 11.0vcincludeamp.h 2255 1 CpuGpu
      Почему возникает эта ошибка в заголовочном файле Microsoft? Проект прикрепил

      Прикреплённый файлПрикреплённый файлCpuGpuAmp.zip (2,25 Кбайт, скачиваний: 320)

      Сообщение отредактировано: tuchin — 22.03.17, 16:23


      Олег М



      Сообщ.
      #2

      ,
      22.03.17, 16:38

        Скорее всего, где-то требуется std::vector, вместо указателя

        Добавлено 22.03.17, 16:39
        Либо какой-то из массивов apm

        Добавлено 22.03.17, 16:44

        Цитата Олег М @ 22.03.17, 16:38

        array_view<int, 1> a(n, pA);

        Здесь требуется обычный указатель, а ты передаёшь константный, она не может найти конструктор


        tuchin



        Сообщ.
        #3

        ,
        23.03.17, 04:56

          Member

          **

          Рейтинг (т): нет

          Попробовал исправить, убрал const, не удается скомпилировать код. Буду разбираться дальше.


          Олег М



          Сообщ.
          #4

          ,
          23.03.17, 05:11

            Цитата tuchin @ 23.03.17, 04:56

            Попробовал исправить, убрал const, не удается скомпилировать код. Буду разбираться дальше.

            Сделай вот так
            void AddArraysAmp(int n, const int *pA, const int *pB, const int *pC)


            tuchin



            Сообщ.
            #5

            ,
            23.03.17, 16:30

              Member

              **

              Рейтинг (т): нет

              Не помогло


              Олег М



              Сообщ.
              #6

              ,
              23.03.17, 16:45

                Цитата tuchin @ 23.03.17, 16:30

                Не помогло

                А как у тебя вообще передаются указатели на double в функцию, которая требует указатели на int?


                tuchin



                Сообщ.
                #7

                ,
                23.03.17, 17:25

                  Member

                  **

                  Рейтинг (т): нет

                  Пробовал и так:

                  ExpandedWrap disabled

                    void AddArraysAmp(int n, const double* const pA, const double* const pB, double* const pC)

                    {

                      clock_t start, finish, duration;

                      start = clock();

                      array_view<double, 1> a(n, pA);

                      array_view<double, 1> b(n, pB);

                      array_view<double, 1> c(n, pC);

                      parallel_for_each(c.extent, [=](index<1> idx) restrict(amp)

                      {

                        c[idx] = a[idx] + b[idx];

                      });

                      finish = clock();

                      duration = finish — start;

                      cout << duration << » pC[i] = pA[i] + pB[i]» << » Время расчета C++ AMP» << endl;

                    }

                    int main()

                    {

                      setlocale(LC_ALL, «»);

                      int n = 3;

                      double* pA = (double*) malloc(sizeof(double)*n);

                      double* pB = (double*) malloc(sizeof(double)*n);

                      double* pC = (double*) malloc(sizeof(double)*n);

                      for(int i = 0; i < n; ++i)

                      {

                        pA[i] = (double) i;

                        pB[i] = (double) i;

                        pC[i] = (double) 0;

                      }

                      AddArrays(n, pA, pB, pC);

                      free(pA);

                      free(pB);

                      free(pC);

                      system(«npause»);

                    }

                  Даже без включения AddArraysAmp в Main возникают те же ошибки. А функция без AMP void AddArrays(int n, const double* const pA, const double* const pB, double* const pC) работает нормально.

                  Добавлено 23.03.17, 17:29
                  Кроме того, проверил результаты в Windows 10 и VS2015, результаты те же. При этом определить, что в коде примера не так у меня не получается, ошибку дает только amp.h. А разобраться в коде заголовочного файла Microsoft для меня непосильная задача.


                  Олег М



                  Сообщ.
                  #8

                  ,
                  23.03.17, 17:30

                    Тебе нужен, насколько я понимаю, чтобы вызывался вот этот конструктор array_view (что значит restrict, я не знаю)

                    ExpandedWrap disabled

                      array_view(  

                         int _E0,  

                         const _Value_type * _Src  

                      )restrict(amp,cpu);

                    Т.е. const double *. А у тебя — const double* const.
                    Поэтому компилятор выбирает другой.
                    Сделай параметры const double *pA, ……..


                    tuchin



                    Сообщ.
                    #9

                    ,
                    23.03.17, 18:12

                      Member

                      **

                      Рейтинг (т): нет

                      При int и double в функции

                      ExpandedWrap disabled

                        void AddArraysAmp(int n, const double* pA, const double*  pB, double* pC)

                        {

                          clock_t start, finish, duration;

                          start = clock();

                          //array_view<double, 1> a(n, pA);

                          //array_view<double, 1> b(n, pB);

                          //array_view<double, 1> c(n, pC);

                          array_view<int, 1> a(n, pA);

                          array_view<int, 1> b(n, pB);

                          array_view<int, 1> c(n, pC);

                          parallel_for_each(c.extent, [=](index<1> idx) restrict(amp)

                          {

                            c[idx] = a[idx] + b[idx];

                          });

                          finish = clock();

                          duration = finish — start;

                          cout << duration << » pC[i] = pA[i] + pB[i]» << » Время расчета C++ AMP» << endl;

                        }

                      получил те же результаты


                      Олег М



                      Сообщ.
                      #10

                      ,
                      23.03.17, 19:01

                        Цитата tuchin @ 23.03.17, 18:12

                        //array_view<double, 1> a(n, pA);
                          //array_view<double, 1> b(n, pB);
                          //array_view<double, 1> c(n, pC);

                        Раскоментируй это, а то, что ниже убери


                        tuchin



                        Сообщ.
                        #11

                        ,
                        23.03.17, 19:24

                          Member

                          **

                          Рейтинг (т): нет

                          ExpandedWrap disabled

                            void AddArraysAmp(int n, const double* pA, const double*  pB, double* pC)

                            {

                              clock_t start, finish, duration;

                              start = clock();

                              array_view<double, 1> a(n, pA);

                              array_view<double, 1> b(n, pB);

                              array_view<double, 1> c(n, pC);

                              //array_view<int, 1> a(n, pA);

                              //array_view<int, 1> b(n, pB);

                              //array_view<int, 1> c(n, pC);

                              parallel_for_each(c.extent, [=](index<1> idx) restrict(amp)

                              {

                                c[idx] = a[idx] + b[idx];

                              });

                              finish = clock();

                              duration = finish — start;

                              cout << duration << » pC[i] = pA[i] + pB[i]» << » Время расчета C++ AMP» << endl;

                            }

                          не помогло


                          Олег М



                          Сообщ.
                          #12

                          ,
                          23.03.17, 19:44

                            Цитата tuchin @ 23.03.17, 19:24

                            array_view<double, 1> a(n, pA)

                            А попробуй сделать array_view<const double, 1>, для a и b


                            tuchin



                            Сообщ.
                            #13

                            ,
                            24.03.17, 05:05

                              Member

                              **

                              Рейтинг (т): нет

                              Ошибка (активно) выражение должно быть допустимым для изменения левосторонним значением CpuGpuVS2015 e:MyProgrammingCpuGpuVS2015CpuGpu.cpp 40
                              Ошибка C3892 Concurrency::array_view<const double,1>::operator []: невозможно присваивать значения переменной, которая объявлена как константа CpuGpuVS2015 e:myprogrammingcpugpuvs2015cpugpu.cpp 40
                              Другие ошибки исчезли


                              Олег М



                              Сообщ.
                              #14

                              ,
                              24.03.17, 05:11

                                Покажи код


                                tuchin



                                Сообщ.
                                #15

                                ,
                                24.03.17, 17:16

                                  Member

                                  **

                                  Рейтинг (т): нет

                                  Я забыл убрать const в array_view для c(n, pC). После исправления в программе

                                  ExpandedWrap disabled

                                    #include <string>

                                    #include <iostream>

                                    #include <Windows.h>

                                    #include <memory.h>

                                    #include <stdio.h>

                                    #include <time.h>

                                    #include <amp.h>

                                    using namespace std;

                                    using namespace concurrency;

                                    void AddArrays(int n, const double* const pA, const double* const pB, double* const pC)

                                    {

                                      clock_t start, finish, duration;

                                      start = clock();

                                      for(int i = 0; i < n; ++i)

                                      {

                                        pC[i] = pA[i] + pB[i];

                                      }

                                      finish = clock();

                                      duration = finish — start;

                                      cout << duration << » — время расчета без OpenMP.» << »  Результат: » << pC[2] << endl;

                                    }

                                    void AddArraysAmp(int n, const double* pA, const double*  pB, double* pC)

                                    {

                                      clock_t start, finish, duration;

                                      start = clock();

                                      array_view<const double, 1> a(n, pA);

                                      array_view<const double, 1> b(n, pB);

                                      array_view<double, 1> c(n, pC);

                                      parallel_for_each(c.extent, [=](index<1> idx) restrict(amp)

                                      {

                                        c[idx] = a[idx] + b[idx];

                                      });

                                      finish = clock();

                                      duration = finish — start;

                                      cout << duration << » pC[i] = pA[i] + pB[i]» << » Время расчета C++ AMP» << endl;

                                    }

                                    int main()

                                    {

                                      setlocale(LC_ALL, «»);

                                      int n = 16000;

                                      double* pA = (double*) malloc(sizeof(double)*n);

                                      double* pB = (double*) malloc(sizeof(double)*n);

                                      double* pC = (double*) malloc(sizeof(double)*n);

                                      for(int i = 0; i < n; ++i)

                                      {

                                        pA[i] = (double) i;

                                        pB[i] = (double) i;

                                        pC[i] = (double) 0;

                                      }

                                      AddArrays(n, pA, pB, pC);

                                      AddArraysAmp(n, pA, pB, pC);

                                      free(pA);

                                      free(pB);

                                      free(pC);

                                      system(«npause»);

                                    }

                                  В результате получаю
                                  Возникло необработанное исключение по адресу 0x751524F2 в CpuGpuVS2015.exe: исключение Microsoft C++: Concurrency::runtime_exception по адресу памяти 0x004FE81C.

                                  0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)

                                  0 пользователей:

                                  • Предыдущая тема
                                  • C/C++: Системное программирование и WinAPI
                                  • Следующая тема

                                  Рейтинг@Mail.ru

                                  [ Script execution time: 0,0500 ]   [ 17 queries used ]   [ Generated: 12.02.23, 22:21 GMT ]  

                                  Глава 3. Указатели и ссылки

                                  3.6.Константные указатели и ссылки

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

                                  const тип *имя – обозначая то, что константой является объект, лежащий по адресу, хранимому в указателе имя (при этом адрес можно изменить);

                                  тип* const имя – фиксируя тот факт, что константой является собственно адрес какого-то объекта (объект при этом может быть изменен).

                                  Листинг 48

                                  int n, m =

                                  10;

                                  const int*

                                  p = &m; // p имеет тип const int* — указатель на константу

                                  p = &n;

                                  //

                                  допустимо изменение указателя (адреса)

                                  m = 13;

                                  //

                                  допустимо изменение переменной на которую указывает

                                  const int*

                                  *p = 12; // ошибка: попытка изменения содержимого указателя на

                                  константу

                                  Префиксное объявление модификатора const делает константным объект на который он указывает, а не сам указатель. На примере выше (Листинг 48) можно видеть этот вариант. Не вызывает ошибки у компилятора тот факт, что указатель на константу p проинициирован адресом переменной m (а не константы).

                                  Описание переменной const int *p = &m можно интерпретировать так: «переменная p имеет тип const int* – она является указателем на константу» или так: «содержимое *p указателя p имеет тип const int – оно является константой».

                                  Понимая обозначение таким образом, можно видеть, почему не вызовет ошибки присваивание указателю p другого адреса (сам указатель не является константой). Корректным будет также явное изменение переменной m через её имя, (ведь никаких ограничений на переменную m не наложено). Но попытка косвенного (через указатель) изменения данных, хранимых в ячейке, на которую указывает p, вызовет ошибку, так как это – указатель на константу. Константное выражение недопустимо для изменения левосторонним значением.

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

                                  Листинг 49

                                  int n, m = 10;

                                  int const p = &m; // константный указатель

                                  p = &n; // ошибка: попытка изменения указателя (константного адреса)

                                  *p = 12; // допустимо изменение значения на которое указывает int*const

                                  Приведенное здесь (Листинг 49) описание переменной int* const p = &m можно интерпретировать так: «переменная p имеет тип int* const – она является константным указателем» или так: «константа const p имеет тип int* – она является указателем».

                                  Теперь понятно, что ошибкой будет присваивание указателю p другого адреса (поскольку сам указатель является константой). Константное выражение недопустимо для изменения левосторонним значением.

                                  Корректным будет явное (по имени) или косвенное (через указатель) изменение переменной m или содержимого *p, лежащего по этому константному адресу p.

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

                                  int m = 10;

                                  const int const X = &m; // константный указатель на константу

                                  51

                                  Информационные технологии

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

                                  Константные ссылки

                                  Аналогично указателям, ссылочные переменные также могут ссылаться на константы, а могут быть константами сами, а ссылаться на изменяемые переменные:

                                  const тип& имя – константой является объект на который делается ссылка; тип& const имя – константой является ссылка, сам объект при этом может

                                  изменяться.

                                  Листинг 50

                                  double a = 12.12; const double& d1 = a; double& const d2 = a;

                                  d1 = 13; // ошибка: значение по ссылке q — константа, оно не меняется d2 = 13; // ок: константная ссылка на изменяемую переменную

                                  Во втором случае не понятно, в чем смысл создания именно константной ссылки, ведь ссылка (в отличие от указателя) и так не имеет смысла сама по себе, её саму (а не тот объект, на который она ссылается) и так не изменить. В случае описания переменной d2 модификатор const не несет никаких дополнительных ограничений и не накладывает никаких дополнительных проверок.

                                  Листинг 51

                                  double x;

                                  const double& q = x; // ссылка на константу

                                  double y = q + 5; // использование константы в правой части выражения x = 56; // double х — не константа

                                  q = 12; // ошибка: значение по ссылке q — константа, оно не меняется

                                  Рассмотрим нюансы (Листинг 51). Здесь создается ссылка на константу, её можно использовать в правой части выражения, но присвоить ей новое значение нельзя, ведь она ссылается на константу. Для присваивания выражение должно быть допустимым для изменения левосторонним значением, а это не так. При этом, сама переменная х может быть изменена.

                                  Листинг 52

                                  const double a = 5;

                                  double& r = a; // ошибка: попытка сослаться на константу изменяемой ссылкой const double& r = a; // константная ссылка инициирована константой

                                  double const& r = a; // константная ссылка инициирована константой r = 21; // ошибка: попытка изменить константу

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

                                  Обычные ссылки в англоязычной литературе по программированию принято называть modifiable lvalues, а константные ссылки non-modifiable lvalues (изменяемые и неизменяемые левосторонние выражения).

                                  52

                                  Глава 3. Указатели и ссылки

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

                                  Контрольные вопросы:

                                  1.Что такое указатель и для чего он используется?

                                  2.Как узнать значение переменной, если известен только ее адрес?

                                  3.Какие действия выполняет оператор new?

                                  4.Имеет ли статическая переменная адрес?

                                  5.В чем разница между динамической переменной и указателем на нее?

                                  6.Какое значение возвращают функции malloc и calloc при выделении памяти?

                                  7.Как получить адрес переменной?

                                  8.Какое имя можно задать динамической переменной?

                                  9.Что такое адрес переменной?

                                  10.Какие пять действий происходят при выделении памяти функциями malloc, calloc?

                                  11.Какой размер занимает переменная-указатель?

                                  12.Что такое динамическая переменная, чем она отличается от статической?

                                  13.В каком случае нужно пользоваться оператором delete, а в каком – функцией free( ) для освобождения памяти?

                                  53

                                  Информационные технологии

                                  4.Подпрограммы

                                  Подпрограмма (процедура, функция) – это именованная часть (блок) программы, к которой можно обращаться из других частей программы столько раз, сколько потребуется. Для того чтобы вызывать (запускать) подпрограмму в разных частях головной программы ее необходимо описать и задать. [1 14]

                                  Описать функцию (описать заголовок, прототип функции) – означает определить ее имя, тип_возвращаемого_значения и список_формальных_параметров, записываемых через запятую как на рисунке (Рис. 33).

                                  Листинг 53

                                  // прототип (описание) функции, её заголовок тип_возвращаемого_значения имя(список_формальных_параметров);

                                  //——————————————————————————

                                  // головная программа, вызывающая подпрограмму имя() int _tmain(int argc, char* argv[])

                                  {

                                  x = имя(список_фактических_параметров); // вызов функции

                                  }

                                  Задать подпрограмму – значит, помимо описания заголовка, задать (определить, прописать) и все её команды, составляющие тело подпрограммы (Рис. 33).

                                  тип возвращаемого значения

                                  имя подпрограммы

                                  список формальных параметров с указанием типа

                                  float add(float x, float y)

                                  заголовок подпрограммы

                                  {

                                  точка начала работы подпрограммы

                                  float z;

                                  z = x+y;

                                  тело подпрограммы

                                  return z;

                                  }

                                  внутренняя (локальная) переменная команда выхода из подпрограммы, возвращающая результат

                                  Рис. 33. Структура задания функции (подпрограммы) в языке С++

                                  Если подпрограмма не должна возвращать никакого значения, то тип_возвращаемого_значения нужно задать как void, такая подпрограмма называется процедурой. Если подпрограмма возвращает какое-то значение, то в теле функции должен присутствовать оператор return, возвращающий значение соответствующего типа. Подпрограммы, возвращающие значение, называются функциями.

                                  Листинг 54

                                  //——————————————————————————

                                  // задание (реализация) функции тип_возвращаемого_значения имя(список_формальных_параметров)

                                  {

                                  // тело функции

                                  return возвращаемое_значение;

                                  }

                                  54

                                  точка начала работы программы main()

                                  Глава 4. Подпрограммы

                                  Тип_возвращаемого_значения определяет, какого типа должен быть результат вычислений, возвращаемый в программу, вызвавшую нашу функцию. Для возврата из функции используется оператор return и стоящее после него выражение (оно должно быть требуемого типа). Первый встретившийся в любом месте функции оператор return немедленно прекращает выполнение подпрограммы, возвращая в вышестоящую подпрограмму значение расположенного после него выражения (Рис. 34).

                                  #include <iostream> using namespace std;

                                  {

                                  float a = 12.5; float b = 13.489;

                                  cout << add(a, b) << «n»;

                                  }

                                  float add(float x, float y)

                                  {

                                  return (x + y);

                                  }

                                  Рис. 34. Логика передачи управления в подпрограмму и возврата из неё.

                                  После того, как функция описана и задана, она может быть вызвана (запущена) из тела главной функции main() или других функций программы. Вызывающая программа по отношению к вызываемой подпрограмме, называется головной. Та точка головной программы, в которой была вызвана функция, называется точка вызова (или точка возврата) – выполнение основной программы в этой точке приостанавливается (прерывается) и выполняется тело_функции, по завершению работы функции возвращаемое_значение передается в точку возврата, после чего выполнение основной программы будет продолжено. В нашем примере (Рис. 34) точка возврата находится в строке после строки символов cout <<.

                                  #include «stdafx.h» #include <conio.h>

                                  int _tmain(int argc, char* argv[]) {

                                  подключаемые библиотеки

                                  заголовок программы main()

                                  float a= 12.5; float b= 13.489;

                                  cout << add(a,b);

                                  return 0;

                                  переменные программы main()

                                  вызов подпрограммы add(float, float)

                                  список фактических значений, подставленных

                                  вместо формальных параметров

                                  точка возврата из подпрограммы add(float, float)

                                  }команда выхода из программы main(), возвращающая 0

                                  Рис. 35. Структура вызова (использования) функции в главной программе

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

                                  Рассмотрим пример (Листинг 55) в котором описывается, задается и вызывается подпрограмма int min(int, int) для вычисления минимума из двух чисел. Во второй

                                  55

                                  Информационные технологии

                                  строке листинга описывается прототип (заголовок) функции. Реализация подпрограммы размещена в 18-23 строках, здесь задается тело функции. В теле программы main() эта функция трижды вызывается для различных фактических параметров: в строке 11 вызывается подпрограмма min() для сравнения чисел 12 и 54, в строке 12 она вызывается с параметрами 12 и 5, и в строке 13 функция min() в качестве фактических значений принимает значения 54 и -7.

                                  Листинг 55

                                  #include <iostream> //—————————————————————————

                                  int min(int, int); // прототип подпрограммы

                                  //—————————————————————————

                                  void main()

                                  {

                                  system(«chcp 1251»); system(«cls»);

                                  int X, Y; X = 12;

                                  Y = 54;

                                  std::cout << «X= » << X << » Y= » << Y << «n»; std::cout << «min(X,Y)= » << min(X, Y) << «n»; std::cout << «min(X,5)= » << min(X, 5) << «n»; int Z = min(Y, -7);

                                  std::cout << «min(Y,-7)= » << Z << «n»; std::cout << «nn»;

                                  system(«PAUSE»);

                                  }

                                  //—————————————————————————

                                  int min(int A, int B) // реализация подпрограммы

                                  {

                                  if (A <= B) return A;

                                  return B;

                                  }

                                  //—————————————————————————

                                  Обратите внимание на то, в каком месте программы main() вызывается функция min(). В первых двух случаях возвращаемое подпрограммой значение передается оператору std::cout << в качестве аргумента, а в третьем варианте результат вычислений функции min() присваивается переменной Z. Разумеется, в этом случае тип переменной Z и тип возвращаемого функцией int min(int, int) значения должны совпадать.

                                  Результат выполнения программы (Листинг 55) приведен ниже (Рис. 36).

                                  Рис. 36. Результат вызова одной функции с различными параметрами

                                  Заголовочные файлы подключаемых библиотек – файлы с расширением *.h: «stdafx.h», <iostream.h>, <conio.h> и др. содержат прототипы используемых нами подпрограмм system(), std::cout <<, std::cin >>, srand(), time(), rand(), strcpy(), printf(), scanf(), calloc(), malloc() и др. Вызвать описание всех доступных подпрограмм можно по комбинации клавиш F12, <ALT>-<F12> и <CTRL>-<F12> или вызвав контекстную помощь. Реализация этих подпрограмм размещена в файлах *.lib или *.dll (библиотеках) в откомпилированном виде.

                                  56

                                  Глава 4. Подпрограммы

                                  Рассмотрим программу, печатающую степени числа 2:

                                  Листинг 56

                                  float pow(float, int); // прототип функции, которая будет прописана ниже void _tmain(int argc, char* argv[]) // главная функция main()

                                  {

                                  for (int i = 0; i < 10; i++)

                                  cout << pow(2, i) << «n»; // вызов функции pow(float, int)

                                  }

                                  Первая строка листинга, прототип функции указывает на то, что float pow(float, int) – это функция, получающая параметры типа float и int и возвращающая float. Прототип функции используется для того, чтобы сделать определенными обращения к функции в других местах.

                                  При вызове функции тип каждого параметра сопоставляется с ожидаемым типом. Это гарантирует надлежащую проверку и преобразование типов. Например, обращение pow(12.3,»abcd») вызовет недовольство компилятора, поскольку «abcd» является строкой, а не int. При вызове pow(2,i) компилятор производит неявное преобразование 2 к типу float, и запустит на выполнение функцию pow(2.0, i).

                                  Функция pow() может быть определена рекурсивно:

                                  Листинг 57

                                  float pow(float x, int n) // задание функции pow()

                                  {

                                  if (n < 0) // если отрицательный показатель для pow() return 1/pow(x, n);

                                  switch (n)

                                  {

                                  case 0: return 1; // выход из рекурсии case 1: return x;

                                  default: return x * pow(x, n — 1); // рекурсия

                                  }

                                  }

                                  4.1.Передача параметров в тело функции

                                  Вязыке С++ существует три способа передачи параметров в тело функции: «по значению», «по ссылке» и «по указателю».

                                  void F1(int p, int q); // передача параметров по значению void F2(int& p, int& q) // передача параметров по ссылке void F3(int* p, int* q) // передача параметров по указателю

                                  Передача параметров по значению

                                  Для того чтобы понять их специфику, рассмотрим процесс передачи данных в функцию более подробно на следующем примере (Рис. 37). Вызывающая функция main() имеет 3 переменных целого типа: x, y и z. Под каждую из них выделено по одной ячейке памяти размером sizeof(int). В точке вызова функции F() приостанавливается выполнение функции main(), в памяти компьютера выделяется еще 3 ячейки под локальные (видимые и используемые только внутри подпрограммы) переменные p, q и r.

                                  Область видимости этих переменных p, q и r (та зона программного кода, где они могут быть использованы) ограничена телом функции F(). Такие переменные называются локальными. В ячейки p и q копируются значения глобальных переменных x и y соответственно, после чего производится вычисление переменной r и оператор return возвращает значение r в точку вызова. После этого возобновляется выполнение функции main() – переменной z присваивается возвращаемое значение.

                                  57

                                  Информационные технологии

                                  // вызываемая функция

                                  int F(int p, int q)

                                  {

                                  область видимости

                                  int r = p + q;

                                  переменных

                                  return r;

                                  функции F()

                                  }

                                  // вызывающая функция

                                  int _tmain(int argc, char* argv[])

                                  {

                                  int x = 15, y = 4;

                                  int z = F(x, y);

                                  точка возврата

                                  из функции F()

                                  }

                                  список локальных переменных функции F()

                                  19

                                  return

                                  15 4

                                  список переменных функции main()

                                  Рис. 37. Передача параметров по значению

                                  В рассмотренном выше примере данные в подпрограмму F() передаются «по значению», это значит, что в программе F() создаются локальные переменные p и q и в них копируются значения фактических параметров – переменных x и y. Локальные переменные p и q в теле основной программы не видны, и не могут использоваться. И, естественно, если в теле функции F() переменные p и q меняют свои значения, это никак не скажется на переменных x и y они не изменятся.

                                  Рассмотрим пример, приведенный ниже (Листинг 58). В нем приводится подпрограмма int sum2ab(int a, int b), в которую передаются «по значению» два целых числа – a и b, в теле подпрограммы значения их удваиваются, затем производится вывод на экран значений этих переменных и их адресов. После чего они суммируются,

                                  ив вызывающую программу оператором return возвращается сумма этих значений.

                                  Восновной программе создаются две глобальные переменные с такими же

                                  именами – a и b, задаются случайными числами, выводятся на экран вместе со своими адресами, потом производится вызов подпрограммы sum2ab(a, b) и вновь значения a и b выводятся на экран. Результаты работы программы (Листинг 58) приведены ниже на рисунке (Рис. 38).

                                  Листинг 58

                                  #include «stdafx.h» #include <iostream> #include <time.h> using namespace std;

                                  //——————————————————————————

                                  int sum2ab(int a, int b) // Передача параметров в функцию «по значению»

                                  {

                                  a = 2 * a; // удваиваем значения переменных b = 2 * b;

                                  printf(«nвывод из подпрограммы sum2ab:n»);

                                  printf(«a= %2d (address a= %p) b= %2d (address b= %p)n», a, &a, b, &b); return a + b; // возвращаем 2a+2b

                                  }

                                  //——————————————————————————

                                  void _tmain(int argc, char* argv[])

                                  {

                                  system(«chcp 1251»); // подпрограмма русского шрифта srand(time(NULL)); // инициализация генератора случайных чисел int a = rand() % 10;

                                  int b = rand() % 10;

                                  printf(«nвывод из главной подпрограммы main():n»);

                                  printf(«a= %2d (address a= %p) b= %2d (address b= %p)n», a, &a, b, &b); printf(«n2a+2b= %dn», sum2ab(a, b));

                                  // значения глобальных переменных a и b не изменились printf(«nповторный вывод из главной подпрограммы main():n»);

                                  58

                                  Глава 4. Подпрограммы

                                  printf(«a= %2d (address a= %p) b= %2d (address b= %p)n», a, &a, b, &b); system(«pause»); // ожидание нажатия клавиши

                                  }

                                  Рис. 38. Результаты выполнения программы

                                  Проанализируем результаты. Можно видеть, что сначала выводятся данные из основной программы, глобальные переменные a и b принимают значения 3 и 1 соответственно. В круглых скобках выведены адреса 0032FB80 и 0032FB74 глобальных переменных a и b.

                                  Затем производится вывод локальных переменных a и b из подпрограммы sum2ab(). Можно видеть, что их адреса 0032FA9C и 0032FAA0 не совпадают с адресами глобальных переменных a и b, следовательно, это другие ячейки памяти. Значения этих переменных удвоены. После этого выводится сумма удвоенных значений – подпрограмма sum2ab() корректно вычисляет и передает в главную программу результат сложения.

                                  Однако заметьте, что повторный вывод глобальных переменных a и b (а это именно те исходные переменные – можно видеть по адресам) показывает, что их значения не изменились!

                                  Можно сделать вывод о том, что изменение локальных переменных внутри подпрограммы не влияет на глобальные переменные в головной программе. Потому что передаваемые «по значению» переменные в подпрограмме – это копии глобальных переменных. Изменение «копий» не изменяет «оригиналы».

                                  Передача параметров по ссылке

                                  Передачу параметров «по ссылке» удобно рассмотреть на примере функции S() меняющей местами свои аргументы p и q (Рис. 39). Если бы p и q передавались в функцию S() по значению, как в предыдущем примере, то обмен местами значений локальных переменных p и q в теле функции S() никак не повлиял бы на значения глобальных переменных x и у.

                                  При передаче данных в функцию S() по ссылке ячейки памяти для ссылочных переменных p и q не создаются, а для работы используются ячейки памяти головной программы – чьи имена передаются в функцию S(). Символ «&» перед переменным в списке формальных параметров в данном случае обозначает обращение «по ссылке».

                                  В примере (Рис. 39) переменные p и q в теле функции S() не существуют как самостоятельные отдельные ячейки памяти, а служат лишь для обозначения ячеек x и y. В момент вызова подпрограммы, заданные в списке фактических параметров ссылки &p и &q инициируются какими-то глобальными переменными (в нашем примере – переменными x и y). Дальше в теле подпрограммы со ссылочными переменными p и q производятся операции так же, как и над обычными ссылками (см. п.3.5).

                                  59

                                  Информационные технологии

                                  // вызываемая подпрограмма void S(int& p, int& q)

                                  { список формальных

                                  int t = p;

                                  параметров

                                  p = q;

                                  функции S()

                                  q = t;

                                  }

                                  // вызывающая программа

                                  int main()

                                  {

                                  список фактических

                                  int x = 15, y = 4;

                                  параметров

                                  S(x, y);

                                  функции S()

                                  }

                                  t — локальная переменная функции S(), &p и &q — ссылки

                                  int t

                                  ссылка на x ссылка на y

                                  переменные функции main( )

                                  Рис. 39. Передача параметров по ссылке

                                  Изменим пример, приведенный выше (Листинг 58), так, чтобы параметры передавались в функцию по ссылке. (Листинг 59). Для этого в заголовке функции int sum2ab(int& a, int& b), изменим строку описания формальных параметров, добавив символы «&». Больше ничего изменять не станем – как и в предыдущем примере в теле подпрограммы значения a и b удваиваются, затем производится вывод на экран значений этих переменных с адресами, после чего они суммируются, и в вызывающую программу возвращается сумма этих значений.

                                  Листинг 59

                                  int sum2ab(int& a, int& b) // Передача параметров в функцию «по ссылке»

                                  {

                                  a = 2 * a; // удваиваем значения переменных b = 2 * b;

                                  printf(«nвывод из подпрограммы sum2ab:n»);

                                  printf(«a= %2d (address a= %p) b= %2d (address b= %p)n», a, &a, b, &b); return a + b; // возвращаем 2a+2b

                                  }

                                  Основную программу также оставим без изменений. Посмотрим, как изменился результат (Рис. 40). Во-первых, можно видеть, что адреса переменных a и b, выводимые из основной программы и из подпрограммы sum2ab() одни и те же. Это можно понять по адресам этих переменных – 001EFA8C и 001EFA80. В головной программе и в вызываемой подпрограммы мы работаем с одними и теми же ячейками памяти. Естественно, что значения переменных как удвоились в подпрограмме, так и остались удвоенными при повторном выводе из программы main().

                                  Рис. 40. Результаты выполнения программы

                                  Передача параметров по указателю

                                  Передача параметров по указателю (Рис. 41) практически ничем не отличается от передачи данных по значению – разница лишь в том, что аргументом функции выступают не статические переменные S(int p, int q), а переменные-указатели S(int* p, int* q). Напомню, что указатели хранят в себе адреса.

                                  60

                                  Понравилась статья? Поделить с друзьями:
                                • Ошибка ворлд оф танк точка входа не найдена
                                • Ошибка выполнения запроса попробуйте позже
                                • Ошибка загрузки midas dll
                                • Ошибка внутреннего диска s m a r t
                                • Ошибка загрузки microg apk