Структуры. Часть 4. Структуры и функции. Передача структуры в функцию в среде CLR. Возврат структуры из функции

Структуры. Часть 4. Структуры и функции. Передача структуры в функцию в среде CLR. Возврат структуры из функции


Содержание



1. Какие существуют способы передачи структуры в функцию?

Существует 2 способа передачи native-структуры в функцию в качестве параметра:

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

2. Какие есть способы возврата структуры из функции?

Так же, как и при передаче структуры в функцию (см. п.1), существуют 2 способа возврата:

  • возврат структуры по значению;
  • возврат указателя на структуру.

Преимущества и недостатки каждого способа такие же, как описано в п. 1.

3. Пример передачи native-структуры в функцию по значению

Пусть в модуле «MyStruct.h» даны объявления native-структуры:

// native-структура, которая описывает точку на координатной плоскости
struct MyPoint
{
    int x;
    int y;
};

Пусть в некотором классе объявляется функция EqualPoints(), которая сравнивает две точки на координатной плоскости. Функция возвращает true, если точки эквивалентны (равны между собой). В другом случае функция возвращает false.

// функция, которая сравнивает на равенство две точки
public: bool EqualPoints(MyPoint p1, MyPoint p2)
{
    bool f = false;
    if ((p1.x == p2.x) && (p1.y == p2.y))
        f = true;
    return f;
}

Тогда использование функции EqualPoints() может быть следующим:

// подключение модуля "MyStruct.h"
#include "MyStruct.h"

...

// передача native-структуры в функцию по значению
bool f_equal;
MyPoint pt1, pt2; // p1, p2 – переменные типа "структура"

// заполнение значениями
pt1.x = 23;
pt1.y = 35;
pt2.x = 23;
pt2.y = 35;

f_equal = this->EqualPoints(pt1, pt2); // f_equal = true
pt1.x = 100;
f_equal = EqualPoints(pt1, pt2);

4. Пример передачи native-структуры в функцию по указателю

Пусть задана native-структура:

// native-структура
struct MyPoint
{
    int x;
    int y;
};

Ниже реализована функция, которая сравнивает на равенство значения структурных переменных. Функция получает указатель на структуру.

// передача указателя на структуру MyPoint
bool EqualPointsP(MyPoint * p1, MyPoint * p2)
{
    bool f = false;
    if ((p1->x == p2->x) && (p1->y == p2->y))
        f = true;
    return f;
}

Программный код, демонстрирующий использование функции EqualPoints().

// Передача структуры по указателю
MyPoint p1;
MyPoint p2;

bool f_equal;
p1.x = 28;
p1.y = 35;
p2.x = 28;
p2.y = 35;

f_equal = EqualPointsP(&p1, &p2); // f_equal = true
p2.y = 100;
f_equal = EqualPointsP(&p1, &p2); // f_equal = false

5. Как передать в функцию managed-структуру, которая объявлена с квалификатором ref? Пример

Пусть задана следующая ref-структура.

// ref-структура
ref struct MyPointRef
{
    int x;
    int y;
};

Структуры с квалификатором ref есть структурами ссылочного типа. Для таких структур память должна выделяться с помощью утилиты gcnew. Поэтому, в функцию можно передать только ссылку на такие структуры.

Пусть дана функция LengthRef(), которая определяет длину линии, которая соединяет 2 точки. Функция получает две ref-структуры в качестве параметров.

// Функция, определяющая длину между двумя точками
float LengthRef(MyPointRef ^p1, MyPointRef ^p2)
{
    float l;
    l = Math::Sqrt((p1->x-p2->x)*(p1->x-p2->x) + (p1->y-p2->y)*(p1->y-p2->y));
    return l;
}

Использование функции LengthRef() в некотором программном коде.

// передача ref-структуры в функцию
MyPointRef ^ pt1;
MyPointRef ^ pt2;
float len;

// выделение памяти для ref-структур
pt1 = gcnew MyPointRef;
pt2 = gcnew MyPointRef;

// заполнение ref-структур значениями
pt1->x = 35;
pt1->y = 35;
pt2->x = 40;
pt2->y = 40;

len = LengthRef(pt1, pt2); // len = 7.071068

6. Как передать в функцию managed-структуру, которая объявлена с квалификатором value? Пример

Managed-структуру, объявленную с квалификатором value можно передать в функцию одним из двух способов:

  • по значению;
  • по указателю.

Ниже приведены оба способа для value-структуры MyPointValue, которая имеет следующее объявление.

// value-структура
value struct MyPointValue
{
    int x;
    int y;
};

Способ 1.

Пусть задана функция LengthValue(), определяющая расстояние между двумя точками. Функция получает входным параметром две структуры типа MyPointValue. Структуры передаются как параметр-значение.

// определение расстояния между двумя точками
float LengthValue(MyPointValue p1, MyPointValue p2)
{
    float len;
    len = (float)Math::Sqrt((p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y));
    return len;
}

Пример использования функции LengthValue() в другом программном коде.

// передача value-структуры по значению
MyPointValue pt1;
MyPointValue pt2;
float len;

pt1.x = 35;
pt1.y = 50;
pt2.x = 40;
pt2.y = 55;
len = LengthValue(pt1, pt2); // len = 7.071068

Способ 2. Передача структуры по указателю.

Пусть задана функция LengthValue(), получающая два указателя на структуры типа MyPointValue.

// функция получает указатель на value-структуру
float LengthValueP(MyPointValue *p1, MyPointValue *p2)
{
    float len;
    len = Math::Sqrt((p1->x-p2->x)*(p1->x-p2->x) + (p1->y-p2->y)*(p1->y-p2->y));
    return len;
}

Использование функции LengthValue() в программе.

// передача value-структуры по указателю
MyPointValue pt1;
MyPointValue pt2;
float len;

pt1.x = 0;
pt1.y = 0;
pt2.x = 10;
pt2.y = 10;

len = LengthValueP(&pt1, &pt2); // len = 14.14214

7. Как реализовать возврат экземпляра native-структуры из функции? Пример

Возвратить native-структуру можно:

  • по значению;
  • по адресу.

Пусть задана структура MyPoint

// native-структура
struct MyPoint
{
    int x;
    int y;
};

Пусть в программе реализована функция GetCenterLine(), которая вычисляет координаты середины отрезка, соединяющего 2 точки. Функция получает 2 параметра – координаты концов отрезка.

// функция, возвращающая native-структуру
// функция вычисляет координаты середины отрезка
MyPoint GetCenterLine(MyPoint p1, MyPoint p2)
{
    MyPoint res;
    res.x = (p1.x + p2.x)/2;
    res.y = (p1.y + p2.y)/2;
    return res;
}

Демонстрация возврата native-структуры по значению.

MyPoint pt1;
MyPoint pt2;
MyPoint res; // результат, память уже выделена

pt1.x = 30;
pt1.y = 20;
pt2.x = 36;
pt2.y = 40;

res = GetCenterLine(pt1, pt2); // res - координаты середины отрезка

8. Пример возврата из функции указателя на native-структуру

Пусть задана структура MyPoint

// native-структура
struct MyPoint
{
    int x;
    int y;
};

Пусть реализована функция GetCenterLine(), которая получает 2 точки и возвращает указатель на структуру. В теле функции реализовано выделение памяти для структуры оператором new.

MyPoint * GetCenterLine(MyPoint p1, MyPoint p2)
{
    MyPoint * res; // указатель на структуру MyPoint

    // выделение памяти для структуры
    res = new MyPoint;

    // вычисление середины отрезка - заполнение значениями
    resP->x = (p1.x + p2.x)/2;
    resP->y = (p1.y + p2.y)/2;

    return res;
}

Демонстрация работы с функцией GetCenterLine().

// возвращение native-структуры по указателю
MyPoint * resPt; // указатель на структуру – память для структуры еще не выделена
MyPoint pt1, pt2; // экземпляры структуры

pt1.x = 28;
pt1.y = 40;
pt2.x = 38;
pt2.y = 50;

// вызов функции GetCenterLine()
// для указателя resPt память выделяется внутри функции
resPt = GetCenterLine(pt1, pt2);

// проверка
int d;
d = resPt->x; // d = 33
d = resPt->y; // d = 45

9. Как возвратить экземпляр value-структуры из функции?

Если в среде CLR описана структура с квалификатором value, то возврат экземпляра такой структуры из функции ничем не отличается от возвращения native-структуры (см. п. 7.).

10. Пример возврата указателя (^) на value-структуру

Если структура объявлена с квалификатором value, то функция может возвращать указатель на эту структуру. Допускается использование двух видов указателей:

  • классического указателя, который обозначается символом ‘*’. Возврат такого указателя из функции не отличается от возврата указателя на native-структуру (см. п. 8);
  • указателя, предназначенного для работы с объектами среды CLR. Такой указатель обозначается символом ^. Память для такого указателя выделяется утилитой gcnew. Ниже приведен пример возврата из функции такого указателя на value-структуру.

Пусть объявлена структура с квалификатором value

// value-структура
value struct MyPointValue
{
    int x;
    int y;
};

Пусть нужно реализовать функцию GetCenterLineVP(), которая получает 2 точки и возвращает указатель на результирующую value-структуру. Результирующая структура содержит координаты центра отрезка, который соединяет точки. Функция имеет следующий вид:

// функция возвращает указатель на value-структуру 
MyPointValue ^ GetCenterLineVP(MyPointValue p1, MyPointValue p2) 
{ 
    MyPointValue ^ pv; // указатель на структуру MyPointValue
    pv = gcnew MyPointValue;
    pv->x = (p1.x + p2.x)/2; 
    pv->y = (p1.y + p2.y)/2;
    return pv;
}

Демонстрация вызова функции из другого программного кода:

// создание экземпляров структур с инициализацией
MyPointValue pt1 = { 20, 30 };
MyPointValue pt2 = { 40, 60 };

// указатель на структуру MyPointValue
MyPointValue ^ resPt;

// вызов функции GetCenterLineVP()
// внутри функции выделяется память для структуры,
// на которую указывает resPt
resPt = GetCenterLineVP(pt1, pt2);

// проверка
int d;
d = resPt->x; // d = 30
d = resPt->y; // d = 45

11. Пример возврата из функции структуры, которая объявлена с квалификатором ref

Для структуры, которая объявлена с квалификатором ref есть определенные особенности использования. Такая структура есть структурой ссылочного типа. Поэтому функция должна возвращать ссылку на такую структуру (указатель  на структуру). Возвратить экземпляр ref-структуры (по значению) из функции не удастся (см. п. 5).

Пусть объявлена структура

// ref-структура
ref struct MyPointRef
{
    int x;
    int y;
};

Тогда функция, которая получает 2 точки в качестве параметров и находит центр между двумя точками будет иметь приблизительно следующий вид:

// функция, возвращающая ref-структуру
MyPointRef ^ GetCenterLineRef(MyPointRef ^p1, MyPointRef ^p2)
{
    MyPointRef ^resPt;

    // выделение памяти для указателя resPt
    resPt = gcnew MyPointRef;

    // заполнение значениями полей указателя
    resPt->x = (p1->x + p2->x)/2;
    resPt->y = (p1->y + p2->y)/2;

    return resPt;
}

Демонстрация использования функции в другом программном коде.

// указатели на ref-структуру
MyPointRef ^pt1;
MyPointRef ^pt2;
MyPointRef ^resPt;

// выделение памяти для указателей pt1, pt2
pt1 = gcnew MyPointRef;
pt2 = gcnew MyPointRef;

// заполнение значениями
pt1->x = 20;
pt1->y = 20;
pt2->x = 30;
pt2->y = 40;

// вызов функции GetCenterLineRef()
// память для указателя resPt выделяется внутри функции
resPt = GetCenterLineRef(pt1, pt2);

// проверка
int d;
d = resPt->x; // d = 25
d = resPt->y; // d = 30


Связанные темы