在 C 语言中隐藏 init 结构并使用 init 函数

Hide init struct in C and use init function

提问人:S2G 提问时间:6/25/2023 最后编辑:S2G 更新时间:6/25/2023 访问量:56

问:

我正在编写一个结构类型定义来初始化一些功能(嵌入式微控制器编程)

但是,如果这个结构是init,那么整个结构就会崩溃,所以我决定编写一个函数来初始化结构并malloc内存,然后在函数返回中将指针发送回用户。如下代码所示:

//this is just a demonstration of what i want not the code
#include <stdio.h>
#include <stdlib.h>

typedef struct {
    int a;
    int b;
}mystr;

mystr * init_str(int a, int b)
{
    mystr * strinit = (mystr *)malloc(sizeof(mystr));
    //for example no more than 10 is acceptable !!
    if(a<10)
        strinit->a=a;
    else
        strinit->a=10;
        
    if(b<10)
        strinit->b=b;
    else
        strinit->b=10;

    return strinit;
}

 void deinit_str (mystr * str)
 {
    free(str);
    str=NULL;
 }
    
int main()
{
    mystr * strinit = init_str(5, 15);
    printf("numbers are = a: %d , b: %d \n",strinit->a,strinit->b);
    deinit_str(strinit);
}

这里输出的是 5 和 10

但是,该结构对用户仍然是可见的,他们可以忽略使用 init 函数并直接进入该结构并使用杀死此方法的整个 porpuse,例如以下代码:

//this is just a demostration of what i want not the code
#include <stdio.h>
#include <stdlib.h>

typedef struct {
    int a;
    int b;
}mystr;

mystr * init_str(int a, int b)
{
    mystr * strinit = (mystr *)malloc(sizeof(mystr));
    //for example no more than 10 is acceptable !!
    if(a<10)
        strinit->a=a;
    else
        strinit->a=10;
        
    if(b<10)
        strinit->b=b;
    else
        strinit->b=10;

    return strinit;
}

 void deinit_str (mystr * str)
 {
    free(str);
    str=NULL;
 }
    
int main()
{
    //IGNORE CREATE AND WRITE WHAT EVER
    mystr strinit={
        .a=5555,
        .b=6969,
    };
    //THE NUMBERS ARE HIGHER THAN 10 AND NOT acceptable
    printf("numbers are = a: %d , b: %d \n",strinit.a,strinit.b);
}

这里输出的是 5555 和 6969,这是错误的 如何限制结构的使用并只允许使用init函数进行创建

如果在 C 中没有办法做到这一点,我可以在 C++ 中做这样的事情吗?

c 函数 结构 malloc

评论

1赞 Retired Ninja 6/25/2023
还有数百种其他方法可以在不使用该结构的情况下破坏程序。记录它的使用方式,如果文档被忽略,他们就会得到他们所得到的。
0赞 AndersK 6/25/2023
程序员是一群经常规避限制的人,无论你付出多少努力和想法来避免这种情况,他们都会找到解决方法。相反,只需将其记录下来并保留它,并在代码中散布一些断言以检查值,以便它们不会使代码崩溃。
0赞 S2G 6/25/2023
@TedLyngmo不,我的意思是C++
0赞 S2G 6/25/2023
@RetiredNinja你是对的!尽管如此,我还是试图将这种可能性降到最低
0赞 S2G 6/25/2023
@AndersK我确实在使用此结构的函数中放置了大量代码来检查,但防止代码出错是更可取的,文档中有警告

答:

1赞 Ted Lyngmo 6/25/2023 #1

您可以将 设置为不透明类型,并将实现细节隐藏在文件中。这样做清楚地表明,除了使用提供的函数之外,连接到此类型的数据不应以任何其他方式弄乱。struct.c

例:

公共头文件:

typedef struct {
    void *ptr;
} Handle;

Handle init_str(int a, int b);
void deinit_str (Handle h);

文件中的内部实现:.c

typedef struct { // never exposed in a public header file
    int a;
    int b;
} mystr;

Handle init_str(int a, int b) {
    mystr *ms = malloc(sizeof *ms);
    *ms = (mystr){.a = a < 10 ? a : 10,
                  .b = b < 10 ? b : 10};
    return (Handle){.ptr = ms};
}

void deinit_str (Handle h) {
    free(h.ptr);
}

在文档中,您只会提到库中的函数返回或采用 .Handle

当然,了解内部实现细节的人仍然可以解决这个问题,但这需要更多的努力,而且这样做的人肯定不会错误地这样做,并且应该为意外做好准备,如果你在下一个版本中更改了内部重复库。

评论

0赞 S2G 6/25/2023
谢谢!我以前从未使用过这个,我将测试它,然后提供反馈
1赞 Ted Lyngmo 6/25/2023
@S2G 不客气!我自己也使用过它,主要不是为了向用户隐藏实现细节,而是为了能够为 C++ 库提供 C 接口。C 编译器只能看到不透明的类类型,而 C++ 编译器可以看到两者并编译内部结构。效果很好。Handle
0赞 0___________ 6/25/2023
为什么这么复杂。只需返回 void *指针
3赞 Ted Lyngmo 6/25/2023
@0___________这是为了添加一些类型安全性。当我自己在库中做这件事时,我发现当有许多类似类型的类型时,它可以帮助用户正确地做到这一点。如果犯了错误,得到一个“不兼容的参数类型”编译错误比崩溃更好:-)Handle