首页 > 解决方案 > C嵌套结构语法斗争

问题描述

我有以下代码,我已经多年没有使用 C 语言了,我对语法感到困惑。如何在init函数中初始化两个顾客?我认为我需要做一些sizeof/malloc魔术,但实际上我对语法感到困惑。

我相信我可以像这样访问顾客数据:

t->seating[0].first_name 

但是,我无法创建大量座位。是的,我的init函数是空的,因为我已经删除了我尝试过的所有疯狂代码。

#include <stdio.h>

struct patron {
   char last_name[30];
   char first_name[30];
};

struct theatre_seating {
    struct patron **seating;
};

void init(int elem, struct theatre_seating *t);


int main() {
    struct theatre_seating theatre_seating;

    int elem = 2;


    init(elem, &theatre_seating);

    return 1;
}

void init(int elem, struct theatre_seating *t) {

}

标签: c

解决方案


这是另一种方法,应该更健壮一些。它使用 C99 的灵活数组成员。

代替固定大小的数组,将字符数据放到灵活的数组成员中。首先存储名字,然后是(字符串 NUL 的结尾,\0和)姓氏(以及字符串 NUL 的另一端)。

为了避免必须找到名字的开始位置,我们可以存储一个指针或一个偏移量。我更喜欢偏移量,但只要你(程序员!)小心,指针也可以正常工作:

struct patron {
    char *last_name;    /* Points to within the first_name member */
    char  first_name[]; /* Flexible array member */
};

通常,您编写辅助函数来分配和初始化以及释放此类结构:

void free_patron(struct patron *p)
{
    if (p) {
        /* "Poisoning" the structure, to help detect possible use-after-free bugs. */
        p->last_name = NULL;
        p->first_name[0] = '\0';

        /* Both names reside in the same dynamically allocated part. */
        free(p);
    }
}

struct patron *new_patron(const char *first, const char *last)
{
    const size_t   firstlen = (first) ? strlen(first) : 0;
    const size_t   lastlen = (last) ? strlen(last) : 0;
    struct patron *newpatron;

    /* Don't allow unnamed patrons. */
    if (firstlen + lastlen < 1) {
        fprintf(stderr, "new_patron(): NULL or empty name.\n");
        exit(EXIT_FAILURE);
    }

    /* Allocate enough memory for the structure. */
    newpatron = malloc(sizeof (struct patron) + firstlen + 1 + lastlen + 1);
    if (!newpatron) {
        fprintf(stderr, "new_patron(): Not enough memory.\n");
        exit(EXIT_FAILURE);
    }

    /* First name goes first. */
    if (firstlen > 0)
        memcpy(newpatron->first_name, first, firstlen);
    newpatron->first_name[firstlen] = '\0';

    /* Last name follows. */
    newpatron->last_name = newpatron->first_name + firstlen + 1;
    if (lastlen > 0)
        memcpy(newpatron->last_name, last, lastlen);
    newpatron->last_name[lastlen] = '\0';

    return newpatron;
}

为了管理一个赞助人数组,这次每个条目都是一个指向结构赞助人的指针。这意味着您可以选择是否使用固定大小的数组,通过定位 NULL 指针来定位空座位。

struct seating {
    size_t           seats;
    struct patron  **seat;
};

#define  NO_VACANCIES  (~(size_t)0)

void free_seating(struct seating *s)
{
    if (s) {
        free(s->seat);
        s->seats = 0;
        s->seat = NULL;
    }
}

void init_seating(struct seating *s, const size_t n)
{
    size_t  i;

    if (!s) {
        fprintf(stderr, "init_seating(): NULL pointer to struct seating.\n");
        exit(EXIT_FAILURE);
    }

    /* No seats wanted at all? */
    if (n < 1) {
        s->seats = 0;
        s->seat  = NULL;
        return;
    }

    s->seat = malloc(n * sizeof s->seat[0]);
    if (!s->seat) {
        fprintf(stderr, "init_seating(): Not enough memory.\n");
        exit(EXIT_FAILURE);
    }
    s->seats = n;

    /* Initialize all seats as vacant. */
    for (i = 0; i < n; i++)
        s->seat[i] = NULL;

    /* Done. */
}

/* Find a vacant/unused seating.
   Returns the seat index, or NO_VACANCIES if all taken. */
size_t vacant_seating(struct seating *s)
{
    size_t  i;

    if (!s || s->seats < 1)
        return NO_VACANCIES;

    for (i = 0; i < s->seats; i++)
        if (!s->seat[i])
            return i; /* Seat i is vacant. */

    return NO_VACANCIES;
}

/* Removes a patron from a seating.
   You'll usually want to call
       free_patron(release_seating(&my_threatre, place));
   to free the structure naming the patron as well.
   This is safe to do even if the seat was vacant. */
struct patron *release_seating(struct seating *s, size_t i)
{
    if (s && i < s->seats) {
        struct patron *old_patron = s->seat[i];
        s->seat[i] = NULL;
        return old_patron;
    } else
        return NULL;
}

在您的程序中,使用这些很简单:

struct seating  my_theatre;
size_t          place;

/* Small venue with 50 seats. */
init_seating(&my_theatre, 50);

/* Find a vacant seat. */
place = vacant_seating(&my_theatre);
if (place == NO_VACANCIES) {
    fprintf(stderr, "Sorry, the theatre is full.\n");
    return EXIT_FAILURE;
}

/* Seat DanielN there. */
my_theatre.seat[place] = new_patron("Daniel", "N");

请注意,因为my_theatre.seat是一个数组,my_theatre.seat + place所以是指向数组中place第 th 个元素的指针,与&(my_theatre.seat[place]).

另请注意,在分配数组时,例如struct something *foo;,您可以使用 sizeof 运算符:foo = malloc(n * sizeof foo[0]);尝试为n任何类型的元素分配足够的内存foo[0]。请注意,为了帮助我们程序员记住这sizeof是一个运算符,而不是一个函数。即使foo是 undefined 或 NULL,sizeof foo[0]也是有效的,因为 sizeof 运算符只检查其参数的类型以确定类型的大小。

NO_VACANCIES宏计算为最大值(该size_t类型可以在非二进制计算机上以二进制形式描述)。该表达式适用于所有无符号整数类型,并且size_t是无符号(非负)整数类型。最好包含<limits.h>和使用SIZE_MAX(类似于该头文件定义的CHAR_MAXUCHAR_MAXINT_MAX等),但我不确定是否所有(嗯,微软;他们喜欢以自己的方式做事)定义SIZE_MAX.


推荐阅读