pointer type casting  - Page 1
Forum section: electronics / general

author: Dadu@
author_rank: Contributor
timestamp: February 21, 2022, 07:31:29 pm
content: I am confused with casting in c language, after reading, I understood that converting one type to another type is called type castingI found that when we use void pointer or function pointer, casting is usedCode:#include <stdio.h>void function2(int x){printf("x = %d\n", x);}int main (){void (*f2)(int) = &function2;f2(2);return 0;}i understand that in this line only address of function is being assignedCode:void (*f2)(int) = &function2;I don't understand how one type is converted into another type in code?

author: Nominal Animal
author_rank: Super Contributor
timestamp: February 21, 2022, 08:00:11 pm
content: There are no casts in the program.The expressionvoid (*f2)(int) = &function2;does not contain a cast, although it might look like it at a quick glance. Instead, it declares a function pointer variable namedf2. The function it points to does not return anything, and takes a singleintas a parameter.Often, we typedef function types to make lives easier. In this case, we could name the typefunction_type,typedef void (function_type)(int);and then we can write the first expression asfunction_type *f2 = &function2;and because the function name alone is a pointer to that function, asfunction_type *f2 = function2;Code:#include <stdio.h>typedef void (function_type)(int);void function2(int x){printf("x = %d\n", x);}int main(void){function_type *f2 = function2;f2(2);return 0;}

author: DavidAlfa
author_rank: Super Contributor
timestamp: February 21, 2022, 08:11:41 pm
content: You're not converting anything actually, that's not casting.https://www.ibm.com/docs/en/i/7.2?topic=program-casting-pointersCasting is when using a pointer to force access as a different data type.This makes a pointer to a void function with (int) input parameter.Code:void (*f2)(int)Which is the same type as:Code:void function2(int x)So this basically copies the function address and tells the compiler to use it in the exact same way.Code:void (*f2)(int) = &function2;A very simple example of casting:(System endiannes type could cause different behavior)Imagine you want to point to the lower byte of a long, so it reads 0x44:Code:uint32_t long = 0x11223344;This would cause warnings, as the compiler expect same data types by default unless you force (Cast) it.Code:uint8_t * byte = &long;Casting:Code:uint8_t * byte = (uint8_t *)&long;Now, *byte would read 0x44 .Casting can be very useful.Imagine this buffer, where you stored raw dataCode:uint8_t buffer;Depending on the packet ID, it might contain different structures.So you could cast that array as a defined struct datatype and access it's contents easily.This would have to be done carefully, taking care of the struct padding, but its a good example to understand how it works.You determined the content is date type:Code:typedef struct{uint8_t year;uint8_t month;uint8_t day;uint8_t hour;uint8_t minute;uint8_t second;} date_t;So instead of parsing, copying.... You could do this:Code:date_t * date = (date_t *)buffer;printf("Year:%u, Month:%u, Day:%u \n", date->year, date->month, date->day);

author: Dadu@
author_rank: Contributor
timestamp: February 22, 2022, 04:47:14 am
content: I have written two test code and I hope type casting is being used in first codeCode:#include<stdio.h>int main (){int x = 1;void *ptr = &x;printf("%d", *(int*)ptr);return 0;}1Code:#include<stdio.h>int main (){int x = 1;void *ptr = &x;printf("%d", *ptr);return 0;}error: invalid use of void expressionI am trying to understand what's wrong with print statement in second code ? why first code gives expected output and why second doesn't ?

author: Ian.M
author_rank: Super Contributor
timestamp: February 22, 2022, 07:20:49 am
content: When you dereference a pointer, the dereferencing operator expression (e.g.*ptr) returns a value dependent on the type of the pointer. For consistency on a void* pointer it would have to return an object of void, (i.e. no value) which *CANNOT* be used anywhere that an expression having a value is needed. In fact the C99 standard says:QuoteTherefore at the point of compiling the evaluation of the printf() second argument the compiler has no way of knowing *what* sort of value your void pointer is pointing at, not even if it is a number, so cant use the Usual Arithmetic Conversions to convert it to whatever type printf() expects, and (6.3.2.2) forbids it from attempting to do so. Also as printf() is a variadic function with mutable argument types after the first, and the compiler cant 'sniff' inside the format string, it doesn't even know what type it *should* be converting it to.Edit: Corrected first line as there is more than one pointer dereferencing operator. e.g. apart from the ordinary*ptryou will encounterstructptr->structmemberas soon as you start learning about linked lists.

author: Nominal Animal
author_rank: Super Contributor
timestamp: February 23, 2022, 01:13:27 am
content: Ian.M provided a very detailed answer. To continue from that (and apologies for repeating some of what Ian.M wrote):The difference between the working expression,printf("%d", *(int*)ptr);and the non-working expression,printf("%d", *ptr);is the cast of the void pointer to a pointer to an int, before being dereferenced.Remember, when applied to a pointer type,*is the dereferencing operator: it "reaches through" the pointer, and grabs the value pointed by the pointer. The type of that value is defined by the pointer type.If we haveint *ptr1;float *ptr2;and we properly set them to point somewhere valid, then*ptr1is the integer thatptr1points to, and*ptr2is the float thatptr2points to.At this point, I'd like to repeat something I basically always tell those who are learning about pointers in C.We have three types of pointers:voidpointers, object pointers, and function pointers.Function pointers point to functions, and can be called like functions.Object pointers point to data of some type.Void pointers are those that do not specify a type at all. POSIX C states that void pointers can be cast to and from both object and function pointers, but standard C does not allow conversion between object and function pointers and does not allow casting a void pointer into a function pointer. This leads to some unfortunate language-lawyerism wrt.dlsym()on POSIXy systems (Linux, Android, Mac OS, BSDs) when portability is discussed. The reason for this is some historical ABIs had certain features that made the distinction useful. On AVR and ARM microcontrollers, object and function pointers are interchangeable. Those with Harvard architectures (separate address spaces for Flash and RAM) can use compiler-provided address space attributes; for example GCC and Clang have excellent support for these now (in 2022), although not all existing code (especially Arduino) use these very effectively.When reading an object or void pointer type or type specification, split it at each*. Start reading from right (starting at the variable name, if any), each split part at a time from right to left, replacing the*with"is a pointer to". This does not work for function pointers because their syntax is more complex, and you can argue how one should read qualifiers likeconstorvolatile, but it does give a pretty good intuitive understanding.For example,char *const *ptrcould be described in English as "ptris a pointer to const pointer to char". This means that it is an indirect pointer, it points to a pointer to char. The variableptritself may be modified, and the char or char array the indirect pointer points to may be modified as well, but the type includes a promise that we do not try to modify the pointer that points to the first char, i.e. we promise not to try and modify*ptr.Finally, array notation is mostly just a convenient form of pointer notation. If we havechar *sthat points to a string –– a string being an array ofcharterminated with a nul char'\0'––, then both*sandsrefer to the first character in that string. Similarly, given some integral type variablei(usuallysize_t,ssize_t, orint, althoughintmay be limited to a smaller range),si]and*(s + i)both refer to the same,i+1'th character of the string (sincei = 0is the first one).One quirk of C array notation, related to this, should only be used in Obfuscated C contests. It is thatiis the same asa, ifais an array or pointer expression, andiis an expression having some integral type. Thus, for example(x+w*y)is the same asa[x+w*y], ifx,y, andwhave some integral type, andais an array or a pointer or a pointer expression. This only leads to confusion, and should never be used. Nevertheless, knowing about this is useful, because if one accidentally writes such an expression, the compiler will not complain about it (because it is valid C), so it is important for us human programmers to detect these instead. (I would even go as far as rejecting code that relies on such, because if code is intended to be confusing, I definitely don't want anything to do with it.)

author: newbrain
author_rank: Super Contributor
timestamp: February 24, 2022, 08:20:40 am
content: Quote from: Nominal Animal on February 23, 2022, 01:13:27 amTo read complex (or simple) declarations, using thespiral mnemonic trickis still probably the best way.Unless one is really lazy, and just goes tohttps://cdecl.org

author: DiTBho
author_rank: Super Contributor
timestamp: February 24, 2022, 11:26:22 am
content: Type-Casting, and in particular Ponter-Type-Casting,should be used as little as possible.It makes tools and people confused, and it also violates QA rules of several certification checkers.The more Type-Casting you do in your code, the more effort you will have to put in to get your tools and customers and colleagues to understand what your code is supposed to do, and the code itself to pass QA checkers.

author: DavidAlfa
author_rank: Super Contributor
timestamp: February 24, 2022, 02:15:53 pm
content: It depends how to you use it.For example, casting a buffer transferred by another system might be very risky due different alignment/padding/endiannes.Correct me if I'm wrong, but within the same system, and taking proper care (Easier to say than done), it should be safe.Ex. Storing/reading a struct of bits/configs into/from a filesystem using read/write (uint8_t *) custom_struct, sizeof(custom_struct).In these operations you're casting between (uint8_t*) and (custom_struct *) types.Btw, I opened a project from 3-4 years ago, I felt happy after seeing how much I had improved since then.Spent some time optimizing it, looks much much better now.Yet I'm probably only capable of 15% of what any of you can do haha

author: eugene
author_rank: Frequent Contributor
timestamp: February 24, 2022, 03:26:27 pm
content: Quote from: DavidAlfa on February 24, 2022, 02:15:53 pmIn most (all?) cases that I've encountered, it was possible to use unions instead of casting pointers. This is a conversation that is out of my league, so I don't know if unions are inherently safer/preferable, but I tend to find them easier to understand without error.

author: DiTBho
author_rank: Super Contributor
timestamp: February 24, 2022, 04:59:56 pm
content: Quote from: eugene on February 24, 2022, 03:26:27 pmFor me: they are better

author: Nominal Animal
author_rank: Super Contributor
timestamp: February 24, 2022, 05:01:10 pm
content: Pointer casting is typically done betweenvoid *and/orunsigned char *, and object pointers.void *, or void pointers, are the "generic" pointer type. For example,malloc()/calloc()/realloc()return one. You do not need an explicit cast from void pointer to an object pointer in C: it is perfectly normal to do e.g.char *ptr = malloc(421);. Note, however, that you cannot do pointer arithmetic with void pointers, because they do not point to any specific type.unsigned char *is used with buffers. C99 6.2.6.1p4 explicitly allows copying the object representation of any type to anunsigned chararray using for examplememcpy(). If you ever need to do pointer arithmetic to find the address of some item in a buffer, you useunsigned char *pointers (instead of sayvoid *.)Typical C code only does pointer casts between the above two, or between an object pointer and one of the above pointer types. Casting sayfloat *to sayint *is always suspicious.To convert a pointer to an integral type, always useuintptr_t(unsigned) orintptr_t(signed), NEVERint, becauseintmay have insufficient range. Similarly, if you want to describe the length of an array in memory, you should usesize_tinstead ofint.Oh, andsizeof (char) == sizeof (signed char) == sizeof (unsigned char) == 1, says the C standard. If you want to know how many bits you have in an unsigned int, include<limits.h>and use(CHAR_BIT * sizeof (unsigned int)). See e.g. C99 6.2.6.1p4.Type punning, i.e. reinterpreting the object representation of some type as another type, is most portable if done using an union (see e.g. C99 6.5.2.3 footnote 82).That is,Code:float u32_to_float(uint32_t val){union {uint32_t u;float f;} temp = { .u = val };return temp.f;}uint32_t float_to_u32(float val){union {uint32_t u;float f;} temp = { .f = val };return temp.u;}

author: nctnico
author_rank: Super Contributor
timestamp: February 24, 2022, 07:33:19 pm
content: Quote from: DiTBho on February 24, 2022, 11:26:22 amIMHO type casting shouldn't be used at all. If it is needed, it shows the basic structure / architecture of the program has not been thought through well enough.

author: Nominal Animal
author_rank: Super Contributor
timestamp: February 25, 2022, 06:00:02 am
content: Quote from: nctnico on February 24, 2022, 07:33:19 pmNo, type casting, either implicit or explicit, between the pointer types I specified, is often required/needed/useful. An actual example:Code:#include <unistd.h>#include <errno.h>/* Async-signal-safe write() variant. Keeps errno unchanged.Retries if interrupted by signal delivery. Assumes the descriptor is blocking.Returns 0 if entire buffer written, errno error code otherwise.*/int write_all(int descriptor, const void *buffer, size_t length){const char *ptr = buffer;const char *const end = (const char *)buffer + length;ssize_t n;int saved_errno;if (descriptor == -1)return EBADF;if (length < 1)return 0;saved_errno = errno;while (ptr < end) {n = write(descriptor, ptr, (size_t)(end - ptr));if (n == -1) {const int err = errno;if (err == EINTR)continue;errno = saved_errno;return err;} elseif (n < 1) {errno = saved_errno;return EIO;}ptr += n;}errno = saved_errno;return 0;}The assignmentconst char *ptr = buffer;contains an implicit pointer type cast. In C, you can assign a void pointer to any object pointer, or vice versa, without an explicit cast. This assignment is therefore exactly the same asconst char *ptr = (const char *)buffer;.The assignmentconst char *const end = (const char *)buffer + length;requires an explicit cast, because we want a pointer to the byte just past the buffer, and pointer arithmetic cannot be done on void pointers. Technically, we could have usedconst char *const end = ptr + length;, in which case we would have relied on only the implicit pointer type cast.The rest of the function is just a standard retry loop, which understands both EINTR (interrupted) and short writes. The 'blocking' means non-nonblocking, i.e. that the descriptor is not in nonblocking mode.Note that one can use the void pointer implicit cast to avoid having to do any explicit casts, simply by casting to an intermediate void pointer type. That is why I do not consider implicit pointer type casts an exception: it is the initial (source) and final (target) types that matter, not the chain of type casts, implicit or explicit, used.(With the example function, the conversion is from whatever data type is to be written to the file descriptor or socket, to void pointer as the function argument, to char pointer so that pointer arithmetic can be used. Ifbufferwas a char pointer, then every caller would need to cast the pointer to the data they want to write explicitly to a char pointer.)

author: Siwastaja
author_rank: Super Contributor
timestamp: February 25, 2022, 07:02:33 am
content: Quote from: nctnico on February 24, 2022, 07:33:19 pmComplete BS. You can't write real-life C without type casting.Typical examples are malloc, buffers, and arithmetic operations where the result does not fit into the type of input operands (like multiplications). I'm 100% sure you are using implicit and explicit casting in these use cases all the time, yet you say differently.(Obviously, with arithmetic, you can avoid the explicit casting by creating enough temporary variables, but this isn't going to make code look or work any better. Classical "obfuscation to avoid X".)Pointer type casting is needed when writing high-quality, generic C code; examples being handler functions for buffers. Avoiding pointer type casting means writing beginner spaghetti code which results in longer code more prone to bugs.I'm surprised that you are this confused. I thought you would know better.

author: TheCalligrapher
author_rank: Regular Contributor
timestamp: March 02, 2022, 05:05:56 pm
content: Quote from: Dadu@ on February 21, 2022, 07:31:29 pmUm, no... The term "casting" is normally used when you force the conversion manually, explicitly, by using acastoperator. The language can also convert types implicitly - without a cast.Quote from: Dadu@ on February 21, 2022, 07:31:29 pmI'm not sure what you mean. There are no type conversions in this code at all. There are no casts.Quote from: Dadu@ on February 21, 2022, 07:31:29 pmNot assigned, butinitialized. And again, there's no casting in this code and no type conversions. What exactly are you asking about?

author: SiliconWizard
author_rank: Super Contributor
timestamp: March 02, 2022, 06:28:02 pm
content: Yes, unless it's explicit, I think the std talks about implicit conversion rather than implicit casting.As to pointers, the example above indeed does not require any conversion. Also, the '&' operator before the function name is useless here. It's allowed, but is unnecessary and (at least to me) hinders readability (but that may be personal preference.)On a related note, going to and from void * (something you sometimes have to do to write "generic" enough code) does not require any explicit cast, and using a cast in this context is often considered bad practice in C. It might be done by some to make source code compatible with C++, which is a whole other can of worms. I personally advise against it.

author: DiTBho
author_rank: Super Contributor
timestamp: March 02, 2022, 08:30:07 pm
content: Quote from: Siwastaja on February 25, 2022, 07:02:33 amThe firmware I wrote for industrial embroidery machine is about 7K lines of C. It's polymorphic, b*tree based with a kind of virtual-memory custom engine I wrote, and there are only 13 points where you can observe pointer/data type-casting13 / 7000 = 0.18% ----> that's what I mean with "in some few cases"