簡體   English   中英

如何將通用結構傳遞給C中的函數?

[英]How can I pass a generic struct to a function in C?

我是C編程的初學者,我對將通用結構傳遞給C中的函數有疑問。

這是我所擁有的:

typedef struct {
    char name[20];
    float price;
} product;

typedef struct {
    char name[20];
    int type;
} category;

我想做這樣的事情:

void changeName(struct *s, newName[20]) {
    strcpy(s->name, newName);
}

如果有人已經問過了,請不要理會,並向我發送問題鏈接。

有人可以幫我嗎?

謝謝。

使用union

一種方法是添加一個包含union的結構,該union本身包含指向productcategory結構的指針,以及一個enum以標識struct的數據類型。 union或指向它的指針可以傳遞給change_name()函數。

這是在C11中可以使用的示例。 它使用一個未命名的union成員,因此這不是有效的C99代碼:

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

typedef struct {
    char name[20];
    float price;
} product;

typedef struct {
    char name[20];
    int type;
} category;

typedef struct {
    enum { PRODUCT, CATEGORY } type;
    union {
        product *prod;
        category *cat;
    };
} generic;

void change_name(generic *gen, const char *new_name);

int main(void)
{
    product prod_a = { .name = "widget", .price = 1.99 };
    category cat_a = { .name = "general", .type = 1 };
    generic gen_prod_a = { .type = PRODUCT, .prod = &prod_a };
    generic gen_cat_a = { .type = CATEGORY, .cat = &cat_a };

    printf("prod_a.name = %s\n", prod_a.name);
    printf("cat_a.name = %s\n", cat_a.name);

    change_name(&gen_prod_a, "gadget");
    change_name(&gen_cat_a, "specific");

    printf("prod_a.name = %s\n", prod_a.name);
    printf("cat_a.name = %s\n", cat_a.name);

    return 0;
}

void change_name(generic *gen, const char *new_name)
{
    switch (gen->type) {
    case PRODUCT:
        strcpy(gen->prod->name, new_name);
        break;
    case CATEGORY:
        strcpy(gen->cat->name, new_name);
        break;
    default:
        fprintf(stderr, "Unknown type in change_name()\n");
    }
}

可以通過命名union使它在C99中起作用:

typedef struct {
    enum { PRODUCT, CATEGORY } type;
    union {
        product *prod;
        category *cat;
    } data;                                  // named for C99
} generic;

/* ... */

generic gen_prod_a = { .type = PRODUCT, .data.prod = &prod_a };
generic gen_cat_a = { .type = CATEGORY, .data.cat = &cat_a };

/* ... */

void change_name(generic *gen, const char *new_name)
{
    switch (gen->type) {
    case PRODUCT:
        strcpy(gen->data.prod->name, new_name);
        break;
    case CATEGORY:
        strcpy(gen->data.cat->name, new_name);
        break;
    default:
        fprintf(stderr, "Unknown type in change_name()\n");
    }
}

可替代地,一個struct類型可以容納一個enum標識符和union含有產物和類結構。 這種方法似乎更加簡化:

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

typedef struct {
    enum { PRODUCT, CATEGORY } type;
    union {
        struct {
            char name[20];
            float price;
        } prod;
        struct {
            char name[20];
            int type;
        } cat;
    } data;
} record;

void change_name(record *rec, const char *new_name);

int main(void)
{
    record prod_a = { .type = PRODUCT };
    change_name(&prod_a, "widget");
    prod_a.data.prod.price = 1.99;

    record cat_a = { .type = CATEGORY };
    change_name(&cat_a, "general");
    cat_a.data.cat.type = 1;

    printf("prod_a.name = %s\n", prod_a.data.prod.name);
    printf("cat_a.name = %s\n", cat_a.data.cat.name);

    change_name(&prod_a, "gadget");
    change_name(&cat_a, "specific");

    printf("prod_a.name = %s\n", prod_a.data.prod.name);
    printf("cat_a.name = %s\n", cat_a.data.cat.name);

    return 0;
}

void change_name(record *rec, const char *new_name)
{
    switch (rec->type) {
    case PRODUCT:
        strcpy(rec->data.prod.name, new_name);
        break;
    case CATEGORY:
        strcpy(rec->data.cat.name, new_name);
        break;
    default:
        fprintf(stderr, "Unknown type in change_name()\n");
    }
}

使用類型通用宏

以上兩種方法都有些尷尬。 僅適用於C11的另一種解決方案是在類型通用宏中使用_Generic關鍵字。 在此,為每種期望的數據類型編寫函數,並且宏根據類型選擇要使用的函數定義。 這種方法的優點是,隨着添加新類型,只需新函數和對類型通用宏的更新即可處理它們。

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

#define change_name(S, N) _Generic ((S),                                \
                                    prod_ptr: change_name_prod,         \
                                    cat_ptr: change_name_cat            \
                                   )((S), (N))

typedef struct {
    char name[20];
    float price;
} product;

typedef struct {
    char name[20];
    int type;
} category;

typedef product *prod_ptr;
typedef category *cat_ptr;

void change_name_prod(product *prod, const char *new_name);
void change_name_cat(category *cat, const char *new_name);

int main(void)
{
    product prod_a = { .name = "widget", .price = 1.99 };
    category cat_a = { .name = "general", .type = 1 };

    printf("prod_a.name = %s\n", prod_a.name);
    printf("cat_a.name = %s\n", cat_a.name);

    change_name(&prod_a, "gadget");
    change_name(&cat_a, "specific");

    printf("prod_a.name = %s\n", prod_a.name);
    printf("cat_a.name = %s\n", cat_a.name);

    return 0;
}

void change_name_prod(product *prod, const char *new_name)
{
    strcpy(prod->name, new_name);
}

void change_name_cat(category *cat, const char *new_name)
{
    strcpy(cat->name, new_name);
}

以上所有程序都具有相同的輸出:

prod_a.name = widget
cat_a.name = general
prod_a.name = gadget
cat_a.name = specific

您已經對結構進行了類型定義。 您可以使用這些其他名稱。

例如,如果名稱為product ,則首先創建一個變量並調用該函數

product var={"name", 1.2};
changeName(&var, "AnotherName");

然后將此變量傳遞給函數

void changeName(product *s, char newName[])
{
    strcpy(s->name, newName);
}

泛型編程,這意味着C語言中實際上並不存在類型安全性。但是有很多方法可以解決這種情況。

我從您的問題中了解到的是,“我可以定義一個可以應用於不同結構的通用元素的函數嗎?

讓我們擴展一下示例,以具有共同特征的項目。

struct info
{
    char name[20];
    int id;
};

struct product
{
   Info info;
   int price;
};

struct category
{
   Info info;
   int type;
};

現在,您可以定義一個可以在產品和類別共享的特征上安全運行的函數。

void changeName(info* p, const char* name)
{
    strcpy_s(info->name, sizeof(info->name), name);
}

int main()
{
    category cat;
    product prod;
    memset(&cat, 0, sizeof(cat));
    memset(&prod, 0, sizeof(prod));

    changeName(&cat.info, "Category 1");
    changeName(&prod.info, "blue product");

    return 0;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM