【问题标题】:Typecasting (or deference) void * to struct foo*类型转换(或遵从) void * 到 struct foo*
【发布时间】:2012-04-28 08:04:13
【问题描述】:

在 api.h

typedef void* hidden_my_type;
void do_something(my_type x);

在 core.c 中

struct _my_type
{
    int a;
}

void do_something(hidden_my_type void_x)
{
   struct *_my_type x = void_x;  /*Don't understand is that correct way to do, as I'm getting segmentation fault error */
   printf("Value: %d\n", x->a);
}

我认为的其他方式,

struct *_my_type x = (struct _my_type *)malloc(sizeof(struct _my_type));
void_x = x
printf(Value: %d\n", x->a);

但我仍然收到 seg-fault 错误。


好的,这里是 void*.... 的问题。

例如 在 core.c 中

void init_my_type(hidden_my_type a)
{
   my_type *the_a = malloc(...);
   a = the_a   // <<<<<<<<<<<<<<<<<<<<<<<<<<<< is this correct?! a is void* and the_a // is original type
   pthread_cond_init(&the_a->...);
    .. (in short any other methods for init ..)
}
void my_type_destroy(my_hidden_type x)
{
    my_type *the_x = x;
    pthread_detroy(&the_x-> ...);
}

在 main.c 中

test()
{
   my_hidden_type x;
   init_my_type(x);
   .... 
   my_type_detroy(x);
}

它自己应该失败。在 main.c 测试函数中,x 是 void* ... init 将分配,但在销毁时我再次传递 void* .. 可以是任何东西!

编辑(为我解决)

在 api.h

typedef void* hidden_my_type;
void do_something(my_type x);

在 core.c 中

 struct _my_type
    {
        int a;
    }


 void init_hidden_type(hidden_my_type void_p_my_type)
    {
        struct _my_type *real_my_type = (struct _my_type *)malloc(sizeof(struct _my_type));
        //--- Do init for your type ---
        void_p_my_type = real_my_type;
    }


 void do_something(hidden_my_type void_x)
    {
       struct *_my_type x = void_x; 
       printf("Value: %d\n", x->a);
    }

【问题讨论】:

  • 请出示您的实际代码,而不是重新编写的代码(我可以说这不是您的真实代码,因为它不可能编译)。
  • 另外,不要在 typedef 中隐藏指针;很混乱!
  • 好吧,我的实际代码与此类似..我无法上传!!!并且出于不透明类型的原因,我正在使用 void* ...它令人困惑但需要这样做! :(
  • 您应该创建一个 10 行测试用例,有人可以将其粘贴到编辑器中,编译、运行并查看您描述的问题。
  • char *x = malloc(16); memset(x, '\0', 32); char *y = malloc(256); 是陷入麻烦的一种似是而非的方式(不保证,但似是而非)。另一个是:char *x = malloc(16); free(x); memset(x, '\0', 16); char *y = malloc(256);。在这两个示例中,您通过写入未分配给您的内存来滥用内存。当你这样做时,malloc() 很容易让你崩溃。

标签: c casting void-pointers


【解决方案1】:

版本 0 — 问题代码批判

发布的代码无法编译。

api.h

typedef void* hidden_my_type;
void do_something(my_type x);

这定义了hidden_my_type,但不是传递给do_something()my_type。大概,你打算:

typedef void *my_type;
void do_something(my_type x);

core.c

struct _my_type
{
    int a;
}

如下所述,结构定义后缺少分号。

void do_something(hidden_my_type void_x)
{
   struct *_my_type x = void_x;
   printf("Value: %d\n", x->a);
}

您又遇到了 hidden_my_typemy_type 的问题。你有指针的*,它不能去;它必须在struct _my_type 之后。你可能打算这样:

void do_something(my_type void_x)
{
   struct _my_type *x = void_x;
   printf("Value: %d\n", x->a);
}

这在语法上现在是正确的(我认为;我实际上并没有通过编译器运行它)。你还没有展示它是如何使用的;实际上,由于用户代码无法生成指向有效结构的指针,因此无法安全地使用此代码。

您的测试代码(未显示 - 为什么不显示您的测试代码)可能如下所示:

#include "api.h"

int main(void)
{
    my_type x = 0;
    do_something(x);
    return 0;
}

或者,它可能没有 = 0 初始化程序。无论哪种方式,您的代码都无法正常运行,并且核心转储几乎是不可避免的。当您向用户隐藏结构时,您必须为他们提供一种机制来获取有效的(指向)结构的(指针),而您还没有这样做。

版本 1

这是一种更好的方法,因为它更接近类型安全:

api.h 版本 1

typedef struct _my_type *my_type;
void do_something(my_type x);

core.c 版本 1

#include "api.h"
struct _my_type
{
    int a;
};

注意添加的分号,以及 api.h 文件的包含。

void do_something(my_type x)
{
    // Now you don't have to do casting here!
    //struct *_my_type x = void_x;  /*Don't understand is that correct way to do, as I'm getting segmentation fault error */
    printf("Value: %d\n", x->a);
}

版本 2

实际上,我们可以讨论隐藏指针的智慧;我宁愿不这样做:

api.h 版本 2

#ifndef API_H_INCLUDED
#define API_H_INCLUDED

typedef struct my_type my_type;
extern void     do_something(my_type *x);
extern my_type *my_type_initializer(void);
extern void     my_type_release(my_type *x);

#endif /* API_H_INCLUDED */

core.c 版本 2

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

struct my_type
{
    int a;
};

void do_something(my_type *x)
{
    printf("Value: %d\n", x->a);
}

my_type *my_type_initializer(void)
{
    my_type *x = malloc(sizeof(*x));
    x->a = 57;  // More plausibly, this would be 0
    return x;
}

void my_type_release(my_type *x)
{
    free(x);
}

main.c

#include "api.h"

int main(void)
{
    my_type *x = my_type_initializer();
    do_something(x);
    my_type_release(x);
    return 0;
}

这很好,很干净。当然,用户不能分配一个struct my_type(只有一个指向它的指针),所以你需要一个函数来为他们分配结构。想想标准 C 库,FILE 类型,fopen() 分配,fclose() 释放,fprintf() 等操作类型。 my_type_initializer()fopen() 类似,my_type_release()fclose() 类似,do_something()fprintf() 类似。

【讨论】:

  • 我同意你的观点,但由于其他用户可以看到 my_type 是结构指针(认为无法访问成员!)我也不想要那个......所以我正在使用 void *
  • 为什么他们可以看到它是指向某个未识别结构的指针?接口中void * 的最大问题是您可以获得任何随机字符串、malloc() 分配的内存或指向传递给您的函数的任何类型的任何结构的指针,编译器不会抱怨。它不能抱怨,因为你已经告诉它接受任何指针。有了这里的机制,您的用户在一定程度上可以避免自己的错误。他们根本无法进入结构;客户端没有信息允许他们这样做。都是隐藏的。
  • 是的......有道理......我完全忘记了 void* 可以是任何东西......那么无论如何都没有使用 void* 并且不会出现段错误! :( 看看情况如何......
  • 有一些方法可以使用void * 而不会出现段错误。但是,还有更多方法可以(ab)使用void * 并获得运行时段错误而不是编译时错误。编译时错误比运行时崩溃要好得多。请特别注意,您需要为用户提供获取指向您的类型的有效指针的方法,并在之后处理它们。
【解决方案2】:

乔纳森,你的答案比我好,但这也可能有帮助。这里,api.c 包含(私有)实现,api.h 提供了供其他代码使用的接口,例如main.c

// main.c: uses only the public interface to the private code
#include "api.h"

int main(int argc, char *argv[]) {
  void *foo;

  foo = create_foo("five", 5);
  print_foo(foo);
  delete_foo(foo);
}
// EOF main.c


// api.h: the public interface
#ifndef _api_h_
#define _api_h_
void *create_foo(char *name, int number);
void print_foo(void *foo);
void delete_foo(void *foo);
#endif // _api_h_


// api.c: the private implementation
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// The real structure is private to the implementation.
typedef struct {
        char name[20];
        int number;
} real_struct;

// Create a new structure, initialize, return as ptr-to-void.
void *create_foo(char *name, int number) {
  real_struct *s = malloc(sizeof(real_struct));
  strcpy(s->name, name);
  s->number = number;
  return (void *) s;
}

// Print the data.
void print_foo(void *foo) {
  real_struct *s = (real_struct *) foo;
  printf("name: %s, number: %d\n", s->name, s->number);
}

// Release the memory.
void delete_foo(void *foo) {
  free(foo);
}
// EOF api.c

这段代码应该编译并运行:

$ gcc -o foo main.c api.c
$ ./foo

name: five, number: 5

【讨论】:

  • 我要吹毛求疵,因为我擅长吹毛求疵(虽然它只是一个小的吹毛求疵)。您不应该使用_api_h_ 作为标题保护;以下划线开头的名称保留用于实现。请参阅 C99 标准中的第 7.1.3 节保留标识符,第 2 条:以下划线开头的所有标识符始终保留用作普通和标记名称空间中具有文件范围的标识符。 您的选择的_api_h_ 因此可能会与系统名称发生冲突。如果您使用api_h_ 或其他类似的东西,则无需争论。
  • @JonathanLeffler:+1 让我诚实。我已经很久没有编写“真正的”C 代码了,旧习惯很难改掉。继续吹毛求疵。我会学习的。 :-)
猜你喜欢
  • 2013-03-05
  • 2013-09-23
  • 2011-06-16
  • 2015-03-12
  • 2023-03-16
  • 1970-01-01
  • 2020-06-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多