構造体と関数

引数に構造体を持つ関数

関数の引数には、構造体を指定することもできます。

#include <stdio.h>

typedef struct
{
    char name[50];
    int age;
    char gender;
} Person;

void PrintPerson(Person p)
{
    printf("name: %s\n", p.name);
    printf("age: %d\n", p.age);
    printf("gender: %d\n", p.gender);
}

int main()
{
    Person person[] = {
        { "A山B男", 20, 0 },
        { "C下D太", 18, 0 },
        { "E田F子", 21, 1 },
        { "G山H美", 19, 1 },
    };
    PrintPerson(person[2]);

    getchar();
}

特に難しい処理ではないでしょう。

関数の仮引数へは、実引数(呼び出し元)に指定した値がコピーされます。
これは構造体であっても同じです。
そのため、関数内で構造体のメンバ変数を書き換えても、呼び出し元の構造体変数には影響はありません。

#include <stdio.h>

typedef struct
{
    char name[50];
    int age;
    char gender;
} Person;

void PrintPerson(Person p)
{
    p.age = 30;
    printf("name: %s\n", p.name);
    printf("age: %d\n", p.age); //30
    printf("gender: %d\n", p.gender);
}

int main()
{
    Person person = { "A山B男", 20, 0 };
    PrintPerson(person);

    printf("\nage: %d", person.age); //20
    getchar();
}
name: A山B男
age: 30
gender: 0

age: 20

関数内でメンバ変数ageを書き換えていますが、呼び出し元には影響がないことを確認してください。

構造体を返す関数

配列は関数の戻り値にはできませんが、構造体はそれが可能です。

#include <stdio.h>
#include <string.h>

#define NAME_LENGTH 50

typedef struct
{
    char name[NAME_LENGTH];
    int age;
    char gender;
} Person;

Person CreatePerson(char *name, int age, char gender)
{
    Person p = { "", age, gender };
    strcpy_s(p.name, NAME_LENGTH, name);
    return p;
}

int main()
{
    Person person;
    person = CreatePerson("A山B男", 20, 0);

    printf("name: %s\n", person.name);
    printf("age: %d\n", person.age);
    printf("gender: %d\n", person.gender);

    getchar();
}

構造体は新しいデータ型ですから、関数の戻り値のデータ型としてそのまま記述することができます。
後は関数の戻り値を構造体変数で受け取るだけです。

関数によって複数の値を同時に返したい場合、引数のポインタ渡しを使用する方法がありましたが、構造体を使えば通常の戻り値として複数の値を受け取ることができるわけです。
ただし、戻り値用の構造体を定義する必要があるため、どちらの方法が良いかはケースバイケースでしょう。

15行目は構造体変数の初期化処理ですが、文字列配列nameの箇所に「""」と、空文字を指定しています。
構造体は、メンバ変数内の文字列配列を文字列リテラルにより初期化することはできます。
しかし文字列ポインタを指定して初期化することはできません。
(これは文字列配列の初期化の制限です)
そのため、いったん空の文字列で初期化し、後からstrcpy(strcpy_s)関数で文字列のコピーを行っています。

また、文字列配列のサイズを#defineによって定義していることに注目してください。

番外:どうしても関数で配列を返したい場合

関数の戻り値に配列を指定することはできません。
関数で配列を得るには、呼び出し元で配列を宣言し、それを引数にして関数内で書き換えるのが一般的です。

どうしても配列を戻り値にしたい場合は構造体を利用する方法もあります。

#include <stdio.h>

typedef struct
{
    int number[10];
} Number10;

Number10 GetNumber10(int start, int step)
{
    Number10 n10;
    for (int i = 0; i < 10; i++)
    {
        n10.number[i] = start + (step * i);
    }

    return n10;
}

int main()
{
    Number10 number10 = GetNumber10(0, 5);

    for (int i = 0; i < 10; i++)
    {
        printf("%d, ", number10.number[i]);
    }

    getchar();
}

配列を受け取るというよりは、そのまま構造体を受け取っているだけです。
わざわざ構造体を定義しなければならず、処理速度の面でもメリットがないためあまり用いられません。