-2

In api.h

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

In 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);
}

Other way I thought as,

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

But still I'm getting seg-fault error.


ok here is the problem with void*....

e.g. in 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-> ...);
}

in main.c

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

this it self should fail. as in main.c test function, x is void* ... init will allocate but in destroy I'm again passing void* .. which can be anything!

EDIT (Solved for me)

In api.h

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

In 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);
    }
code muncher
  • 1,592
  • 2
  • 27
  • 46
  • 3
    Please show your *actual* code, not re-written code (I can tell this isn't your real code because it couldn't possibly compile). – Oliver Charlesworth Apr 17 '12 at 00:29
  • 2
    Also, don't hide pointers inside typedefs; it's confusing! – Oliver Charlesworth Apr 17 '12 at 00:31
  • well my actual code is similar to this .. I can't upload that!!! and for Opaque type reason I'm using void* ... its confusing but need to do! :( – code muncher Apr 17 '12 at 00:33
  • 1
    You should create a 10-line test case that someone can paste into an editor, compile, run, and see the problem you're describing. – Oliver Charlesworth Apr 17 '12 at 00:34
  • I can't possibly see how you are seg.faulting on the second way: you allocate the required amount of memory, then access a member (containing garbage) – Attila Apr 17 '12 at 00:34
  • @Attila: It's possible (but unlikely) that `malloc` fails. – Oliver Charlesworth Apr 17 '12 at 00:35
  • @OliCharlesworth - true that, but unless the OP is allocating insane amount of memory elsewhere in the code, I don't think that would happen – Attila Apr 17 '12 at 00:36
  • @OliCharlesworth how can malloc can fail? – code muncher Apr 17 '12 at 00:41
  • The most common cause of `malloc()` failing is because you wrote to memory outside the bounds of what is currently allocated to you. For example, you allocated 16 bytes and wrote 32 bytes of data into the space, or allocated 16 bytes and then freed it, but then wrote over the 16 bytes that used to be allocated to you. – Jonathan Leffler Apr 17 '12 at 00:46
  • still didn't get it, can you explain little bit more? – code muncher Apr 17 '12 at 00:52
  • 1
    `char *x = malloc(16); memset(x, '\0', 32); char *y = malloc(256);` is one plausible way to get into trouble (not guaranteed, but plausible). Another is: `char *x = malloc(16); free(x); memset(x, '\0', 16); char *y = malloc(256);`. In both these examples, you are abusing memory by writing to memory that is not allocated to you. And when you do that, `malloc()` is very apt to crash on you. – Jonathan Leffler Apr 17 '12 at 00:55

2 Answers2

1

Version 0 — Critique of Question's Code

The posted code does not compile.

api.h

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

This defines hidden_my_type but not the my_type that is passed to do_something(). Presumably, you intended:

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

core.c

struct _my_type
{
    int a;
}

As noted below too, there is a semi-colon missing after the structure definition.

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

You have the hidden_my_type vs my_type problem again. You have the * of the pointer where it cannot go; it must go after the struct _my_type. You probably intended something like:

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

This is now syntactically correct (I think; I haven't actually run it past a compiler). You have not shown how it is used; indeed, since the user code has no way to generate a pointer to a valid structure, there is no way for this code to be used safely.

Your test code (unshown — why don't you show your test code) might look something like this:

#include "api.h"

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

Alternatively, it might not have the = 0 initializer in place. Either way, your code is unable to function sanely, and a core dump is almost inevitable. When you hide the structure from the user, you have to provide them with a mechanism to get hold of a valid (pointer to) the structure, and you've not done that.

Version 1

This is a better way to do it, because it is more nearly type-safe:

api.h version 1

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

core.c version 1

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

Note the added semi-colon, and the include of the api.h file.

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);
}

Version 2

Actually, we can debate the wisdom of hiding the pointer; I would prefer not to do so:

api.h version 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 version 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;
}

That's nice and clean. Of course, the user cannot allocate a struct my_type (only a pointer to it), so you need a function to allocate the structure for them. Think of the Standard C Library, and the FILE type, and fopen() to allocate and fclose() to release and fprintf() etc to manipulate the type. The my_type_initializer() is functioning as an analogue to fopen(), my_type_release() as an analogue to fclose(), and do_something() as an analogue to fprintf().

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • I agree with ou, but since other user can see that my_type is struct pointer (thought can't access the member!) I don't want that too... so I'm using void* – code muncher Apr 17 '12 at 00:38
  • Why is it a problem that they can see it is a pointer to some unidentified struct? The big trouble with `void *` in the interface is that you can get any random string, memory allocated by `malloc()`, or a pointer to any structure of any sort whatsoever passed to your function and the compiler won't whinge. It can't whinge, because you've told it to accept any pointer at all. With the mechanism here, your users are protected to some extent from their own mistakes. And they can't poke into the structure at all; there is no information on the client side to allow them to do so. It is all hidden. – Jonathan Leffler Apr 17 '12 at 00:42
  • yup ... make sense ... I completely forgot void* can be anything ... then there isn't anyway to use void* and not getting seg-fault! :( will see how it goes ... – code muncher Apr 17 '12 at 00:49
  • There are ways to use `void *` and not get a seg-fault. However, there are many more ways to (ab)use `void *` and get a run-time seg-fault instead of a compile-time error. Compile-time errors are much better than run-time crashes. Note, in particular, that you need to provide ways for the user to obtain valid pointers to your type, and to dispose of them afterwards. – Jonathan Leffler Apr 17 '12 at 00:51
0

Jonathan, you beat me to an answer, but this may be helpful as well. Here, api.c contains the (private) implementation, and api.h provides the interface to be consumed by other code such as 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

This code should compile and run:

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

name: five, number: 5
Adam Liss
  • 47,594
  • 12
  • 108
  • 150
  • 2
    I'm going to nit-pick because I'm good at nit-picking (though it's only a small nit-pick). You should not use `_api_h_` as the header guard; names starting with an underscore are reserved for the implementation. See §7.1.3 Reserved Identifiers in the C99 standard, bullet 2: _All identifiers that begin with an underscore are always reserved for use as identifiers with file scope in both the ordinary and tag name spaces._ Your choice of `_api_h_` therefore can run foul of the system's names. If you used `api_h_` or something else like that, no argument. – Jonathan Leffler Apr 17 '12 at 01:14
  • 1
    @JonathanLeffler: +1 for keeping me honest. It's been a long time since I've written "real" C code, and old habits die hard. Keep nit-picking. I'll learn. :-) – Adam Liss Apr 17 '12 at 01:15