Hello to all.
This is one of the drawbacks using generic pointers.
"There is no way to detect an error caused by pushing a pointer of the wrong type."
Is there any code example to see???? I didn't find something from google.
Thank you in advance
Hello to all.
This is one of the drawbacks using generic pointers.
"There is no way to detect an error caused by pushing a pointer of the wrong type."
Is there any code example to see???? I didn't find something from google.
Thank you in advance
I assume that since the void * doesn't enforce type, it might be hard to keep track of errors where you accidentaly pass a void pointer pointing to the wrong type of data.
For instance:
This is perfectly valid code, yet the program will crash inside the function initTest() because the second integer it tries to reference is out of bounds.Code:typedef struct Test Test; struct Test { int a, b; }; int initTest(void *vp) { Test *t = (Test *)vp; t->a = 20; t->b = 10; //crash } int main() { int a; void *vp = &a; initTest(vp); return 1; }
The first integer is the a from main not the a from the structure. So the b integer in the call of initTest is not a known variable? it has nothing to do with the b variable inside the structure Test?
also the return type of initTest is int but you return nothing.
Last edited by Mr.Lnx; 04-09-2015 at 03:04 PM.
C doesn't enforce type safety on pointers of any kind. A particular C implementation may issue warnings for obvious stupidity, but they are not required to.
What can this strange device be?
When I touch it, it gives forth a sound
It's got wires that vibrate and give music
What can this thing be that I found?
Given two types T1 and T2, compilers are required to issue diagnostics (i.e. warning/error messages) if you assign T1* to T2* if T1 and T2 are not compatible types. Without going into smaller details such as type qualifiers, function types, etc, two types are considered compatible if they are the same type.
So a compiler must issue a diagnostic for:
void* is a “hole” in the type system, because you can assign most pointer types to/from void* without a diagnostic. Casting is another such hole, since you can cast from (say) long* to int*, and the compiler doesn't have to issue a diagnostic.Code:int *x = NULL; long *z = x;
C's type system is pretty weak, because you can work around it in a number of ways, but it's not useless.
Ok I got it.
According to shiroaisu's example vp points to an integer variable. But inside the function *t is a pointer to a structure. So there is no way to check this inconsistency.
t->a is weird to me for this case though. Can you assign a value with this way to integer that is not member of a structure?
It should seem weird. It isn't normal to try and hack C's type system. But when you get down to how C really works, there isn't much stopping you from assigning to an int by pretending it's a structure.
C99 requires that objects be represented by a contiguous sequence of bytes.
Further down, the standard says:Originally Posted by section 6.2.6.1 paragraph 2
This makes it possible to do all sorts of wonderful things. For instance, any object can be punned into a sequence of unsigned chars -- (unsigned char*)&obj -- this is the reason that memcpy can work.4 Values stored in non-bit-field objects of any other object type consist of n x CHAR_BIT
bits, where n is the size of an object of that type, in bytes. The value may be copied into
an object of type unsigned char [n] (e.g., by memcpy); the resulting set of bytes is
called the object representation of the value. [...]
Let's simplify shiroaisu's example for discussion:5 Certain object representations need not represent a value of the object type. If the stored
value of an object has such a representation and is read by an lvalue expression that does
not have character type, the behavior is undefined. If such a representation is produced
by a side effect that modifies all or any part of the object by an lvalue expression that
does not have character type, the behavior is undefined.41) Such a representation is called
a trap representation.
6 When a value is stored in an object of structure or union type, including in a member
object, the bytes of the object representation that correspond to any padding bytes take
unspecified values.42) The value of a structure or union object is never a trap
representation, even though the value of a member of the structure or union object may be
a trap representation.
41) Thus, an automatic variable can be initialized to a trap representation without causing undefined
behavior, but the value of the variable cannot be used until a proper value is stored in it.
42) Thus, for example, structure assignment need not copy any padding bits.
Now, does the program exhibit undefined behavior or not? It does. Since the object representations of an integer and a struct T containing an integer do not have to be the same, there are a bunch of question marks around this. The program can appear to work correctly when the compiler decides that the way to represent the structure T is the same way to represent other integers. (After all, where exactly is the difference in bits.) However, it is also valid to allocate space for the integer and then some padding. If you're lucky, the computer would segfault in that case. A standards conforming compiler could try to access the padding, which would probably cause a crash.Code:struct T { int a; }; void foo(void *pointer) { struct T *t = pointer; t->a = 10; } int main() { int x = 1; foo(&x); }
The bad thing about undefined behavior is that tons of programs invoking it appear to work. Crashes are a good thing, a sign something is probably a mistake.
In the same vein, void pointers/casts are bad, for silently allowing things like this.
Last edited by whiteflags; 04-09-2015 at 07:53 PM.
Something not mentioned is that a void pointer does carry some type information, namely it will contain an address which is generally memory aligned for the object type it points to.
And in general a pointer should be memory aligned, by holding an address which is a multiple of the size of the type it is pointing to.
If you convert a pointer from one type to another and the resulting pointer is not memory aligned - that is undefined behaviour.
Here, as Whiteflags has mentioned, we can't be strictly sure of the size of the struct which means there is no way of knowing that the resulting pointer is memory aligned.
So that would indicate to me that:
And,Code:Test *t = (Test *)vp;
both exhibit undefined behaviour.Code:struct T *t = pointer;
Last edited by gemera; 04-10-2015 at 12:29 AM.
Unless there is a flaw in my argument?, it would mean that more generally - the conversion of a pointer to any primitive type to a pointer to a struct type results in undefined behaviour.
No, the conversion of a pointer to any primitive type to a pointer to a struct type does not necessarily result in undefined behaviour.
In fact, in C99, if the first member of that struct is of that primitive type, or of type char (which is required to have size 1 and alignment 1), the conversion itself does not result in undefined behaviour.
Let me show you the references.
In other words, the conversion is undefined behaviour only if the pointer is not sufficiently aligned. If the pointer is sufficiently aligned, there is no problem (in the conversion itself, at least).Originally Posted by C99 6.3.2.3 p 7
If the first element of the structure is of the primitive type (or if two structures are involved, their first elements are of the same type), the alignment will be sufficient.
For two structures with bitfields as first members, their units would have to be the same as well. The above says that a pointer to a structure must point to the first member in that structure, without any extra padding.Originally Posted by C99 6.7.2.1 p 13
Side note: This also means that if your linked list and tree node structures start with the pointers, i.e.
the first two members will be compatible across all three structures, and you only need one set of functions to manipulate any doubly linked list (or whatever the structure is), as long as the pointers in the actually used type are also at the same order at the beginning of the structure.Code:struct dlist { struct dlist *prev; struct dlist *next; }; struct dlist_a { struct dlist_a *prev; struct dlist_a *next; double value; }; struct dlist_b { struct dlist_b *prev; struct dlist_b *next; char data[]; };
It is frigging annoying to see all pointer tutorials put the structure pointers last in the structure.
But doesn't malloc automatically align memory blocks using the bigger type so that isn't a problem?If the first element of the structure is of the primitive type (or if two structures are involved, their first elements are of the same type), the alignment will be sufficient.
What you are saying would be correct for a normal "suitably converted" pointer.
By that I mean you have declared an actual structure variable and are then just creating a pointer to it.
e.g. say the structure was 8 bytes and contained an int of 4 bytes and 4 bytes padding.
Then the structure as I understand it would be aligned in terms of the 4 byte int and 8 byte structure.
So its address would be divisible by 4 and 8.
But in the examples we have been considering and for primitives in general as I see it you would definitely
have a pointer aligned to the size of the primitive, but then you are forcing that alignment on the structure.
So for example an arbitrary int address will be divisible by 4 but is not always divisble by 8 ( if we again
assume an 8 byte structure). So basically there is no guarantee that the int alignment is compatible
with the structure alignment.
Last edited by gemera; 04-11-2015 at 12:14 AM.
It's ok nominal - figured out where I went wrong.
The struct as a whole isn't aligned by its size - misread something.
As you probably know, malloc() returns a pointer sufficiently aligned for any standard type.
However, it does not apply to extended types -- extended as in extensions to C -- like __float128 or certain vector types, and that is truly problematic. (See GCC bug 55916 for real world issues related to it, and a workaround.)
I don't know. Mostly the latter standards either add new stuff (and new rules), or specify things that earlier versions left implementation-defined. (Sometimes implementation-defined behaviour has been changed to undefined behaviour, though.)
I rely on the publicly available versions (or final drafts) of the standards: C89 (ANSI C == C89 == C90), C99, C11, as linked to from the ANSI C, C99, and C11 (C standard revision) Wikipedia pages.
I do know that structure padding has not changed: See ANSI C 3.5.2.1, C99 6.7.2.1, C11 6.7.2.1. With regards to structure padding, they say the same thing: the pointer to a structure points to the first member; there cannot be any padding prior to the first member. (Same applies to unions, too: the pointer to an union points to the first member.)
Pointers have had one major change, related to the concept of pointer aliasing (strict aliasing). C99 added the restrict keyword.
Note that regardless of the C standards, there exists a lot of code that relies on specific implementation-defined behaviour. One example is interpreting the storage representation of one type as another type using an union -- for example, using an unsigned int or unsigned long member to examine the bit pattern of a float or double member. In C89, that was implementation-defined behaviour, but a lot of code expects it to work. In C99 and later, it is undefined behaviour. (You should use memcpy()/memmove() for this, with either an unsigned char array, or a specific-size unsigned integer type that matches the size of the other type.)
No worries!