C语言不完整类型

Posted by 周思进 on December 20, 2020

因为 openssl 1.0.2 已不再维护,在进行 openssl 升级到 1.1.1 版本的时候,应用中有不少使用 openssl 的接口都需要进行相应的修改。

而修改最大的感受就是很多结构体都无法直接声明使用了,只能改为指针声明使用。

在 openssl 的 CHANGES 文档(该文档对于 openssl 的变更很有必要查看了解)中也有说明,如下:

Made EVP_MD_CTX, EVP_MD, EVP_CIPHER_CTX, EVP_CIPHER and HMAC_CTX opaque. For HMAC_CTX, the following constructors and destructors were added:

HMAC_CTX *HMAC_CTX_new(void); void HMAC_CTX_free(HMAC_CTX *ctx);

这里的关键字, opaque 不透明的

即你无法查看该结构体的组成,不清楚其有哪些成员变量。这个特性可以认为是 C 语言的封装。


使用示例:

#point.h

typedef struct point POINT;
POINT * new_point(int x, int y);
void free_point(POINT *a);
int cal_point_value(POINT *a);
#point.c

#include <stdio.h>
#include <stdlib.h>

typedef struct point
{
    int x;
    int y;
}POINT;

POINT * new_point(int x, int y)
{
    POINT *p = malloc(sizeof(POINT));
    if (NULL == p)
        return NULL;

    p->x = 2 * x;
    p->y = 3 * y;
    return p;
}

void free_point(POINT *a)
{
    if (a != NULL)
        free(a);
}

int cal_point_value(POINT *a)
{
    return a->x + a->y;
}

#include <stdio.h>
#include "point.h"

int main(void)
{
    int ret = -1;
    POINT *p = NULL;

    p = new_point(2, 4);
    if (NULL == p)
        return -1;
    ret = cal_point_value(p);
    printf("ret:%d\n", ret);

    free_point(p);

    return 0;
}

如果调用方像往常一样声明定义结构体,编译链接就会报错,如下:

image

错误提示 point.h 使用的是 forward declaration,即前置声明。

前置声明针对的都是不完整类型,如具体成员未明确的结构体,也就如错误提示说明的那样,variable has incomplete type 'POINT' (aka 'struct point')

如要使用不完整类型,都需以指针的方式声明使用。


使用不完整类型的好处:
调用方因不能直接定义 POINT 结构体,无法直接操作结构体成员变量,隐藏细节的同时避免了外部随意破坏结构体中成员变量的值,只能按提供方的接口去调用操作,保证操作的可预见性,而不会出现意料之外的错误。

不好的地方:
1、因为不能由调用方定义结构体,则结构体的定义申请操作放到了接口内部实现,相应的,调用方使用完之后,需要手动进行释放操作,而不像直接定义结构体变量,函数退出就自动释放了。

2、无法使用 sizeof 操作获取结构体的大小。