如何使struct成员私有化?

ryy*_*yst 6 c struct

我在头文件中定义了一个结构,如下所示:

typedef struct {
    void *data;
} point;
Run Code Online (Sandbox Code Playgroud)

我希望让其他人不直接访问*数据,所以我想我会在.c文件中声明结构,并使用类似extern typedef struct point;头文件中的内容.然而,这不起作用.

实现这一目标的最佳方法是什么?

Gra*_*and 14

在您的(公共)头文件中:

typedef struct point point;
Run Code Online (Sandbox Code Playgroud)

在您的.c文件中:

struct point
{
    void *data;
};
Run Code Online (Sandbox Code Playgroud)

请注意,代码的用户将无法再point在堆栈上创建,因为编译器不知道它有多大.您可能必须提供一个point_create()分配内存并将其地址返回给调用者的函数.

  • +1如果你想用C这样的语言进行这种保护,那么丢弃堆栈分配就是你必须付出的代价. (2认同)

Alo*_*aus 6

使用 C++


由于这里似乎不允许开玩笑,因此是纯 C 版本。正如另一位评论者指出的那样,如果您真的想保护您的内部结构免受 Api 用户的影响,那么您已经看到并使用了大量此类 API。该 API 是例如 Windows 或 Linux 用户模式 ​​API。在那里您创建了您永远无法访问的内核对象。处理内核对象的 API 使用一种称为 handle 的合成构造,它不仅仅是指向您自己的对象的指针,而是指向内核已为您的对象存储相关元数据的数组的索引。您也可以将相同的想法用于您的 API。例如,这里是一个 C 风格的公共 Api:

// Public.h
#include <stdlib.h>

typedef enum 
{
    None = 0,
    PointType = 1
} Types;

typedef int Handle;

Handle CreateType(Types type);
int    DeleteType(Handle object);

void IncrementX(Handle point);
void PrintPoint(Handle point);
Run Code Online (Sandbox Code Playgroud)

如您所见,您拥有创建和删除在枚举中定义的对象的通用方法。然后,使用该对象的方法将需要查找整数句柄以获取存储真实数据的元数据对象。如果您管理的对象很小,则这种设计效率不高,因为对于每个对象,都需要第二个对象来存储对象类型、句柄值和指向实际数据的指针。但是您可以获得更强大的安全保证,例如

  • 类型安全
  • 无效句柄很容易找到
  • 双重释放是不可能的,因为您可以在元对象中管理自由状态

您的 Api 的典型用法可能如下所示:

Handle h = CreateType(PointType);
IncrementX(h);
IncrementX(h);
PrintPoint(h);
DeleteType(h);
Run Code Online (Sandbox Code Playgroud)

在 private.cpp 中有一个超级秘密实现,其中存在 Handle 查找数组和一些辅助方法:

// Private.C
#include "stdafx.h"
#include <stdlib.h>
#include <Windows.h>  // for ZeroMemory

#include "Public.h"

typedef struct 
{
    LPVOID pData;
    Types  type;
    Handle handle;
} HandleInfo;


typedef struct
{
    int x;
    int y;
} Point;

HandleInfo *pAllocated;
int HandleBuffer = 0xffff;
unsigned char bInit = 0;

HandleInfo *GetFreeHandle()
{
    int i;

    if( !bInit )
    {
        pAllocated = (HandleInfo *) malloc(sizeof(HandleInfo)*HandleBuffer);
        bInit = 1;
        ZeroMemory(pAllocated, sizeof(HandleInfo)*HandleBuffer);
    }

    for(i=0; i<HandleBuffer; i++)
    {
        HandleInfo *pInfo = (pAllocated+i);
        if( 0 == pInfo->handle  )
        {
            pInfo->handle = i+1;
            return pInfo;
        }
    }

    return NULL;
}

HandleInfo * GetHandleInfo(Handle h)
{
    if( h <= 0 || h >= HandleBuffer-1)
    {
        return NULL;
    }

    return (pAllocated+h-1);
}

Handle CreateType(Types typeId)
{
    HandleInfo *pInfo;

     pInfo = GetFreeHandle();
     if( NULL == pInfo )
     {
         return -1;
     }

     pInfo->type = typeId;
     switch(typeId)
     {
         case PointType:
             pInfo->pData = malloc(sizeof(Point));
             ZeroMemory(pInfo->pData, sizeof(Point));
         break;

     }

     return pInfo->handle;
}

int DeleteType(Handle object)
{
    HandleInfo *pInfo = GetHandleInfo(object);

    if( NULL == pInfo  )
    {
        return -1;
    }

    if( pInfo->handle != 0 )
    {
        free(pInfo->pData);
        pInfo->pData = NULL;
        pInfo->handle = 0;
        return 1;
    }
    else
    {
        return 0; // Handle was already closed
    }
}

void *GetObjectOfCorrectType(Handle object, Types type)
{
    HandleInfo *p = GetHandleInfo(object);
    if( p == NULL )
    {
        return NULL;
    }

    if( p->type != type)
    {
        return NULL; // handle has wrong object type
    }

    return p->pData;
}

void IncrementX(Handle point)
{
    Point *pPoint = (Point *) GetObjectOfCorrectType(point, PointType);
    if( pPoint == NULL )
    {
        return;
    }

    pPoint->x++;
}

void PrintPoint(Handle point)
{
    Point *pPoint = (Point *) GetObjectOfCorrectType(point, PointType);
    if( pPoint == NULL )
    {
        return;
    }

    printf("Point has x: %d y: %d", pPoint->x, pPoint->y);
}
Run Code Online (Sandbox Code Playgroud)

你的,阿洛伊斯·克劳斯