The below program is having two structures. I don't understand how I can pass value from one structure variable to another structure variable while using pointers
#include <stdio.h>
typedef struct {
int c;
char d;
}bob;
typedef struct {
int c;
char d;
}anna;
//expecting 'bob' type variable
void fun(bob *var2)
{
printf("var2->c=%d\n",var2->c);
printf("var2->d=%c\n",var2->d);
}
int main()
{
anna var1;
var1.c=2;
var1.d='c';
fun(&var1);//passing 'anna' type pointer
return 0;
}
...but if I change the program to pass values using normal variables, it gives compilation error.
#include <stdio.h>
typedef struct {
int c;
char d;
}bob;
typedef struct {
int c;
char d;
}anna;
//expecting a variable of type 'bob'
void fun(bob var2)
{
printf("var2.c=%d\n",var2.c);
printf("var2.d=%c\n",var2.d);
}
int main()
{
anna var1;
var1.c=2;
var1.d='c';
fun(var1);//passing a variable of type 'anna'
return 0;
}
What is the logic behind this?
First of all, the first version, does warn you. with proper compiler options enabled, you'll see
source_file.c: In function ‘main’:
source_file.c:30:5: warning: passing argument 1 of ‘fun’ from incompatible pointer type
fun(&var1);//passing 'anna' type pointer
^
source_file.c:16:6: note: expected ‘struct bob *’ but argument is of type ‘struct anna *’
void fun(bob *var2)
That said, in the first version of the code
//expecting 'bob' type variable
is wrong!! Its ought to be (follow emphasis)
//expecting a pointer to 'bob' type variable
As per C rules, a pointer to a type can be converted to another type, but the result is not always defined. So, the first version of the code compiler (with warnings) and runs.
OTOH, in the second case, anna and bob are different types, so the inter-change is not possible while passing the argument and receiving the parameter. To the compiler, they are different types and compiler behaves accordingly.
Answer for beginner/intermediately experienced:
Any two structure types in C with different type names are not compatible types. No matter if they happen to have the same member variables. Similarly, pointers to different structure types are not compatible. So your types "bob" and "anna" are not compatible, and using them like you attempt is formally not safe nor well-defined.
Simply refrain from doing things like this, you will end up writing subtle bugs.
Answer for veterans:
Despite the type compatibility rules mentioned above, C (but not C++) allows some tricks to let you use one memory region in several different ways, no matter the type of the variables stored there. This is known as "type punning". Most often type punning is done through union, for example:
typedef struct
{
bob b;
anna a;
} bobanna;
If you would use a pointer to a type like the one above, you are actually allowed to assume that either of the two types can be used, but only because in this case, all the involved struct/union members contain (recursively) the very same types. For example, code like this is fine:
void fun (bobanna *var)
{
bob* b = var.b;
b.c = something;
}
bobanna ba = {.anna.c = 1 };
fun(&ba);
Please note that type punning is an advanced topic. It is very easy to do things like this in the wrong way - I would not recommend to even consider the above trick if you don't already know about pointer aliasing and the strict aliasing rule.
Related
I've read this thread and searched around for more information but most people just say "don't use typedefs like this." This way of doing things is appealing to me and I am also trying to learn new things, so I want to stick with this.
I'm using gcc version 9.2.1
Here is a minimum reproducible example of what I'm doing:
#define TABLE_SZ 10
typedef int vec_t[3];
typedef struct {
vec_t table_one[TABLE_SZ];
} tables;
void foo(vec_t * foo_vec) {
(*foo_vec)[0] = 0;
}
void bar (tables bar_table, vec_t bar_vec) {
foo(&(bar_table.table_one[0]));
foo(&bar_vec);
}
int main() {
vec_t vector;
foo(&vector);
}
/home/happy/CLionProjects/os_2019_p5/tests/test_array_typedef_ops.c: In function ‘bar’:
/home/happy/CLionProjects/os_2019_p5/tests/test_array_typedef_ops.c:18:7: warning: passing argument 1 of ‘foo’ from incompatible pointer type [-Wincompatible-pointer-types]
18 | foo(&bar_vec);
| ^~~~~~~~
| |
| int **
/home/happy/CLionProjects/os_2019_p5/tests/test_array_typedef_ops.c:12:18: note: expected ‘int (*)[3]’ but argument is of type ‘int **’
12 | void foo(vec_t * foo_vec) {
| ~~~~~~~~^~~~~~~
I assume this has something to do with "pointer decay", I guess bar converting bar_vec to int **?
Specifically, why is the type of &(bar_table.table_one[0]) int (*)[3] when bar_vec is int ** ?
I obviously don't want to supress all incompatible pointer type warnings in gcc.
Casting the pointer seems like a kludge.
I want to keep the typedef. I don't want to hide it in a struct.
Is there a compiler flag or other solution to tell the compiler to treat this situation as I expect it to?
Is there more information on this I should be aware of?
Thank you.
p.s. does stack overflow have pretty printing for compiler output?
void bar (tables bar_table, vec_t bar_vec) is bad in many ways:
You pass the struct by value which very ineffective and can't get optimized by the compiler if the function is in a different translation unit.
The typedef vec_t doesn't prevent your array type from getting adjusted ("decay") upon getting passed as parameter, so vec_t bar_vec is equivalent to int*. And this array is therefore not getting passed by value, just its address.
The reason for the compiler error is however this: foo(&bar_vec);. You pass a pointer to the int* you have hidden beneath the typedef, meaning that you pass an int**. The function however, expects a vec_t *, meaning an array pointer of type int(*)[3]. This is what the compiler is telling you.
There is no compiler flag to solve this, because the code doesn't make any sense. Solve this by getting rid of the typedef, then rewrite the code.
Suppose that one has the following code:
#include <stdio.h>
typedef int myIntType;
int main()
{
int a;
myIntType *ptr = &a;
*ptr = 1;
printf("%d\n", a);
return 0;
}
Does this invoke undefined behaviour?
Under any reasonable interpretation, I expect this should simply set the value of a to 1 and therefore print the line 1 on stdout.
Compiling with gcc -Wall -Wextra -pedantic main.c -o main on Mac OS X does not produce any errors or warnings, and produces the expected output on execution.
My question is purely theoretical, and I do not intend to use such a construct in real-life code.
From cppreference.com about the typedef specifier:
The typedef specifier ... may declare array and function types, pointers and references, class types, etc. Every identifier introduced in this declaration becomes a typedef-name, which is a synonym for the type of the object or function that it would become if the keyword typedef were removed.
In other words, it is the exact same as if you removed your custom type and replaced it with int. Thus, it is well-defined behavior. int and myIntType are 100% interchangeable.
That is actually a c++ reference. From K&R, chapter 6.7 on typedef:
C provides a facility called typedef for creating new data type names. For example, the declaration typedef int Length; makes the name Length a synonym for int. The type Length can be used in declarations, casts, etc., in exactly the same ways that the type int can be.
Do be aware that K&R isn't the most up-to-date standard. The other answer cites the current standard. As far as I know, typedef hasn't changed.
In your example, myIntType * is the same as int *.
Section 6.7.8 of the C standard gives an example of this:
4 EXAMPLE 1 After
typedef int MILES, KLICKSP();
typedef struct { double hi, lo; } range;
the constructions
MILES distance;
extern KLICKSP *metricp;
range x;
range z, *zp;
are all valid declarations. The type of distance is int, that of
metricp is "pointer to function with no parameter specification
returning int", and that of x and z is the specified structure; zp is
a pointer to such a structure. The object distance has a type
compatible with any other int object.
I just found a quirk in C that I find really confusing. In C it's possible to use a pointer to a struct before it has been declared. This is a very useful feature that makes sense because the declaration is irrelevant when you're just dealing with a pointer to it. I just found one corner case where this is (surprisingly) not true, though, and I can't really explain why. To me it looks like a mistake in the language design.
Take this code:
#include <stdio.h>
#include <stdlib.h>
typedef void (*a)(struct lol* etc);
void a2(struct lol* etc) {
}
int main(void) {
return 0;
}
Gives:
foo.c:6:26: warning: ‘struct lol’ declared inside parameter list [enabled by default]
foo.c:6:26: warning: its scope is only this definition or declaration, which is probably not what you want [enabled by default]
foo.c:8:16: warning: ‘struct lol’ declared inside parameter list [enabled by default]
To remove this problem we can simply do this:
#include <stdio.h>
#include <stdlib.h>
struct lol* wut;
typedef void (*a)(struct lol* etc);
void a2(struct lol* etc) {
}
int main(void) {
return 0;
}
The unexplainable problem is now gone for an unexplainable reason. Why?
Note that this question is about the behavior of language C (or possible the compiler behavior of gcc and clang) and not the specific example I pasted.
EDIT:
I won't accept "the order of declaration is important" as an answer unless you also explain why C would warn about using a struct pointer for the first time in a function argument list but allow it in any other context. Why would that possibly be a problem?
To understand why the compiler complains, you need to know two things about C "struct"s:
they are created (as a declared, but not yet defined, type) as soon as you name them, so the very first occurrence of struct lol creates a declaration
they obey the same "declaration scope" rules as ordinary variables
(struct lol { declares and then begins defining the structure, it's struct lol; or struct lol * or something else that does not have the open-brace that stops after the "declare" step.)
A struct type that is declared but not yet defined is an instance of what C calls an "incomplete type". You are allowed to use pointers to incomplete types, as long as you do not attempt to follow the pointer:
struct lol *global_p;
void f(void) {
use0(global_p); /* this is OK */
use1(*global_p); /* this is an error */
use2(global_p->field); /* and so is this */
}
You have to complete the type in order to "follow the pointer", in other words.
In any case, though, consider function declarations with ordinary int parameters:
int imin2(int a, int b); /* returns a or b, whichever is smaller */
int isum2(int a, int b); /* returns a + b */
Variables named a and b here are declared inside the parentheses, but those declarations need to get out of the way so that the the next function declaration does not complain about them being re-declared.
The same thing happens with struct tag-names:
void gronk(struct sttag *p);
The struct sttag declares a structure, and then the declaration is swept away, just like the ones for a and b. But that creates a big problem: the tag is gone and now you can't name the structure type ever again! If you write:
struct sttag { int field1; char *field2; };
that defines a new and different struct sttag, just like:
void somefunc(int x) { int y; ... }
int x, y;
defines a new and different x and y at the file-level scope, different from the ones in somefunc.
Fortunately, if you declare (or even define) the struct before you write the function declaration, the prototype-level declaration "refers back" to the outer-scope declaration:
struct sttag;
void gronk(struct sttag *p);
Now both struct sttags are "the same" struct sttag, so when you complete struct sttag later, you're completing the one inside the prototype for gronk too.
Re the question edit: it would certainly have been possible to define the action of struct, union, and enum tags differently, making them "bubble out" of prototypes to their enclosing scopes. That would make the issue go away. But it wasn't defined that way. Since it was the ANSI C89 committee that invented (or stole, really, from then-C++) prototypes, you can blame it on them. :-)
The compiler is warning you about a forward declaration of struct lol. C allows you to do this:
struct lol; /* forward declaration, the size and members of
struct lol are unknown */
This is most used when defining self-referencing structs, but it is also useful when defining private structs that are never defined in the header. Because of this latter use case, it is allowed to declare functions that receive or return pointers to incomplete structs:
void foo(struct lol *x);
However, just using an undeclared struct in a function declaration, as you did, will be interpreted as a local incomplete declaration of struct lol whose scope is constrainted to the function. This interpretation is mandated by the C standard, but it is not useful (there is no way to construct the struct lol to pass to the function) and is almost certainly not what the programmer intended, so the compiler warns.
This is because, in the first example, the struct is previously undefined and so the compiler tries to treat this first reference to that struct as a definition.
In general, C is a language where the order of your declarations matters. Everything you use should be properly declared in advance in some capacity, so that the compiler can reason about it when it's referenced in other context.
This is not a bug or a mistake in the design of the language. Rather, it's a choice that I believe was made to simplify the implementations of the first C compilers. Forward declarations allow a compiler to translate the source code serially in one pass (as long as some information such as sizes and offsets is known). If this weren't the case, the compiler would have be able to go back and forth in the program whenever it meets an unrecognized identifier, requiring its code emission loop to be much more complex.
I see this same warning before. My fix is to include the proper header file which contains the definition of the struct.
In addition to other answers, I would like to post a code example, which makes the problem more obvious. Please consider the following:
int testFunc(struct SomeStruct {int a; int b;} param1) // gcc: warning ...
{
return param1.a + param1.b;
}
int main(void)
{
struct SomeStruct params; // gcc: error: storage size of 'params' isn't known
params.a = 25;
params.b = 15;
return testFunc(params);
}
As you can see, the function declaration of testFunc is considered a valid C code (tested with GCC 12, Clang 15 and MSVC 19.32). However, you can't really use it because SomeStruct is only valid within the scope of the function, which is what compiler warns you about.
I've stumbled on this myself when working on my own C parser as the allowance of such syntax makes parser development easier as you can reuse the same implementation to parse struct declaration inside function parameter lists. It is surprising, however, that such bizarre syntax is still considered valid nowadays (as of C17) and instead of error you just get a warning.
Lookout for typos and DOUBLE CHECK your line numbers! I concat all my source files before compiling, so the line numbers from the source I am working in are meaningless. I have to take extra time to open up the concatted payload and examine it. So usually I don't and I just assume I know what line I am looking at from the console output message.
Example:
GCC Says:
EXAMPLE.C11:27:1: error: 'struct THIS_STRUCT_IS_OK' declared inside
parameter list [-Werror] ){
#include <stdio.h> //:for: printf(...)
struct THIS_STRUCT_IS_OKAY{
int whatever;
};
int LookingAtThisFunction(
struct THIS_STRUCT_IS_OKAY* arg
){
//: (Because you are not checking line numbers, you )
//: (assume you are looking here. But you are not. )
//: (Maybe you are concatenating all of your source )
//: (files together before compiling and line numbers )
//: (don't correspond to the original source and you )
//: (didn't examine your concatted source code payload?)
return( arg -> whatever );
}
//:You are not looking here because this is later in the
//:file, so the compiler would be complaining about the
//:FIRST usage of the struct, not the second one, you assume.
//:And you would be correct, if there wasn't a typo.
void WhereYouAreNotLooking(
struct THIS_STRUCT_IS_OK* arg
){
LookingAtThisFunction( arg );
}
int main( void ){
}
In summary: If you know what the error message means. And you swear to god
the compiler is broken because you already checked that...
1. Look for typos.
2. Make sure you are really looking at the correct line number.
I get that this is kinda stupid. But it had been scratching my head for
half an hour. So hopefully it helps someone who's already looked at the
obvious solutions.
The code below returns a warning message, I can't understand why and also couldn't find an answer among questions about the same warning message. I'll thank to get the right code and the explanation about the warning message.
The warning is:
WARNING: assignment from incompatible pointer type
The code is:
#include <stdio.h>
typedef struct sum {
int a,b,c;
} mytype;
mytype sum_operation, *ptr;
mytype sum(sum_operation) {
ptr = &sum_operation;
(*ptr).a = 1;
(*ptr).b = 3;
(*ptr).c =(*ptr).b + (*ptr).a ;
printf("%d\n",(*ptr).c);
}
int main(){
sum(sum_operation);
return 0;
}
the function declaration should be (you omitted the parameter type):
mytype sum(mytype sum_operation) {
mytype sum(sum_operation) {
Where is the type of sum_operation?
It might default to int like this. (For a reason why see #Gilles' comment and #AndreyT's answer). Of course this leads to a conflict with
ptr = &sum_operation;
which expects sum_operation to be of type mytype. So write:
mytype sum(mytype sum_operation) {
Also your function has no return statement, although it is declared to return a mytype.
Your sum function is declared with an old-style K&R declaration. I.e. you declared a single parameter sum_operation without specifying its type. A pre-C99 compiler accepts this declaration, implicitly treating your parameter as having type int. This is what leads to "incompatible type" problem.
It looks like you intended your parameter to have mytype type. In that case you have to explicitly tell the compiler about it. Either use a modern prototype-style function declaration with explicitly specified parameter type
mytype sum(mytype sum_operation)
{
or, if you insist on old-style declaration, specify the parameter type between function header and the first {
mytype sum(sum_operation)
mytype sum_operation;
{
Both styles are still legal in modern C, with the former being greatly superior to the latter. Your original declaration is illegal in modern C, meaning that it might be a good idea to configure your compiler to properly catch such errors at earlier stage.
As part of answering another question, I came across a piece of code like this, which gcc compiles without complaint.
typedef struct {
struct xyz *z;
} xyz;
int main (void) {
return 0;
}
This is the means I've always used to construct types that point to themselves (e.g., linked lists) but I've always thought you had to name the struct so you could use self-reference. In other words, you couldn't use xyz *z within the structure because the typedef is not yet complete at that point.
But this particular sample does not name the structure and it still compiles. I thought originally there was some black magic going on in the compiler that automatically translated the above code because the structure and typedef names were the same.
But this little beauty works as well:
typedef struct {
struct NOTHING_LIKE_xyz *z;
} xyz;
What am I missing here? This seems a clear violation since there is no struct NOTHING_LIKE_xyz type defined anywhere.
When I change it from a pointer to an actual type, I get the expected error:
typedef struct {
struct NOTHING_LIKE_xyz z;
} xyz;
qqq.c:2: error: field `z' has incomplete type
Also, when I remove the struct, I get an error (parse error before "NOTHING ...).
Is this allowed in ISO C?
Update: A struct NOSUCHTYPE *variable; also compiles so it's not just inside structures where it seems to be valid. I can't find anything in the c99 standard that allows this leniency for structure pointers.
As the warning says in the second case, struct NOTHING_LIKE_xyz is an incomplete type, like void or arrays of unknown size. An incomplete type can only appear in a struct as a type pointed to (C17 6.7.2.1:3), with an exception for arrays of unknown size that are allowed as the last member of a struct, making the struct itself an incomplete type in this case. The code that follows cannot dereference any pointer to an incomplete type (for good reason).
Incomplete types can offer some datatype encapsulation of sorts in C...
The corresponding paragraph in http://www.ibm.com/developerworks/library/pa-ctypes1/ seems like a good explanation.
The parts of the C99 standard you are after are 6.7.2.3, paragraph 7:
If a type specifier of the form
struct-or-union identifier occurs
other than as part of one of the above
forms, and no other declaration of the
identifier as a tag is visible, then
it declares an incomplete structure or
union type, and declares the
identifier as the tag of that type.
...and 6.2.5 paragraph 22:
A structure or union type of unknown
content (as described in 6.7.2.3) is
an incomplete type. It is completed,
for all declarations of that type, by
declaring the same structure or union
tag with its defining content later in
the same scope.
The 1st and 2nd cases are well-defined, because the size and alignment of a pointer is known. The C compiler only needs the size and alignment info to define a struct.
The 3rd case is invalid because the size of that actual struct is unknown.
But beware that for the 1st case to be logical, you need to give a name to the struct:
// vvv
typedef struct xyz {
struct xyz *z;
} xyz;
otherwise the outer struct and the *z will be considered two different structs.
The 2nd case has a popular use case known as "opaque pointer" (pimpl). For example, you could define a wrapper struct as
typedef struct {
struct X_impl* impl;
} X;
// usually just: typedef struct X_impl* X;
int baz(X x);
in the header, and then in one of the .c,
#include "header.h"
struct X_impl {
int foo;
int bar[123];
...
};
int baz(X x) {
return x.impl->foo;
}
the advantage is out of that .c, you cannot mess with the internals of the object. It is a kind of encapsulation.
You do have to name it. In this:
typedef struct {
struct xyz *z;
} xyz;
will not be able to point to itself as z refers to some complete other type, not to the unnamed struct you just defined. Try this:
int main()
{
xyz me1;
xyz me2;
me1.z = &me2; // this will not compile
}
You'll get an error about incompatible types.
Well... All I can say is that your previous assumption was incorrect. Every time you use a struct X construct (by itself, or as a part of larger declaration), it is interpreted as a declaration of a struct type with a struct tag X. It could be a re-declaration of a previously declared struct type. Or, it can be a very first declaration of a new struct type. The new tag is declared in scope in which it appears. In your specific example it happens to be a file scope (since C language has no "class scope", as it would be in C++).
The more interesting example of this behavior is when the declaration appears in function prototype:
void foo(struct X *p); // assuming `struct X` has not been declared before
In this case the new struct X declaration has function-prototype scope, which ends at the end of the prototype. If you declare a file-scope struct X later
struct X;
and try to pass a pointer of struct X type to the above function, the compiler will give you a diagnostics about non-matching pointer type
struct X *p = 0;
foo(p); // different pointer types for argument and parameter
This also immediately means that in the following declarations
void foo(struct X *p);
void bar(struct X *p);
void baz(struct X *p);
each struct X declaration is a declaration of a different type, each local to its own function prototype scope.
But if you pre-declare struct X as in
struct X;
void foo(struct X *p);
void bar(struct X *p);
void baz(struct X *p);
all struct X references in all function prototype will refer to the same previosly declared struct X type.
I was wondering about this too. Turns out that the struct NOTHING_LIKE_xyz * z is forward declaring struct NOTHING_LIKE_xyz. As a convoluted example,
typedef struct {
struct foo * bar;
int j;
} foo;
struct foo {
int i;
};
void foobar(foo * f)
{
f->bar->i;
f->bar->j;
}
Here f->bar refers to the type struct foo, not typedef struct { ... } foo. The first line will compile fine, but the second will give an error. Not much use for a linked list implementation then.
When a variable or field of a structure type is declared, the compiler has to allocate enough bytes to hold that structure. Since the structure may require one byte, or it may require thousands, there's no way for the compiler to know how much space it needs to allocate. Some languages use multi-pass compilers which would be able find out the size of the structure on one pass and allocate the space for it on a later pass; since C was designed to allow for single-pass compilation, however, that isn't possible. Thus, C forbids the declaration of variables or fields of incomplete structure types.
On the other hand, when a variable or field of a pointer-to-structure type is declared, the compiler has to allocate enough bytes to hold a pointer to the structure. Regardless of whether the structure takes one byte or a million, the pointer will always require the same amount of space. Effectively, the compiler can tread the pointer to the incomplete type as a void* until it gets more information about its type, and then treat it as a pointer to the appropriate type once it finds out more about it. The incomplete-type pointer isn't quite analogous to void*, in that one can do things with void* that one can't do with incomplete types (e.g. if p1 is a pointer to struct s1, and p2 is a pointer to struct s2, one cannot assign p1 to p2) but one can't do anything with a pointer to an incomplete type that one could not do to void*. Basically, from the compiler's perspective, a pointer to an incomplete type is a pointer-sized blob of bytes. It can be copied to or from other similar pointer-sized blobs of bytes, but that's it. the compiler can generate code to do that without having to know what anything else is going to do with the pointer-sized blobs of bytes.