简体   繁体   English

使用typedef结构时的C设计风格

[英]C design style when using typedef structs

When one uses typedef structs within a source file and an external system will need to connect to this class via an interface, what is the correct way to handle the input? 当一个人在源文件中使用typedef结构并且外部系统需要通过接口连接到此类时,处理输入的正确方法是什么?

One example is if a source file inside a sub-system has 一个示例是,子系统内部的源文件是否具有

typedef struct
{
    double latitude;
    double longitude;
}
GPSLocation;

and an external class wants to use the following function 外部类想使用以下功能

void setupGPSSystem(char* navigationSystem, GPSLocation *start, GPSLocation *destination)

Lets say I abstract this using the interface and have a function which the external class calls. 可以说我使用接口对此进行了抽象,并具有外部类调用的功能。 Should the function take arguments of type GPSLocation(thus forcing the external system to #include the source file with the struct present, not so external now) or is it better to keep all typedef structs used within the sub-system and thus having an interface function as follows? 应的功能采取类型GPSLocation的参数(从而迫使外部系统到#include与该结构目前,现在不那么外部源文件)或者是它更好地保持副系统内使用的所有的typedef结构,因此具有接口功能如下?

void setupGPSSystem(char* navigationSystem, double startLat, double startLong, double destinationLat, double destinationLong)

You can declare the types in your interface's public header, and have the structure definitions hidden in your implementation source files. 您可以在接口的公共头文件中声明类型,并在实现源文件中隐藏结构定义。 So, you might have: 因此,您可能有:

gps_system.h: gps_system.h:

typedef struct GPSLocation GPSLocation;

void setupGPSSystem(char* navigationSystem, GPSLocation *start, GPSLocation *destination);

gps_system.c: gps_system.c:

struct GPSLocation
{
    double latitude;
    double longitude;
}

That way, you get the best of both worlds: your users can use meaningful types in the interface, and your implementation is private. 这样一来,您就可以充分利用这两个方面的优势:用户可以在界面中使用有意义的类型,而实现则是私有的。

In designing the module you need to decide whether users of this module need access to the fields within the structure directly, and this will determine where you end up defining the structure. 在设计模块时,您需要确定该模块的用户是否需要直接访问结构内的字段,这将确定最终在何处定义结构。 I'll describe a few different scenarios: 我将描述几种不同的情况:

1. A user (of the module) will directly manipulate the GPS data within your structure. 1.(模块的)用户将直接在您的结构内操纵GPS数据。 You have no choice but to define the structure in a header (.h) file as part of the API. 您别无选择,只能在头(.h)文件中定义该结构作为API的一部分。 The advantage is that this technique is simple and gives the user the ability to statically or dynamically allocate memory for your structure as required (since the structure's make-up is known to your other modules). 优点是该技术很简单,并且使用户能够根据需要为您的结构静态或动态分配内存(因为其他模块知道结构的组成)。 This disadvantage is that this technique does not hide your data; 缺点是该技术不会隐藏您的数据。 it can be intentionally or inadvertently corrupted by the user. 用户可能会有意或无意地破坏它。

2. A user does not care what a "GPS location" is, it will always use the module's functions to operate on your data structure. 2.用户不在乎“ GPS位置”是什么,它将始终使用模块的功能对您的数据结构进行操作。 In this case your structure can be opaque. 在这种情况下,您的结构可能是不透明的。 You declare it in the header (.h) file and define it in the source (.c) file (as covered by Graham's answer). 您可以在标头(.h)文件中声明它,并在源(.c)文件中定义它(如Graham的回答所述)。 The advantage is you get to hide all of your data which means a user can't easily corrupt it or (in the case of a proprietary library) understand how it has been implemented. 好处是您可以隐藏所有数据,这意味着用户无法轻易破坏它或(对于专有库而言)不能理解其实现方式。 The disadvantage is your module must manage the allocation (and freeing) of your opaque type (see point 3 below for an expansion of this idea). 缺点是您的模块必须管理不透明类型的分配(和释放)(有关此概念的扩展,请参见下面的第3点)。

gps_system.h gps_system.h

typedef struct _GPSLocation GPSLocation;

void setupGPSSystem(char* navigationSystem, GPSLocation *start, GPSLocation *destination);

gps_system.c gps_system.c

struct _GPSLocation
{
    double latitude;
    double longitude;
}

3. You'd like to give the user read-only access to some fields but to hide others. 3.您想授予用户对某些字段的只读访问权限,但隐藏其他字段。 This can be a useful technique for hiding implementation details and to prevent a user from corrupting your data while making it easy to access useful information. 这对于隐藏实现细节并防止用户破坏您的数据,同时使其易于访问有用信息是很有用的技术。 The disadvantage is you still need your module to manage allocation of the opaque type. 缺点是您仍然需要模块来管理不透明类型的分配。

gps_system.h gps_system.h

typedef struct
{
    double latitude;
    double longitude;    
}
GPSLocation;

/*  Create a new GPS object. */
void gps_new(GPSLocation **gps);

/*  Set the GPS object's current location. */
void gps_set(GPSLocation *gps, double latitude, double longitude);

/*  Calculate the distance from a GPS coordinate to a different location. */
void gps_distance(GPSLocation *gps, double latitude, double longitude);

/*  Free all memory allocated to a gps object. */
void gps_delete(GPSLocation *gps);

gps_system.c gps_system.c

struct GPSLocation_private
{
    GPSLocation gps_public;

    int field_0;
    int field_1;
};

/** Convert from the public version of the gps object object to the private. */
#define gps_get_private(gps_public)  ((struct GPSLocation_private *)(((char *)(gps_public)) - offsetof(struct GPSLocation_private, gps_public)))

void gps_new(GPSLocation **gps)
{
    struct GPSLocation_private *priv;

    priv = malloc(sizeof(struct GPSLocation_private));

    if (priv)
    {
        priv->field_0 = 1234;
        priv->field_1 = 4567;
        priv->gps_public.latitude = 1111;
        priv->gps_public.longitude = 2222;
        gps = &priv->gps_public;
    }
    else
    {
        *gps = NULL;
    }    
}

void gps_set(GPSLocation *gps, double latitude, double longitude)
{
    struct GPSLocation_private *priv;

    priv = gps_get_private(gps);

    /*  Do stuff with 'priv'. */
}

void gps_delete(GPSLocation *gps)
{
    struct GPSLocation_private *priv;

    priv = gps_get_private(gps);
    free(priv);
}

Your subsystem probably (ought to) have a header file defining the function prototypes other people use to access your system (the API). 您的子系统可能(应该)有一个头文件,该文件定义了其他人用来访问系统的函数原型(API)。 There is nothing wrong with also defining your struct in that header file, complete with typedef. 在该头文件中定义type完整的结构也没有错。 In fact it is very common. 实际上这是很常见的。 That way, providing your function prototype follows that prototype, you're ok. 这样,只要提供函数原型就可以了。 So this: 所以这:

#ifndef MYSUBSYSTEM_H
#define MYSUBSYSTEM_H

typedef struct
{
    double latitude;
    double longitude;
}
GPSLocation;

void setupGPSSystem(char* navigationSystem, 
                     GPSLocation *start, GPSLocation *destination);

#endif

Would make a good header. 会成为一个很好的标题。 Newline in the middle of your function to make it fit on SO. 函数中间的换行符使其适合SO。 #include that in the code that is linking to yours and you're set. #include到链接到您的代码中的代码,您就设置好了。

GPSLocation类型属于API,并且应位于API用户将#include的头文件(以.h结尾的文件)中。

您必须使用户#include带有接口函数原型的标头,因此您也可以将结构定义保留在该标头中。

It might not be such a hot idea to mention "class" in a question about C-language structs :-) Just to avoid any possible confusion with, say, C++. 在有关C语言结构的问题中提到“类”可能不是一个好主意:-)只是为了避免与C ++产生任何混淆。

You ask "Should the function take arguments of type GPSLocation(thus forcing the external system to #include the source file with the struct present, ... " 您询问“函数是否应采用GPSLocation类型的参数(从而强制外部系统#include结构存在的源文件,...”

The function as you've written it does not take arguments of type GPSLocation; 您编写的函数没有采用GPSLocation类型的参数; it takes arguments of type pointer to (ie, address of ) a GPSLocation struct, a very different and a wonderful thing. 它接受类型指针的参数指向 GPSLocation结构(即的地址 ),这是一件非常与众不同的事情。

And, very properly and correctly, you're not forcing anybody to #include anything except the header (.h) file that defines GPSLocation. 而且,非常正确正确地,您没有强迫任何人#include定义GPSLocation的标头(.h)文件之外的任何内容。 That's because in your function entry (as I understand it) you are expecting pointers to instantiations err, copies of those structs (start and end) as they exist in the calling program. 这是因为在函数入口(据我所知)中,您期望指向 实例化 err的指针 ,即调用程序中存在的这些结构的副本(开始和结束)。 You'll get at the members of each passed-by-reference struct through their addresses/pointers. 您将通过它们的地址/指针了解每个传递引用结构的成员。

This is by far the best way to pass structs in C -- via pointers to the calling program's copies of those structs. 到目前为止 ,这是在C中传递结构的最佳方法-通过指向那些结构的调用程序副本的指针。

Forget about the second choice you gave. 忘记您给的第二个选择。 Forever! 永远!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM