Մինչ այժմ, երբ օրինակներում փոփոխականներ կամ ֆունկցիաներ էի հայտարարում, օգտագործում էի C լեզվի պարզ (սկալյար) տիպերը՝ int
, double
, float
և այլն։ Բայց, առավել հաճախ կարիք է լինում ծրագրերում օգտագործել բաղադրյալ օբյեկտներ, այնպիսիք, որոնց կառուցվածքը սահմանում է ծրագրավորողը։ Օրինակ, հենց նույն այդ դեկարտյան կետերի հետ աշխատելիս ավելի հարմար կլինի ունենալ մի օբյեկտ, որն ունի x
և y
հատկությունները (դաշտերը), քան աշխատել երկու double
թվերի կամ նրանց ցուցիչների հետ։
C լեզվի struct
ծառայողական բառը ծրագրավորողին տալիս է այդ հնարավորությունը։ Օրինակ․
struct { double x, y; } p0;
Այս հրամանով p0
փոփոխականը հայտարարեցի որպես x
և y
դաշտերն ունեցող բաղադրյալ օբյեկտ՝ ստրուկտուրա։
Ստրուկտուրայի դաշտերին դիմում են .
գործողության միջոցով։ Օրինակ, p0
օբյեկտը տպելու համար պետք է գրել․
printf( "%lf, %lf", p0.x, p0.y );
Ստրուկտուրային օբյեկտներ սահմանելիս struct
ծառայողական բառից հետո կարելի է գրել ստրուկտուրայի անուն, որն էլ հետագայում կարելի է օգտագործել նույն տիպի այլ օբյեկտներ սահմանելիս։ Օրինակ, p1
դեկարտյան կետի սահմանումը կարող է ունենալ այսպիսի սահմանում․
struct point { double x, y; } p1;
Այս տիպի արտահայտությունը թույլ է տալիս ծրագում ամեն անգամ struct { double x, y; }
գրելու փոխարեն պարզապես գրել struct point
։
Հիմա հետ վարադառնամ իմ հին խնդիրն, երբ գրեցի polar
ֆունկցիան, որը տրված կետի դեկարտյան կոորդինատներից հաշվում է դրա բևեռային կոորդինատները։ Նախ հայտարարեմ cartesian_point
ստրուկտուրան՝ իր x
, y
դաշտերով, և polar_point
ստրուկտուրան՝ rho
, phi
դաշտերով։
/* դեկարտյան կետը */
struct cartesian_point {
double x; /* աբսցիս */
double y; /* օրդինատ */
};
/* բևեռային կետը */
struct polar_point {
double rho; /* շառավիղ */
double phi; /* ազիմուտ */
};
ՈՒնենալով cartesian_point
և polar_point
ստրուկտուրաների հայտարարությունները, ես կարող եմ սահմանել to_polar
ֆունկցիան, որը ստանում է դեկարտյան կետ և վերադարձնում է համարժեք բևեռային կետը։
struct polar_point to_polar( struct cartesian_point c )
{
struct polar_point res;
res.rho = sqrt( c.x * c.x + c.y * c.y );
res.phi = atan2( c.y, c.x );
return res;
}
Ճիշտ նույն կերպ կարող եմ սահմանել to_cartesian
ֆունկցիան, որը ստանում է բևեռային կետ և վերադարձնում է դրա դեկարտյան ներկայացումը։
struct cartesian_point to_cartesian( struct polar_point p )
{
struct cartesian_point res;
res.x = p.rho * cos( p.phi );
res.y = p.rho * sin( p.phi );
return res;
}
Հիմա գրեմ main
ֆունկցիան, որտեղ ցուցադրված են վերը հայտարարված ստրուկտուրաների ու սահմանված ֆունկցիաների կիրառությունները։
int main()
{
struct cartesian_point c0;
puts( "Ներածիր դեկարտյան կոորդինատները x,y․ " );
scanf( "(%lf, %lf)", &c0.x, &c0.y );
struct polar_point p0 = to_polar( c0 );
puts( "Բևեռային կոորդնատներն են․ " );
printf( "ρ = %lf, φ = %lf\n", p0.rho, p0.phi );
struct cartesian_point c1 = to_cartesian( p0 );
puts( "Դեկարտյան կոորդնատներն են․ " );
printf( "x = %lf, y = %lf\n", c1.x, c1.y );
return 0;
}
Ակնհայտ է, որ cartesian_point
և polar_point
ստրուկտուրաները, ըստ էության, նույնն են։ Երկուսն էլ մոդելավորում են կետի գաղափարը, բայց տարբերվում են իրենց դաշտերի անուններով։ Կարելի է երկուսի փոխարեն սահմանել մեկ point
ստրուկտուրա՝ դաշտերին տալով first
(առաջին) և second
(երկրորդ) պայմանական անունները, և այդ միակ ստրուկտուրան օգտագործել և՛ դեկարտյան, և՛ բևեռային կետերը ներկայացնելու համար։ Օրինակ.
struct point {
double first;
double second;
};
Դեկարտյան կետերը բևեռային կետերից տարբերելու համար կարող եմ typedef
հրամանով սահմանել struct point
տիպի երկու հոմանիշ անուններ։
typedef struct point cartesian;
typedef struct point polar;
typedef
հրամանը ծրագրում սահմանում է արդեն գոյություն ունեցող տիպի համարժեք, հոմանիշ անուն։ Օրինակ, կարող եմ իմ ծրագրում real
անունը սահմանել որպես long double
տիպի անուն․
typedef long double real;
Առավել հաճախ typedef
հայտարարությունն օգտագործվում է հենց ստրուկտուրաների համար կարճ անուններ հայտարարելու նպատակով։ Մեկ typedef
արտահայտությամբ կարելի է մի քանի համարժեք անուններ։ Օրինակ.
typedef struct point {
double first;
double second;
} cartesian, polar;
Ավելին, typedef
հայտարարություններ անելիս, կարելի է բաց թողնել ստրուկտուրայի անունը (եթե այն այլևս չի օգտագործվելու)։ Այսպես.
typedef struct {
double first;
double second;
} cartesian, polar;
Նման հայտարարությունից հետո վերը սահմանված to_polar
ֆունկցիան կունենա հետևյալ տեսքը.
polar to_polar( cartesian c )
{
polar res;
res.first = sqrt( c.first * c.first + c.second * c.second );
res.second = atan2( c.second, c.first );
return res;
}
Ստրուկտուրայի նոր նմուշ ստեղծելիս դրա դաշտերին տրվող արժեքները կարելի է թվարկել {
և }
փակագծերի մեջ առնված ցուցակում։ Օրինակ, to_polar
ֆունկցիան կարելի է սահմանել նաև հետևյալ կերպ.
polar to_polar( cartesian c )
{
polar res = {
sqrt( c.first * c.first + c.second * c.second ),
atan2( c.second, c.first )
};
return res;
}
Եթե ինչ-որ պատճառով պետք է խախտել արժեքների թվարկման հաջորդականությունը, ապա պետք է նշել, թե հերթական արժեքը որ դաշտին է վերագրվում՝ անունից առաջ .
նիշը գրելով։ Օրինակ, այսպես.
struct cartesian c1 = { .y = 1.2, .x = 3.4 };
Բայց մի անհարմար բան էլ կա. ստիպված եմ և՛ cartesian
, և՛ polar
օբյեկտների դաշտերին դիմելու համար օգտագործել նրանց first
և second
անունները, որի պատճառով գրված կոդը նվազ հասկանալի է դառնում։ Ես այդ խնդիրը կարող եմ լուծել մի քանի պարզ մակրոսներ սահմանելով.
#define X(p) p.first
#define Y(p) p.second
#define RHO(p) p.first
#define PHI(p) p.second
Կոմպիլյացիայից առաջ C լեզվով գրված ծրագրերը մշակվում են նախապրոցեսորի կողմից, և մակրոսները հենց նախապրոցեսորի սահմանումներ են։ Մակրոսի սահմանումն ունի հետևյալ տեսքը․
#define ⟨name⟩[(⟨parameters⟩)] ⟨body⟩
Ամեն անգամ, երբ նախապրոցեսորը ծրագրի տեքստում հանդիպում է ⟨name⟩
անունը, դա փոխարինվում է մակրոսի ⟨body⟩
մարմնով՝ իհարկե, համապատասխան տեղերում տեղադրելով մակրոսի պարամետրերի արժեքները։ C լեզվում ընդունված է մակրոսների անունները գրել մեծատառերով։
Դեկարտյան և բևեռային կոորդինատների դաշտերը ընտրող մակրոսները սահմանելուց հետո, օրինակ, to_cartesian
ֆունկցիան կարող եմ սահմանել հետևյալ կերպ.
cartesian to_cartesian( polar p )
{
cartesian res;
X(res) = RHO(p) * cos( PHI(p) );
Y(res) = RHO(p) * sin( PHI(p) );
return res;
}
Երբ C լեզվի նախապրոցեսորը կմշակի այս տեքստը, բոլոր մակրոսները կփոխարինվեն իրենց սահմանումներով և կոմպիլյացիան կշարունակվի այնպես, կարծես թե մակրոսներ չեն էլ եղել։
Ստրուկտուրայի չափը՝ հիշողություն մեջ նրա նմուշի զբաղեցրած բայթերի քանակը, որոշվում է որպես նրա դաշտերի չափերի գումար։ Քանի որ point
ստրուկտուրան ունի երկու double
տիպի դաշտեր, ապա ճիշտ է sizeof(struct point) >= 2 * sizeof(double)
հավասարությունը։