I have code in my header file that looks like:
typedef struct _bn bnode;
I can do
bnode b;
just fine, but
b[i], where i is an int gives me the following error:
invalid use of undefined type ‘struct _bn’
Any ideas?
As stated, b is not an array and, as such, can not be accessed as one.
Also, how do you expect the compiler to figure out the size of that structure? When you do something like bnode b[i] a certain amount of space is to be set aside for later use. As you have it there no size.
What is your opacity intended to do for you? Maybe if you explain further what you are trying to accomplish you will get a more revealing answer...
As far as an API/library goes, normally if you're going to need an opaque structure, you don't allow the user of the API to declare things like arrays or static instances because of this. Not knowing anything about the structure is the name of the game so you're probably going to have to define some functions to manipulate them. Most C libraries that declare opaque structures often has accessor and modification functions.
One example is from Lua (obviously a Lua state is an single use structure but it's the idea):
typedef struct lua_State lua_State;
void lua_pushnumber(lua_State *s, lua_Number n);
In this case, if you decided you needed multiple Lua states, you would do something like the following:
lua_State *states[5];
for(int i = 0; i < 5; i++)
states[i] = lua_open();
I think the general rule-of-thumb is that if you're working with opaque structures, you're going to be working through pointers only, which is pretty much the only way to go about it anyway.
Sounds like you either want an opaque pointer/PIMPL implementation, or you should include the appropriate header file.
Structs in C++ are almost identical to classes, so the same techniques apply.
You can't define an array of opaque structs. If you do you get an error such as:
error: array type has incomplete element type
(the specific error text will vary; the one above is from gcc 4.4.1).
But what you can do is create an array of pointers to opaque structs. This is doable as the details of the struct do not affect the size of the pointer.
typedef struct _bn bnode;
bnode *b[20];
You have to at least know the size of bnode to be able to make an array of them.
You could do, in your opaque definition of bnode:
typedef struct bnode_struct {
uint8_t opaque_bytes[1024]; /* magically just "know" how big it is. */
} bnode;
Then you can do:
bnode b[10];
and it will work.
Related
I have been writing C for a decent amount of time, and obviously am aware that C does not have any support for explicit private and public fields within structs. However, I (believe) I have found a relatively clean method of implementing this without the use of any macros or voodoo, and I am looking to gain more insight into possible issues I may have overlooked.
The folder structure isn't all that important here but I'll list it anyway because it gives clarity as to the import names (and is also what CLion generates for me).
- example-project
- cmake-build-debug
- example-lib-name
- include
- example-lib-name
- example-header-file.h
- src
- example-lib-name
- example-source-file.c
- CMakeLists.txt
- CMakeLists.txt
- main.c
Let's say that example-header-file.h contains:
typedef struct ExampleStruct {
int data;
} ExampleStruct;
ExampleStruct* new_example_struct(int, double);
which just contains a definition for a struct and a function that returns a pointer to an ExampleStruct.
Obviously, now if I import ExampleStruct into another file, such as main.c, I will be able to create and return a pointer to an ExampleStruct by calling
ExampleStruct* new_struct = new_example_struct(<int>, <double>);,
and will be able to access the data property like: new_struct->data.
However, what if I also want private properties in this struct. For example, if I am creating a data structure, I don't want it to be easy to modify the internals of it. I.e. if I've implemented a vector struct with a length property that describes the current number of elements in the vector, I wouldn't want for people to just be able to change that value easily.
So, back to our example struct, let's assume we also want a double field in the struct, that describes some part of internal state that we want to make 'private'.
In our implementation file (example-source-file.c), let's say we have the following code:
#include <stdlib.h>
#include <stdbool.h>
typedef struct ExampleStruct {
int data;
double val;
} ExampleStruct;
ExampleStruct* new_example_struct(int data, double val) {
ExampleStruct* new_example_struct = malloc(sizeof(ExampleStruct));
example_struct->data=data;
example_struct->val=val;
return new_example_struct;
}
double get_val(ExampleStruct* e) {
return e->val;
}
This file simply implements that constructor method for getting a new pointer to an ExampleStruct that was defined in the header file. However, this file also defines its own version of ExampleStruct, that has a new member field not present in the header file's definition: double val, as well as a getter which gets that value. Now, if I import the same header file into main.c, which contains:
#include <stdio.h>
#include "example-lib-name/example-header-file.h"
int main() {
printf("Hello, World!\n");
ExampleStruct* test = new_example(6, 7.2);
printf("%d\n", test->data); // <-- THIS WORKS
double x = get_val(test); // <-- THIS AND THE LINE BELOW ALSO WORK
printf("%f\n", x); //
// printf("%f\n", test->val); <-- WOULD THROW ERROR `val not present on struct!`
return 0;
}
I tested this a couple times with some different fields and have come to the conclusion that modifying this 'private' field, val, or even accessing it without the getter, would be very difficult without using pointer arithmetic dark magic, and that is the whole point.
Some things I see that may be cause for concern:
This may make code less readable in the eyes of some, but my IDE has arrow buttons that take me to and from the definition and the implementation, and even without that, a one line comment would provide more than enough documentation to point someone in the direction of where the file is.
Questions I'd like answers on:
Are there significant performance penalties I may suffer as a result of writing code this way?
Am I overlooking something that may make this whole ordeal pointless, i.e. is there a simpler way to do this or is this explicitly discouraged, and if so, what are the objective reasons behind it.
Aside: I am not trying to make C into C++, and generally favor the way C does things, but sometimes I really want some encapsulation of data.
Am I overlooking something that may make this whole ordeal pointless, i.e. is there a simpler way to do this or is this explicitly discouraged, and if so, what are the objective reasons behind it.
Yes: your approach produces undefined behavior.
C requires that
All declarations that refer to the same object or function shall have compatible type; otherwise, the behavior is undefined.
(C17 6.2.7/2)
and that
An object shall have its stored value accessed only by an lvalue expression that has one of the following types:
a type compatible with the effective type of the object,
a qualified version of a type compatible with the effective type of the object,
[...]
an aggregate or union type that includes one of the aforementioned types among its members (including, recursively, a member of a
subaggregate or contained union), or
a character type.
(C17 6.5/7, a.k.a. the "Strict Aliasing Rule")
Your two definitions of struct ExampleStruct define incompatible types because they specify different numbers of members (see C17 6.2.7/1 for more details on structure type compatibility). You will definitely have problems if you pass instances by value between functions relying on different of these incompatible definitions. You will have trouble if you construct arrays of them, whether dynamically, automatically, or statically, and attempt to use those across boundaries between TUs using one definition and those using another. You may have problems even if you do none of the above, because the compiler may behave unexpectedly, especially when optimizing. DO NOT DO THIS.
Other alternatives:
Opaque pointers. This means you do not provide any definition of struct ExampleStruct in those TUs where you want to hide any of its members. That does not prevent declaring and using pointers to such a structure, but it does prevent accessing any members, declaring new instances, or passing or receiving instances by value. Where member access is needed from TUs that do not have the structure definition, it would need to be mediated by accessor functions.
Just don't access the "private" members. Do not document them in the public documentation, and if you like, explicity mark them (in code comments, for example) as reserved. This approach will be familiar to many C programmers, as it is used a lot for structures declared in POSIX system headers.
As long as the public has a complete definition for ExampleStruct, it can make code like:
ExampleStruct a = *new_example_struct(42, 1.234);
Then the below will certainly fail.
printf("%g\n", get_val(&a));
I recommend instead to create an opaque pointer and provide access public functions to the info in .data and .val.
Think of how we use FILE. FILE *f = fopen(...) and then fread(..., f), fseek(f, ...), ftell(f) and eventually fclose(f). I suggest this model instead. (Even if in some implementations FILE* is not opaque.)
Are there significant performance penalties I may suffer as a result of writing code this way?
Probably:
Heap allocation is expensive, and - today - usually not optimized away even when that is theoretically possible.
Dereferencing a pointer for member access is expensive; although this might get optimized away with link-time-optimization... if you're lucky.
i.e. is there a simpler way to do this
Well, you could use a slack array of the same size as your private fields, and then you wouldn't need to go through pointers all the time:
#define EXAMPLE_STRUCT_PRIVATE_DATA_SIZE sizeof(double)
typedef struct ExampleStruct {
int data;
_Alignas(max_align_t) private_data[EXAMPLE_STRUCT_PRIVATE_DATA_SIZE];
} ExampleStruct;
This is basically a type-erasure of the private data without hiding the fact that it exists. Now, it's true that someone can overwrite the contents of this array, but it's kind of useless to do it intentionally when you "don't know" what the data means. Also, the private data in the "real" definition will need to have the same, maximal, _AlignAs() as well (if you want the private data not to need to use AlignAs(), you will need to use the real alignment quantum for the type-erased version).
The above is C11. You can sort of do about the same thing by typedef'ing max_align_t yourself, then using an array of max_align_t elements for private data, with an appropriate length to cover the actual size of the private data.
An example of the use of such an approach can be found in CUDA's driver API:
Parameters for copying a 3D array: CUDA_MEMCPY3D vs
Parameters for copying a 3D array between two GPU devices: CUDA_MEMCPY3D_peer
The first structure has a pair of reserved void* fields, hiding the fact that it's really the second structure. They could have used an unsigned char array, but it so happens that the private fields are pointer-sized, and void* is also kind of opaque.
This causes undefined behaviour, as detailed in the other answers. The usual way around this is to make a nested struct.
In example.h, one defines the public-facing elements. struct example is not meant to be instantiated; in a sense, it is abstract. Only pointers that are obtained from one of it's (in this case, the) constructor are valid.
struct example { int data; };
struct example *new_example(int, double);
double example_val(struct example *e);
and in example.c, instead of re-defining struct example, one has a nested struct private_example. (Such that they are related by composite aggregation.)
#include <stdlib.h>
#include "example.h"
struct private_example {
struct example public;
double val;
};
struct example *new_example(int data, double val) {
struct private_example *const example = malloc(sizeof *example);
if(!example) return 0;
example->public.data = data;
example->val = val;
return &example->public;
}
/** This is a poor version of `container_of`. */
static struct private_example *example_upcast(struct example *example) {
return (struct private_example *)(void *)
((char *)example - offsetof(struct private_example, public));
}
double example_val(struct example *e) {
return example_upcast(e)->val;
}
Then one can use the object as in main.c. This is used frequently in linux kernel code for container abstraction. Note that offsetof(struct private_example, public) is zero, ergo example_upcast does nothing and a cast is sufficient: ((struct private_example *)e)->val. If one builds structures in a way that always allows casting, one is limited by single inheritance.
For the sake of simplicity I'm going to recreate the actual situation with dummy structures. I have this structure (not my code, I can't edit it):
// private_header_a.h
struct A_s{
int a1;
};
// header_a.h
typedef struct A_s A_t;
Then in one of my headers I extended it this way:
// my_header.h
typedef struct B_s{
A_t* a_f;
int b1;
} B_t;
Now, in my function, I have:
B_t* b;
// Initialization and some other code
b->b1 = 4; // Just an example and compiler does not give any error
// Some other code
b->a_f->a1;
This last line of code makes the compiler throw this error:
error: invalid use of incomplete typedef ‘A_t’ {aka ‘struct A_s’}
Where is the error?
EDIT: the piece of code that triggers the compiler has header_a.h and my_header.h included. private_header_a.h cannot be included directly as not installed (I should copy-paste it, but frankly I would like to avoid to do that)
The compiler error is probably intentional - the library's designers don't want you using A_t directly in that sort of way.
When a struct is only declared in a library's public header file, and only defined in the library's private implementation files, that means you're not supposed to know or care about its members or even size. So looking up that the struct has a member named a1 and writing b->af->a1 is not the intended use. This arrangement is called an "opaque handle". A few of its benefits are that the library keeps your application code from initializing or changing members in ways that don't make sense, and a future version of the library can change the names, numbers, and meanings of the members without breaking your application code.
(Also, how did you get a valid pointer for b->af without doing malloc(sizeof(A_t)) or similar? That sizeof would also cause a compiler error about the incomplete struct type.)
When a library uses an opaque handle, since you can't create any such objects yourself, it will typically provide functions that create the objects for you. Look for public functions in the library with names including init, create, open, etc. which return an A_t* pointer, and read their documentation. Usually there will also be a corresponding destroy, cleanup, close, etc. function which the program should call later when the library object is no longer needed. (In the case of some very simple handles, the function which creates the object might say instead you should just pass the pointer to free. But only do this if the documentation says to!)
Here's two fundamental rules:
Each c file is compiled separately
When you #include a file, think of it as directly replacing the #include line with the contents of the file being included.
Therefore, you are compiling a piece of source code that looks like this:
struct A_s {
int a1;
};
typedef struct B_s {
A_t* a_f;
int b1;
} B_t;
void foo() {
B_t* b;
}
This code doesn't know what A_t is. You've never defined that in the code that's visible to the compiler.
A trivial way to fix this is to replace A_t with struct A_s.
I'm currently a bit confused regarding the concept of information hiding of C-structs.
The backround of this question is an embedded c project with nearly zero knowledge of OOP.
Up until now I always declared my typedef structs inside the header file of the corresponding module.
So every module which wants to use this struct knows the struct type.
But after a MISRA-C check I discovered the medium severity warning: MISRAC2012-Dir-4.8
- The implementation of a structure is unnecessarily exposed to a translation unit.
After a bit of research I discovered the concept of information hiding of C-structs by limiting the visible access of the struct members to private scope.
I promptly tried a simple example which goes like this:
struct_test.h
//struct _structName;
typedef struct _structName structType_t;
struct_test.c
#include "struct_test.h"
typedef struct _structName
{
int varA;
int varB;
char varC;
}structType_t;
main.c
#include "struct_test.h"
structType_t myTest;
myTest.varA = 0;
myTest.varB = 1;
myTest.varC = 'c';
This yields the compiler error, that for main.c the size of myTest is unknown.
And of course it is, main.c has only knowledge that a struct of the type structType_t exists and nothing else.
So I continued my research and stumbled upon the concept of opaque pointers.
So I tried a second attempt:
struct_test.h
typedef struct _structName *myStruct_t;
struct_test.c
#include "struct_test.h"
typedef struct _structName
{
int varA;
int varB;
char varC;
}structType_t;
main.c
#include "struct_test.h"
myStruct_t myTest;
myTest->varA = 1;
And I get the compiler error: dereferencing pointer to incomplete type struct _structName
So obviously I haven't understood the basic concept of this technique.
My main point of confusion is where the data of the struct object will?
Up until now I had the understanding that a pointer usually points to a "physical" representation of the datatype and reads/writes the content on the corresponding address.
But with the method above, I declare a pointer myTest but never set an address where it should point to.
I took the idea from this post:
What is an opaque pointer in C?
In the post it is mentioned, that the access is handled with set/get interface methods so I tried adding one similiar like this:
void setVarA ( _structName *ptr, int valueA )
{
ptr->varA = valueA;
}
But this also doesn't work because now he tells me that _structName is unknown...
So can I only access the struct with the help of additional interface methods and, if yes, how can I achieve this in my simple example?
And my bigger question still remains where the object of my struct is located in memory.
I only know the pointer concept:
varA - Address: 10 - Value: 1
ptrA - Address: 22 - Value: 10
But in this example I only have
myTest - Address: xy - Value: ??
I have trouble understanding where the "physical" representation of the corresponding myTest pointer is located?
Furthermore I can not see the benefits of doing it like this in relatively small scope embedded projects where I am the producer and consumer of the modules.
Can someone explain me if this method is really reasonable for small to mid scale embedded projects with 1-2 developers working with the code?
Currently it seems like more effort to make all this interface pointer methods than just declaring the struct in my header-file.
Thank you in advance
My main point of confusion is where the data of the struct object will?
The point is that you do not use the struct representation (i.e. its size, fields, layout, etc.) in other translation units, but rather call functions that do the work for you. You need to use an opaque pointer for that, yes.
how can I achieve this in my simple example?
You have to put all the functions that use the struct fields (the real struct) in one file (the implementation). Then, in a header, expose only the interface (the functions that you want users to call, and those take an opaque pointer). Finally, users will use the header to call only those functions. They won't be able to call any other function and they won't be able to know what is inside the struct, so code trying to do that won't compile (that is the point!).
Furthermore I can not see the benefits of doing it like this in relatively small scope embedded projects where I am the producer and consumer of the modules.
It is a way to force modules to be independent of each other. Sometimes it is used to hide implementations to customers or to be able to guarantee ABI stability.
But yes, for internal usage, it is usually a burden (and hinders optimization, since everything becomes a black box to the compiler except if you use LTO etc.). A syntactic approach like public/private in other languages like C++ is way better for that.
However, if you are bound to follow MISRA to such degree (i.e. if your project has to follow that rule, even if it is only advisory), there is not much you can do.
Can someone explain me if this method is really reasonable for small to mid scale embedded projects with 1-2 developers working with the code?
That is up to you. There are very big projects that do not follow that advice and are successful. Typically a comment for private fields, or a naming convention, is enough.
As you've deduced, when using an opaque type such as this the main source file can't access the members of the struct, and in fact doesn't know how big the struct is. Because of this, not only do you need accessor functions to read/write the fields of the struct, but you also need a function to allocate memory for the struct, since only the library source knows the definition and size of the struct.
So your header file would contain the following:
typedef struct _structName structType_t;
structType_t *init();
void setVarA(structType_t *ptr, int valueA );
int getVarA(structType_t *ptr);
void cleanup(structType_t *ptr);
This interface allows a user to create an instance of the struct, get and set values, and clean it up. The library source would look like this:
#include "struct_test.h"
struct _structName
{
int varA;
int varB;
char varC;
};
structType_t *init()
{
return malloc(sizeof(structType_t ));
}
void setVarA(structType_t *ptr, int valueA )
{
ptr->varA = valueA;
}
int getVarA(structType_t *ptr)
{
return ptr->varA;
}
void cleanup(structType_t *ptr)
{
free(ptr);
}
Note that you only need to define the typedef once. This both defines the type alias and forward declares the struct. Then in the source file the actual struct definition appears without the typedef.
The init function is used by the caller to allocate space for the struct and return a pointer to it. That pointer can then be passed to the getter / setter functions.
So now your main code can use this interface like this:
#include "struct_test.h"
int main()
{
structType_t *s = init();
setVarA(s, 5);
printf("s->a=%d\n", getVarA(s));
cleanup(s);l
}
In the post it is mentioned, that the access is handled with set/get interface methods so I tried adding one similiar like this:
void setVarA ( _structName *ptr, int valueA )
{
ptr->varA = valueA;
}
But this also doesn't work because now he tells me that _structName is unknown...
The type is not _structName, but struct _structName or (as defined) structType_t.
And my bigger question still remains where the object of my struct is located in memory.
With this technique, there would be a method which returns the address of such an opaque object. It could be statically or dynamically allocated. There should of course also be a method to free an object.
Furthermore I can not see the benefits of doing it like this in relatively small scope embedded projects where I am the producer and consumer of the modules.
I agree with you.
I have two structures, with values that should compute a pondered average, like this simplified version:
typedef struct
{
int v_move, v_read, v_suck, v_flush, v_nop, v_call;
} values;
typedef struct
{
int qtt_move, qtt_read, qtt_suck, qtd_flush, qtd_nop, qtt_call;
} quantities;
And then I use them to calculate:
average = v_move*qtt_move + v_read*qtt_read + v_suck*qtt_suck + v_flush*qtd_flush + v_nop*qtd_nop + v_call*qtt_call;
Every now and them I need to include another variable. Now, for instance, I need to include v_clean and qtt_clean. I can't change the structures to arrays:
typedef struct
{
int v[6];
} values;
typedef struct
{
int qtt[6];
} quantities;
That would simplify a lot my work, but they are part of an API that need the variable names to be clear.
So, I'm looking for a way to access the members of that structures, maybe using sizeof(), so I can treat them as an array, but still keep the API unchangeable. It is guaranteed that all values are int, but I can't guarantee the size of an int.
Writing the question came to my mind... Can a union do the job? Is there another clever way to automatize the task of adding another member?
Thanks,
Beco
What you are trying to do is not possible to do in any elegant way. It is not possible to reliably access consecutive struct members as an array. The currently accepted answer is a hack, not a solution.
The proper solution would be to switch to an array, regardless of how much work it is going to require. If you use enum constants for array indexing (as #digEmAll suggested in his now-deleted answer), the names and the code will be as clear as what you have now.
If you still don't want to or can't switch to an array, the only more-or-less acceptable way to do what you are trying to do is to create an "index-array" or "map-array" (see below). C++ has a dedicated language feature that helps one to implement it elegantly - pointers-to-members. In C you are forced to emulate that C++ feature using offsetof macro
static const size_t values_offsets[] = {
offsetof(values, v_move),
offsetof(values, v_read),
offsetof(values, v_suck),
/* and so on */
};
static const size_t quantities_offsets[] = {
offsetof(quantities, qtt_move),
offsetof(quantities, qtt_read),
offsetof(quantities, qtt_suck),
/* and so on */
};
And if now you are given
values v;
quantities q;
and index
int i;
you can generate the pointers to individual fields as
int *pvalue = (int *) ((char *) &v + values_offsets[i]);
int *pquantity = (int *) ((char *) &q + quantities_offsets[i]);
*pvalue += *pquantity;
Of course, you can now iterate over i in any way you want. This is also far from being elegant, but at least it bears some degree of reliability and validity, as opposed to any ugly hack. The whole thing can be made to look more elegantly by wrapping the repetitive pieces into appropriately named functions/macros.
If all members a guaranteed to be of type int you can use a pointer to int and increment it:
int *value = &(values.v_move);
int *quantity = &(quantities.qtt_move);
int i;
average = 0;
// although it should work, a good practice many times IMHO is to add a null as the last member in struct and change the condition to quantity[i] != null.
for (i = 0; i < sizeof(quantities) / sizeof(*quantity); i++)
average += values[i] * quantity[i];
(Since the order of members in a struct is guaranteed to be as declared)
Writing the question came to my mind... Can a union do the job? Is there another clever way to automatize the task of adding another member?
Yes, a union can certainly do the job:
union
{
values v; /* As defined by OP */
int array[6];
} u;
You can use a pointer to u.values in your API, and work with u.array in your code.
Personally, I think that all the other answers break the rule of least surprise. When I see a plain struct definition, I assume that the structure will be access using normal access methods. With a union, it's clear that the application will access it in special ways, which prompts me to pay extra attention to the code.
It really sounds as if this should have been an array since the beggining, with accessor methods or macros enabling you to still use pretty names like move, read, etc. However, as you mentioned, this isn't feasible due to API breakage.
The two solutions that come to my mind are:
Use a compiler specific directive to ensure that your struct is packed (and thus, that casting it to an array is safe)
Evil macro black magic.
How about using __attribute__((packed)) if you are using gcc?
So you could declare your structures as:
typedef struct
{
int v_move, v_read, v_suck, v_flush, v_nop, v_call;
} __attribute__((packed)) values;
typedef struct
{
int qtt_move, qtt_read, qtt_suck, qtd_flush, qtd_nop, qtt_call;
} __attribute__((packed)) quantities;
According to the gcc manual, your structures will then use the minimum amount of memory possible for storing the structure, omitting any padding that might have normally been there. The only issue would then be to determine the sizeof(int) on your platform which could be done through either some compiler macros or using <stdint.h>.
One more thing is that there will be a performance penalty for unpacking and re-packing the structure when it needs to be accessed and then stored back into memory. But at least you can be assured then that the layout is consistent, and it could be accessed like an array using a cast to a pointer type like you were wanting (i.e., you won't have to worry about padding messing up the pointer offsets).
Thanks,
Jason
this problem is common, and has been solved in many ways in the past. None of them is completely safe or clean. It depends on your particuar application. Here's a list of possible solutions:
1) You can redefine your structures so fields become array elements, and use macros to map each particular element as if it was a structure field. E.g:
struct values { varray[6]; };
#define v_read varray[1]
The disadvantage of this approach is that most debuggers don't understand macros. Another problem is that in theory a compiler could choose a different alignment for the original structure and the redefined one, so the binary compatibility is not guaranted.
2) Count on the compiler's behaviour and treat all the fields as it they were array fields (oops, while I was writing this, someone else wrote the same - +1 for him)
3) create a static array of element offsets (initialized at startup) and use them to "map" the elements. It's quite tricky, and not so fast, but has the advantage that it's independent of the actual disposition of the field in the structure. Example (incomplete, just for clarification):
int positions[10];
position[0] = ((char *)(&((values*)NULL)->v_move)-(char *)NULL);
position[1] = ((char *)(&((values*)NULL)->v_read)-(char *)NULL);
//...
values *v = ...;
int vread;
vread = *(int *)(((char *)v)+position[1]);
Ok, not at all simple. Macros like "offsetof" may help in this case.
This question already has answers here:
Is it a good idea to typedef pointers?
(15 answers)
Closed 8 years ago.
In order to define a new datatype in C for example a type for linked list one can use one of the following definitions
struct list_node {
int x;
struct list_node * next;
};
//1
typedef struct list_node list1;
//2
typedef struct list_node *list2;
From what I have seen the common practice is the first definition.
The question is if the second definition is also an acceptable practice.
In which cases if any should one prefer the second over the first?
Provided that the variables that we use are pointers to struct list_node
is it possible to do the same operations with both types?
What are the advantages of the first?
I would require API users to type the "*", i.e. typedef the struct, not a pointer to the struct. This is the widely-used style in GLib, which is one of the more popular C stacks.
It works out well because it is important to know whether you have a pointer to a struct or the struct itself. For example, can you store NULL in a variable of the type? If you hide that an object is a pointer, you have to create a special NIL_NODE or other value to replace NULL. If you just make it a pointer, then people can treat it like one.
Another important benefit is the ability to put the actual struct definition somewhere private, i.e. in the .c file instead of in the header. You can put just the typedef in the header, while keeping the struct itself opaque to API users. This requires people to only use the exported methods that you provide to manipulate the struct. It also means you don't have to rebuild the world if you change the struct fields.
Yet another reason to take this path is that sometimes you do want a struct that can be copied, and you still want to typedef it. Take a thing like GdkPoint:
typedef struct {
int x, y;
} GdkPoint;
Here it's useful to allow direct struct access, like:
GdkPoint point = { 10, 10 };
GdkPoint copy = point;
do_stuff_with_point(©);
However, if your convention is that the "GdkPoint" typedef would be a pointer, you're going to have to be inconsistent.
Code is just clearer if you can tell what's a pointer and what isn't, and code is better encapsulated if you don't put struct definitions in the header (for structs that represent abstract, opaque data types, which is maybe the most common kind).
My default template for a C data type is something like this in the header:
typedef struct MyType MyType;
MyType* my_type_new(void);
void my_type_unref(MyType *t);
void my_type_ref(MyType *t);
Then the actual "struct MyType" in the .c file, along with the new, the unref, and any operations on the type.
The exception would be for types such as Point, Rectangle, Color where it's "plain old data" and direct field access is desired; another variation is to have a my_type_free() instead of _unref() if you don't need refcounting.
Anyhow, this is one style, basically the GLib style, that's widely-used and is known to work well.
I prefer the first style, so it's clear which types are objects, and which types are pointers. But I find C pointers easy to understand / interpret, which (apparently) plenty of people don't. So if you think most of the people reading your code base won't understand pointers, use typedef STRUCT_FOO *PSTRUCT_FOO everywhere, like the Win32 API does.
Consider using both of these:
typedef struct list_node Node;
typedef Node* ListHead;
As long as the identifier makes it clear it's a pointer then there's no harm (I personally prefer a suffix ala _Pointer or _Ptr, but whatever suits the project). That said, for C-only code there's no actual benefit in avoiding the explicit *. For mixed C/C++ environments or pure C++, I've often seen use of typedefs to pointers rationalised/justified re facilitating transitioning to, use of, and switching between smart pointer types. There's probably some truth to this, though I've never found myself wanting to adopt the practice.
typedef int* ptr_int;
ptr_int x,y,z;
Programmer may be confused that all three x, y, z are of int* type i.e.
Pointers to int.
But in Reality only x is of int* type y and z are simple int.
because ptr_int x, y, z; is equals to int* x, y, z;
not int*x, *y, *z;
so,
typedef also create confusion in programmers mind and may lead to bugs and errors.