I am learning c in school , and having a little confusion on how to use type casting.
here is my code.
I am trying to figure out what type casting is and how it works.
I initialized a pointer(ptr3) that points the adress of k, then initialized ptr4 and assign ptr3 that is converted into int*.
but this does not seem like working, since it gives random values every time.
Why is it?
I appreciate any feedback ! thank you so much.
#include <stdio.h>
int main() {
char k = 10;
char* ptr3 = &k;
int* ptr4 = (int*) ptr3;
printf("*ptr3 = %d *ptr4 = %d\n", *ptr3, *ptr4);
return 0;
}
output is
*ptr3 = 10 *ptr4 = 1669824522
You have two undefined behaviour in one line.
When you dereference int * pointer you read outside the k object which is illegal.
Even if the k had enough size (for example is a char array), using the data as another type violates the strict aliasing rules - which is UB as well
Generally speaking, do not typecast pointers unless you really know what you are doing. If you want to convert byte array to integer use memcpy functionh.
A char is 1 byte in size, however, an integer is 4 bytes.
What you are doing is called Type Punning, where you are telling the compiler to not convert your char to an int and directly read 4 bytes from that same memory address while you are only allocating 1 byte. Those other 3 bytes could be anything.
The bytes of the char look like this:
10
Real simple, only one number. Integers need 3 more bytes than a char so here is what the integer could look like in bytes:
10 ? ? ?
The solution:
#include <stdio.h>
int main()
{
char k = 10;
int i = (int)k;
printf("k = %d i = %d\n", k, i);
return 0;
}
This code instead of directly reading the memory tells the compiler to convert the char to an int, filling in those 3 random bytes in the process with zeros. Here is what the variable i could look like in memory:
10 0 0 0
Those last 3 bytes were filled in with zeros and the integer is 10.
I hope this answer helps and I am open to feedback to improve it.
Related
I dont understand why the output of this code is 4 and not 16, Assuming that unsigned int takes 4 bytes and long int takes 8 bytes. Any help?
#include <stdio.h>
struct test {
unsigned int x;
long int y : 33;
unsigned int z;
};
int main()
{
struct test t;
unsigned int* ptr1 = &t.x;
unsigned int* ptr2 = &t.z;
printf("%d", ptr2 - ptr1);
return 0;
}
I assume you expected the output to be 16, because the offset between the pointers should be 16 bytes (assuming sizes are as you mentioned in your question).
But in c when you subtract pointers, you don't get the number of bytes but the number of elements. The elements are the data the pointers point to.
Since these are unsigned int pointers, and assuming sizeof(unsigned int) is 4, the difference between thr pointers is 4 elements.
However - as #Lundin commented above this theoretical claculation is not really relevant because subtrating pointers that do not point the same array is UB (undefined behavior).
BTW - Note when you add an integral value to a pointer, a similar thing happens on the other way: the result is the address of the pointer plus the integral value times the size of the element pointed by the pointer. E.g.: if you add 1 to unsigned int* ptr1, the resulting address will be higher than ptr1 by 4.
I'm playing with OnlineGDB compiler in order to understand how pointers work in C.
First, I ran the following code and got the output I expected:
int *array1[] = {1,4,3,4};
int main()
{
printf("%d \n", array1[0+1]);
printf("%d", array1[1+1]);
return 0;
}
Output was:
4
3
Secondly, I ran the following code - And I can't understand its output:
int *array1[] = {1,4,3,4};
int main()
{
printf("%d \n", array1[0]+1);
printf("%d", array1[1]+1);
return 0;
}
Output:
5
8
It seems like I'm adding 4 to the value from the array, but why? (each element in the array is consisted from a byte).
Thanks!
It seems like I'm adding 4 to the value from the array, but why?
No, each element of the array is an int * because you declared the array that way:
int *array1[] = {1,4,3,4};
That says array1 is an array whose values have type pointer to int. Remove the * if you want an array of int, like:
int array1[] = {1,4,3,4};
When you add or subtract from pointer types, the value changes by some multiple of the size of the type that the pointer refers to. An int on your system is probably 4 bytes, so an expression like array1[0]+1 gets the int * stored in array[0] and increments it, so it increases by sizeof(int).
(each element in the array is consisted from a byte).
Even if you had declared your array as an array of int rather than an array of int *, the size of an int is probably not 1 byte. int is typically 4 bytes long, but size depends on the compiler and target system.
With
int *array1[] = {1,4,3,4};
you are defining tan array of pointers to integers.
So every element of that array, even if initialized with what appear to be integer values, are actually pointers, that are addresses.
If you had deferenced those pointers you would probably have caused a segmentation fault. But fortunately you simply printed them, even if using %d instead of %p: a choice that according to C standard could have caused undefined behavior. Anyway in your case the pointers were converted to integers, and the printed values were the expected ones. Exactly like if the array was an array of integers.
In the second example you pushed it further: you added 1 to the array elements. But since they are pointers, then pointers arithmetics is applied. Meaning that pointer + N = pointer + N * sizeof(*pointer). In this case, since sizeof(int) is 4:
array[0] + 1 =
= 1 + sizeof (array[0]) =
= 1 + sizeof( int ) =
= 1 + 4 = 5
I wrote a pretty simple piece of C code:
int main(void) {
void *area = malloc(2 * sizeof(int));
unsigned int *int_one = (unsigned int *) area;
unsigned int *int_two = ((unsigned int *) area) + 3;
*int_two = 4293422034;
*int_one = 2;
printf("%u\n%u\n", *int_two, *int_one);
return 0;
}
sizeof(int) is 4 on my machine. Per my understanding, shouldn't the modification of memory at address int_one have an effect on the value stored at address int_two?
Modifying *int_one alters the first 4 bytes of mem. address area (perhaps not all 4, but enough to warrant the result I'm expecting?), and the integer at address int_two starts at the last byte of integer int_one.
So, shouldn't changing memory at int_one have an effect on memory at int_two?
Yet the printf call produces 4293422034 and 2 respectively.
I made sure to use unsigned variables to avoid confusion around 2s complement and negative values, and to use a small value for *int_one to warrant the change of its last byte (don't know if this is right?)
What am I not getting?
Operator '+', when applied to a pointer, increases the pointer n times the size of the object it points to. So increasing an int* by 3 does not add 3 bytes but 3*sizeof(int) bytes.
Pointer arithmetic is scaled by the size of the type the pointer points at. (sizeof(unsigned int) in your case). You'd need to cast to (char*) before adding 3 if you want to increase the address by 3 bytes, but converting that to an unsigned* pointer would incur undefined behavior by violating alignment requirements (6.3.2.3p7) and dereferencing the pointer would make the program even "more undefined" by violating strict aliasing rules (6.5p7).
To realy do this kind of type punning right, you'd need to use memcpy (or unions).
Example:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void) {
void *area = malloc(2 * sizeof(int));
if(!area) return EXIT_FAILURE;
unsigned int *int_one_p = area;
void *int_two_p = (((char*) area) + 3); /*saving this to an (unsigned*) would be UB*/
memcpy(int_two_p,&(unsigned){4293422034},sizeof(unsigned));
memcpy(int_one_p,&(unsigned){2},sizeof(unsigned));
unsigned one,two;
memcpy(&one,int_one_p, sizeof(one));
memcpy(&two,int_two_p, sizeof(two));
printf("%u\n%u\n", two, one);
return 0;
}
In this answer, the author discussed how it was possible to cast pointers in C. I wanted to try this out and constructed this code:
#include <stdio.h>
int main(void) {
char *c;
*c = 10;
int i = *(int*)(c);
printf("%d", i);
return 1;
}
This compiles (with a warning) and when I execute the binary it just outputs bus error: 10. I understand that a char is a smaller size than an int. I also understand from this post that I should expect this error. But I'd really appreciate if someone could clarify on what is going on here. In addition, I'd like to know if there is a correct way to cast the pointers and dereference the int pointer to get 10 (in this example). Thanks!
EDIT: To clarify my intent, if you are worried, I'm just trying to come up with a "working" example of pointer casting. This is just to show that this is allowed and might work in C.
c is uninitialized when you dereference it. That's undefined behaviour.
Likewise, even if c were initialized, your typecast of it to int * and then a dereference would get some number of extra bytes from memory, which is also undefined behaviour.
A working (safe) example that illustrates what you're trying:
int main(void)
{
int i = 10;
int *p = &i;
char c = *(char *)p;
printf("%d\n", c);
return 0;
}
This program will print 10 on a little-endian machine and 0 on a big-endian machine.
These lines of code are problematic. You are writing through a pointer that is uninitialized.
char *c;
*c = 10;
Change to something like this:
char * c = malloc (sizeof (char));
Then, the following line is invalid logic, and the compiler should at least warn you about this:
int i = *(int*)(c);
You are reading an int (probably 4 or 8 bytes) from a pointer that only has one byte of storage (sizeof (char)). You can't read an int worth of bytes from a char memory slot.
First of all your program has undefined behaviour because pointer c was not initialized.
As for the question then you may write simply
int i = *c;
printf("%d", i);
Integral types with rankes less than the rank of type int are promoted to type int in expressions.
I understand that a char is a smaller size than an int. I also understand from this post that I should expect this error. But I'd really appreciate if someone could clarify on what is going on here
Some architectures like SPARC and some MIPS requires strict alignment. Thus if you want to read or write for example a word, it has to be aligned on 4 bytes, e.g. its address is multiple of 4 or the CPU will raise an exception. Other architectures like x86 can handle unaligned access, but with performance cost.
Let's take your code, find all places where things go boom as well as the reason why, and do the minimum to fix them:
#include <stdio.h>
int main(void) {
char *c;
*c = 10;
The preceding line is Undefined Behavior (UB), because c does not point to at least one char-object. So, insert these two lines directly before:
char x;
c = &x;
Lets move on after that fix:
int i = *(int*)(c);
Now this line is bad too.
Let's make our life complicated by assuming you didn't mean the more reasonable implicit widening conversion; int i = c;:
If the implementation defines _Alignof(int) != 1, the cast invokes UB because x is potentially mis-aligned.
If the implementation defines sizeof(int) != 1, the dereferencing invokes UB, because we refer to memory which is not there.
Let's fix both possible issues by changing the lines defining x and assigning its address to c to this:
_Alignas(in) char x[sizeof(int)];
c = x;
Now, reading the dereferenced pointer causes UB, because we treat some memory as if it stored an object of type int, which is not true unless we copied one there from a valid int variable - treating both as buffers of characters - or we last stored an int there.
So, add a store before the read:
*(int*)c = 0;
Moving on...
printf("%d", i);
return 1;
}
To recap, the changed program:
#include <stdio.h>
int main(void) {
char *c;
_Alignas(in) char x[sizeof(int)];
c = x;
*c = 10;
*(int*)c = 0;
int i = *(int*)(c);
printf("%d", i);
return 1;
}
(Used the C11 standard for my fixes.)
Even after casting a void pointer, I am getting compilation error while dereferencing it.
Could anyone please let me know the reason of this.
int lVNum = 2;
void *lVptr;
lVptr = (int*)&lVNum;
printf("\nlVptr[60 ] is %d \n",lVptr[1]);
It doesn't make sense to dereference a void pointer. How will the compiler interpret the memory that the pointer is pointing to? You need to cast the pointer to a proper type first:
int x = *(int*)lVptr;
printf("\nlVptr[60 ] is %d \n", *(int*)lVptr);
This will cast the void pointer to a pointer to an int and then dereference it correctly.
If you want to treat it as an array (of one), you could do a slightly ugly ((int *)lVptr)[0]. Using [1] is out of bounds, and therefore not a good idea (as for lVptr[60]...)
It's still a void* because that's what you declared it as. Any pointer may be implicitly converted to a void*, so that cast does nothing and you are left with a pointer to void just as you began with.
You'll need to declare it as an int*.
void *some_ptr = /* whatever */;
int *p = (int*)some_ptr;
// now you have a pointer to int cast from a pointer to void
Note that the cast to an int* is also unnecessary, for the same reason you don't have to (and should not) cast the return value of malloc in C.
void*'s can be implicitly converted to and from any other pointer type. I added the cast here only for clarity, in your code you would simply write;
int *p = some_void_ptr;
Also, this:
lVptr[1]
Is wrong. You have a pointer to a single int, not two. That dereference causes undefined behavior.
You can not dereference a void pointer because it doesn't have a type,
first you need to cast it(int *)lVptr, then dereference it *(int *)lVptr.
int lVNum = 2;
void *lVptr;
lVptr = &lVNum;
printf("\nlVptr[60 ] is %d \n",*(int *)lVptr);
Example of what you might be trying to do:
#include <stdio.h>
int main () {
void *v;
unsigned long int *i = (unsigned long int *)v;
*i = 5933016743776703571;
size_t j = sizeof(i);
printf("There are %ld bytes in v\n", j);
size_t k;
for (k = 0; k < j; k++) {
printf("Byte %ld of v: %c\n", k, ((char *)v)[k]);
}
}
Output:
There are 8 bytes in v
Byte 0 of v: S
Byte 1 of v: T
Byte 2 of v: A
Byte 3 of v: C
Byte 4 of v: K
Byte 5 of v: O
Byte 6 of v: V
Byte 7 of v: R
A void pointer is just that, a pointer to a void (nothing definable).
Useful in some instances.
For example malloc() returns a void pointer precisely because it allocated memory for an UNDEFINED purpose.
Some functions may likewise take void pointers as arguments because they don't care about the actual content other than a location.
To be honest, the snippet you posted makes absolutely no sense, can't even guess what you were trying to do.
# Code-Guru
I tried to compile it in visual studio. It gives error - expression must be a pointer to complete object.
Thanks teppic,
As you suggested, the following compiles and yields right result.
#include<stdio.h>
void main(){
printf("study void pointers \n");
int lvnum = 2;
void *lvptr;
lvptr = &lvnum;
printf("\n lvptr is %d\n",((int *)lvptr)[0]);
}
However if I try printf("\n lvptr is %d\n",((int *)lVptr)[60]);
It compiles and runs but gives random number.
Thanks a lot, friends for all the suggestions. Apologies that I assigned a void pointer to unnecessarily casted int pointer and expected it to get dereferenced. However I should have casted it when I want to dereference it.
Purpose of the snippet:
In my sources I found klocwork error which was caused by similar situation. On the contrary the program not only compiled but also gave correct results. Reason- it is a low level code (no OS) where the memory assigned to the void pointer is already reserved till the count of like 60. But the klocwork tool was unable to parse the files having that limit resulting in error. I did a lot of brain storming and ended up in something silly.
Saurabh