Cannot get pointers result - c

#include<stdio.h>
void main()
{
int v=10;
char *p=&v;
int i;
for(i=0;i<10;i++,p++)
{
++(*p);
printf("%d",v);
}
}
Output
11
267
65803
16843019
16843019
16843019
I am not getting how output came like this please explain

I can only assume, that an expected behavior is to get variable v incremented 10 times using pointer.
If that's correct, you have two mistakes:
Type of pointer should be the same with the data you're pointing. If you're pointing at int variable, you should use int * pointer.
In the for loop condition: at each iteration you're incrementing both i and p (i++,p++).
When you're incrementing pointer, it moves to the next memory cell (in simple words, actually it's a bit complicated).
If you want to work with variable v only, you should not modify the pointer itself, only the variable it refers to.
Thus, if you'll remove p++ part , you'll get 11, 12, 13, ... as a result.
Why it shows such a weird results now? Just because at each iteration you're changing pointer (thus it refers to other memory cell). Memory that pointer refers to after increment may contain random data, which we are able to see. However, such an approach contains undefined behavior, and results may vary. It may even end with termination of the program.
However, it's indeed not clear what behavior are you expecting to get, and if you'll clarify that more, I guess community will be able to help you more.

I am not getting how output came like this please explain
First let's make some minor changes to your code and print the values in hex:
int main() {
int v = 10;
char *p = (char*)&v;
int i;
printf("%8d (0x%08x)\n", v, v);
for(i=0; i<sizeof(i); i++, p++)
{
++(*p);
printf("%8d (0x%08x)\n", v, v);
}
return 0;
}
Output:
10 (0x0000000a)
11 (0x0000000b)
267 (0x0000010b)
65803 (0x0001010b)
16843019 (0x0101010b)
So what happens here is that the int is four bytes - consequently I get 4 values printed by the loop (plus the print before the loop).
Since p is a char pointer and my system is little endian, p will first point to the LSB (least significant byte) of the integer, i.e. "0a", and increment that byte to "0b".
When p is incremented by p++ it will point to the next byte, i.e. "00" and increment that byte to "01". So now the integer holds "0000010b" (267 decimal). This step is repeated twice so that the integer first become "0001010b" (65803 decimal) and then "0101010b" (16843019 decimal).
In memory it looks like:
After initialization: 0a 00 00 00
^
|
p
After loop 1: 0b 00 00 00
^
|
p
After loop 2: 0b 01 00 00
^
|
p
After loop 2: 0b 01 01 00
^
|
p
After loop 4: 0b 01 01 01
^
|
p
BTW: Notice that the standard gives no guarantees about this behavior. Updating bytes inside an integer using a char pointer is not well defined by the standard.

Related

Does unasigned pointers have bogus address?

So i am doing CS50 lecture 4 Memory.
David says we have to assign a address when we declare int *x; as a pointer in order to store a value (Ex 45, 23 etc) in it. He also says if you don't initialize a pointer and then try to put a value in it by de referencing it as shown for y below you are asking the computer to store the value in a bogus address or random address?
int main(void)
{
int *x;
int *y;
x = malloc(sizeof(int));
*x = 42;
*y = 13;
}
But what is a bogus address? When i declare int *x; does that already have an address in it? How is that possible? I understand that the memory location where pointer x's value will be stored might have some remnants of prev operations but i don't understand how there can be an address there.
First, remember that x and y are variables that exist independently of whatever they point to. The initial values of x and y are indeterminate - they could be 0x00000000, they could be 0xdeadbeef, they could be a bit pattern that doesn't correspond to a valid address value at all.
The space for the x and y variables has to be taken from somewhere, and since memory isn't infinite, memory locations get reused; some memory locations get reused a lot. Memory doesn't automatically get erased1 when you're done with it in most implementations, so when you create a new object, it will contain the bit pattern of whatever was last written to those bytes2.
C has a concept of a lifetime for objects, which is the period of your program's execution where storage is guaranteed to be reserved for that object. A pointer is valid if it stores the address of an object during that object's lifetime. Valid pointer values are obtained in one of two ways:
using the & operator on an object during that object's lifetime
calling malloc, calloc, or realloc, to dynamically allocate space for an object, as you do for x3.
For example:
void foo( void )
{
int *ptr; // ptr is initially indeterminate and invalid
for ( int i = 0; i < 10; i++ )
{
ptr = &i; // i's lifetime is each iteration of the for loop;
printf( "%d = %d\n", *ptr, i ); // ptr is valid within the loop;
}
// ptr still stores the address of i, but i's lifetime has ended,
// so ptr is *no longer valid* - attempting to read or write it now
// will lead to undefined behavior
}
After i's lifetime has ended, the space that was reserved for it can be used by something else. If we try to read or write to it through ptr after the loop has finished the result may not be what we expect. The behavior of doing this is undefined, meaning the compiler and runtime environment aren't required to handle the situation in any particular way. It may work as we expect, we may corrupt data somewhere, we may cause a runtime error, or anything else can happen.
Similarly, executing
*y = 13;
in your program will have undefined behavior, because y does not store the address of an object in your program during that object's lifetime. Literally anything can happen at this point - your program can appear to work as intended, you can corrupt data elsewhere in your program, you can cause your program to branch off into a random function, you can cause a runtime error, or literally anything else can happen. And the result can be different each time you run it.
Edit
Addressing a question in the comments:
Are you referring to pointers here? Can pointers be considered as object? or is it just the ints and chars that are to be called as object?
Yes, the pointer variables x and y are objects (in the C sense that they're regions of memory that can store values). To better illustrate this, I wrote the following:
#include <stdio.h>
#include <stdlib.h>
#include "dumper.h"
int main( void )
{
int *x;
int *y;
int a;
char *names[] = { "a", "x", "y", "*x", "*y" };
void *addrs[] = { &a, &x, &y, NULL, NULL };
size_t sizes[] = { sizeof a, sizeof x, sizeof y, sizeof *x, sizeof *y };
puts( "Initial states of a, x, and y:" );
dumper( names, addrs, sizes, 3, stdout );
x = calloc( 1, sizeof *x ); // makes sure *x is initialized to 0
if ( x )
{
addrs[3] = x;
puts( "States of a, x, and y after allocating memory for x" );
dumper( names, addrs, sizes, 4, stdout );
*x = 0x11223344;
puts( "States of a, x, y, and *x after assigning *x" );
dumper( names, addrs, sizes, 4, stdout );
}
y = &a;
addrs[4] = y;
puts( "States of a, x, y, *x, and *y after assigning &a to y" );
dumper( names, addrs, sizes, 5, stdout );
*y = 0x55667788;
puts( "States of a, x, y, *x, and *y after assigning to *y" );
dumper( names, addrs, sizes, 5, stdout );
free( x );
return 0;
}
dumper is a little utility I wrote to dump the address and contents of the objects to a specified output stream.
After building and running the code, I get this output for the initial states of my variables:
Initial states of a, x, and y:
Item Address 00 01 02 03
---- ------- -- -- -- --
a 0x7ffee3bc59f4 2c b3 0c 1b ,...
x 0x7ffee3bc5a00 01 00 00 00 ....
0x7ffee3bc5a04 00 00 00 00 ....
y 0x7ffee3bc59f8 80 5b bc e3 .[..
0x7ffee3bc59fc fe 7f 00 00 ....
The variable a lives at address 0x7ffee3bc59f4 and takes up 4 bytes - its initial contents for this run are 0x1b0cb32c (x86 is little-endian, so bytes are ordered from least-significant to most-significant). Since a isn't explicitly initialized, its initial contents are indeterminate - each time I run this program the initial value of a will likely be different (as will its address - as a defense against malware, most OSes randomize locations from run to run).
The variable x lives starting at address 0x7ffee3bc5a04 and takes up 8 bytes (the stack on x86 grows "downwards", so we start from the higher address). Similarly, the variable y lives at address 0x7ffee3bc59fc and also takes 8 bytes. Like a, the initial contents of x and y are indeterminate and will vary from run to run.
After allocating space for an int object that x will point to, I have this:
States of a, x, and y after allocating memory for x
Item Address 00 01 02 03
---- ------- -- -- -- --
a 0x7ffee3bc59f4 2c b3 0c 1b ,...
x 0x7ffee3bc5a00 a0 25 50 1e .%P.
0x7ffee3bc5a04 c2 7f 00 00 ....
y 0x7ffee3bc59f8 80 5b bc e3 .[..
0x7ffee3bc59fc fe 7f 00 00 ....
*x 0x7fc21e5025a0 00 00 00 00 ....
The variable x now stores the value 0x7fc21e5025a0, which is the address of a block of memory large enough to store an int value. Since I used calloc to allocate the memory, the initial contents of it are all-bits-0. I can now assign a new int value to that object through the expression *x, which gives me:
States of a, x, y, and *x after assigning *x
Item Address 00 01 02 03
---- ------- -- -- -- --
a 0x7ffee3bc59f4 2c b3 0c 1b ,...
x 0x7ffee3bc5a00 a0 25 50 1e .%P.
0x7ffee3bc5a04 c2 7f 00 00 ....
y 0x7ffee3bc59f8 80 5b bc e3 .[..
0x7ffee3bc59fc fe 7f 00 00 ....
*x 0x7fc21e5025a0 44 33 22 11 D3".
So I've updated the int object that x points to (i.e., stores the address of).
Finally, I set y to point to a, giving me:
States of a, x, y, *x, and *y after assigning &a to y
Item Address 00 01 02 03
---- ------- -- -- -- --
a 0x7ffee3bc59f4 2c b3 0c 1b ,...
x 0x7ffee3bc5a00 a0 25 50 1e .%P.
0x7ffee3bc5a04 c2 7f 00 00 ....
y 0x7ffee3bc59f8 f4 59 bc e3 .Y..
0x7ffee3bc59fc fe 7f 00 00 ....
*x 0x7fc21e5025a0 44 33 22 11 D3".
*y 0x7ffee3bc59f4 2c b3 0c 1b ,...
The value stored in the variable y is the address of the variable a: 0x7ffee3bc59f4. As you can see, the expression *y holds the same value as the variable a. I can now change the value of a by writing to *y, which leaves us with:
States of a, x, y, *x, and *y after assigning to *y
Item Address 00 01 02 03
---- ------- -- -- -- --
a 0x7ffee3bc59f4 88 77 66 55 .wfU
x 0x7ffee3bc5a00 a0 25 50 1e .%P.
0x7ffee3bc5a04 c2 7f 00 00 ....
y 0x7ffee3bc59f8 f4 59 bc e3 .Y..
0x7ffee3bc59fc fe 7f 00 00 ....
*x 0x7fc21e5025a0 44 33 22 11 D3".
*y 0x7ffee3bc59f4 88 77 66 55 .wfU
There's nothing magic about pointer variables - they're just chunks of memory that store a certain type of value (an address). Different pointer types may have different sizes and/or representations (i.e., an int * variable may look different from a char * variable, which may look different from a struct foo * variable). The only rules are
char * and void * have the same size and alignment;
Pointers to qualified types have the same size and alignment as pointers to their unqualified equivalents (i.e., const int * and int * should have the same size and alignment);
All struct pointer types have the same size and alignment (e.g., struct foo * and struct bar * look the same);
All union pointer types have the same size and alignment;
Operations on pointer values are special, and the syntax for them can be confusing. But pointers are just another data type, and pointer variables are just another kind of object.
That is, set to all-bits-0 or some other well-defined "not a value" bit pattern.
We're not going to get into the distinction between virtual and physical memory here.
You're not allocating space for x itself - you're allocating space for an int object that x will point to.
I understand that the memory location where pointer x's value will be stored might have some remnants of prev operations but i don't understand how there can be an address there.
That's why you see terms like "bogus address" or "random address". Garbage is a bogus address. Garbage, if understood to be an address, is a random address.
There could only be a valid address there by luck (whether good or bad is another question). But if there is random garbage and you use it as an address, it will likely be a "bogus address" or a "random address".
These two variables with automatic storage duration
int *x;
int *y;
of course have their addresses. You can output their addresses the following way
printf( "&x = %p\n", ( void * )&x );
printf( "&y = %p\n", ( void * )&y );
However the variables x and y themselves were not initialized and have indeterminate values. So dereferencing these pointers as in this statement
*y = 13;
results in undefined behavior.
If you want to dereference a pointer it must point to an object as it is done in these statements
x = malloc(sizeof(int));
*x = 42;
After the first above statement the pointer x points to a memory allocated for an object of the type int. So dereferencing the pointer
*x = 42;
you can change the object.
I understand that the memory location where pointer x's value will be stored might have some remnants of prev operations but i don't understand how there can be an address there.
Suppose the previous operations used that memory as a uint32_t object (an unsigned 32-bit integer), and suppose an int * in your C implementation is also 32 bits. As an example, suppose some address on your system is 0x103F0. When the memory was used for a uint32_t, it might have been used to store the unsigned integer value 66544. The hexadecimal for 66544 is 0x103F0. So the memory will contain 0x103F0, which is the same as the hypothetical address.
Every valid address is some particular setting of bits1. And every setting of bits is some unsigned integer. So there can easily be bits in the uninitialized memory for x that represent an address. This can happen with other types, too. The memory for x might have been used as an array of char or as a float, and the bits used for those could also be the same bits used to represent 0x103F0.
Another problem is that when you define int *x; and then use x, modern compilers do not just mechanically reserve some memory for x and then load the contents of that value from memory. They try to optimize your program (unless optimization is turned off). When doing this, they attempt to seek the “best” program that implements the defined behavior of your source code. However, when you use an uninitialized variable, the value of that variable is not defined by the C standard. Depending on circumstances, the behavior of using it may not be defined at all by the standard. Then your program has no defined behavior, and the “best” set of instructions that implements the defined behavior of that part of your source code is no instructions at all—the compiler might just eliminate that part of your program or might simply replace it with other parts of your program or exhibit other behaviors that are surprising to new programmers.
Footnote
1 Sometimes there can be multiple settings of bits that represent the same address, as when some bits are unused or memory is address in a base-and-offset scheme that overlaps segments.
Here int *x is a wild pointer, which means it may be initialized to a non-NULL garbage value that may not be a valid address.

What type casting address, when assigning to pointer, does?

I'm working on understanding C and I came across this example:
int main()
{
double d = 010;
unsigned *pi = (unsigned *) &d;
printf("%x", *++pi);
}
So d has value of 8 in decimal numerical system, 0 10000000010 000... in binary and 40200000 in hexadecimal. I don't understand what this typecast of address does. Why doesn't *pi get hexadecimal value, but instead it prints 0? When it's address is incremented and then dereferenced it get's right hexadecimal value. Why?
The code actually has undefined behavior because of the aliasing rule: you are accessing the representation of the double variable with a pointer to another type that is not a character type.
The intent is to print the 32-bits from the bytes 4 to 7 of the double variable, which are all 0 because of the way 8.0 is represented on your architecture.
The cast tells the compiler to convert the pointer to another type of pointer. This usually does not change its value, but dereferencing the resulting pointer may produce undefined behavior, except for character types.
Here is a modified version that is portable and prints the byte values:
#include <stdio.h>
int main() {
double d = 010;
unsigned char *p = (unsigned char*)&d;
int n = sizeof(double);
while (n --> 0) {
printf("%02x ", *p++);
}
printf("\n");
return 0;
}
Output on an Intel Mac (little endian): 00 00 00 00 00 00 20 40
Output on a big-endian system: 40 20 00 00 00 00 00 00

If pointer stores the address of a variable and is itself a variable, doesn't it create infinite pointers and fills the entire system memory?

Here's one more problem with pointers :
How is printing something or not influencing the value stored at a particular address?
l-k has a value equal to 1, that's why i'm checking if the value stored at k+1 is equal to 88 or not.
#include <iostream>
int main()
{
int i=55;
int j=88;
int *k=&i;
int *l=&j;
k++;
// printf("%p\n",l-k);
/* Why does uncommenting previous line changes the output from 0 to 88? */
printf("%i",*k);
return 0;
}
Whilst
k++;
is allowed (you are allowed to set a pointer one past the address of a scalar and read that pointer value), the behaviour of the subsequent dereference of k is undefined. Somewhat paradoxically that means that the behaviour of your entire program is undefined.
The behaviour of l-k would also be undefined. Pointer arithmetic, including the difference between two pointers, is only defined within arrays. For this purpose an object can be regarded as a single element array.
Regarding the question in the title:
If pointer stores the address of a variable and is itself a variable, doesn't it create infinite pointers and fills the entire system memory?
No. I added some code to dump the addresses and contents of each of i, j, k, and l, and here is the result:
Item Address 00 01 02 03
---- ------- -- -- -- --
i 0x7ffee31d3a48 37 00 00 00 7...
j 0x7ffee31d3a44 58 00 00 00 X...
k 0x7ffee31d3a38 48 3a 1d e3 H:..
0x7ffee31d3a3c fe 7f 00 00 ....
l 0x7ffee31d3a30 44 3a 1d e3 D:..
0x7ffee31d3a34 fe 7f 00 00 ....
Hopefully the output is self-explanatory - each row shows the name of the item, its address, and its contents (both in hex and as a sequence of bytes).
I'm on a little-endian system, so multi-byte objects have to be read from bottom to top, right to left.
Anyway, i lives at address 0x7ffee31d3a48 and stores the value 55 (0x37). k lives at address 0x7ffee31d3a38 and stores the value 0x7ffee31d3a48, which is the address of i.
There's no infinite regression of addresses. k is just another variable - the only difference between it and i is that it stores a different type of value.
As for your other question:
Why does uncommenting previous line changes the output from 0 to 88?
The expression k++ changes what k points to - it's no longer pointing to i. Here's the state of the program after that expression:
Item Address 00 01 02 03
---- ------- -- -- -- --
i 0x7ffee31d3a48 37 00 00 00 7...
j 0x7ffee31d3a44 58 00 00 00 X...
k 0x7ffee31d3a38 4c 3a 1d e3 L:..
0x7ffee31d3a3c fe 7f 00 00 ....
l 0x7ffee31d3a30 44 3a 1d e3 D:..
0x7ffee31d3a34 fe 7f 00 00 ....
Instead of storing the address of i (0x7ffee31d3a48), k now stores the address 0x7ffeee31d3a4c, which is ... not the address of any object in your program. At this point, attempting to dereference k invokes undefined behavior - your code may crash, or you may get unexpected output, or through some miracle you may get the result you expect. Removing the printf statement changes the layout of your program in memory, which will affect what k points to after the k++ expression.
Actually, it's undefined behavior. This here:
k++;
Increases the pointer so it points to a different memory location, it advances it by the size of an int. If i were an array of multiple ints, it would point to the next one in line. But it isn't, so reading from this pointer in the print later is undefined behavior and it might read from an unspecified place.
When I try this program in MSVC, it doesn't print 0 or 88, it prints -858993460 every time. A different compiler may print something entirely else, something that changes, or just crash the program, or even do something different than all of those.
If uncommenting a line affects the output, it seems likely that your code has undefined behaviour. Which is clear from actually reading the code, especially these two lines.
This line is fine.
int *k=&i;
But what do you expect this line to do?
k++;
i is a single int so pointing k at the int after it has no meaning or use as you could be accessing any part of memory as evident by the fact that sometimes you get 0 or sometimes get 88.

c pointers and memory representation [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 8 years ago.
Improve this question
I came across this question on SO:
(Tricky pointer question):
A C programmer is working with a little-endian machine with 8 bits in a byte and 4 bytes
in a word. The compiler supports unaligned access and uses 1, 2 and 4 bytes to store char, short and int respectively. The programmer writes the following definitions (below right) to access values in main memory (below left):
Address | Byte offset
--------|-0--1--2--3----
0x04 | 10 00 00 00
0x08 | 61 72 62 33
0x0c | 33 00 00 00
0x10 | 78 0c 00 00
0x14 | 08 00 00 00
0x18 | 01 00 4c 03
0x1c | 18 00 00 00
int **i=(int **)0x04;
short **pps=(short **)0x1c;
struct i2c {
int i;
char *c;
}*p=(struct i2c*)0x10;
(a) Write down the values for the following C expressions:
**i
p->c[2]
&(*pps)[1]
++p->i
That question only answers the third subquestion but I was wondering how the rest of the subquestions would be solved. I'm new to C and trying to improve my understanding on pointers and this was particularly confusing. Thanks for any help!
You'll probably want to refer to this: Operators in C and C++ - Operator precedence
The question didn't specify, but we will assume that pointers are 4 bytes - sizeof(void*) == 4.
1.
int **i=(int **)0x04;
**i = ???
i is a pointer to a (pointer to int). When we say **i, we are de-referencing the pointer twice - or reading the value that the pointer points to.
First note that **i == *(*i). So we first read a pointer-sized (4-byte) value from memory at address 0x04. This is 10 00 00 00 - interpreted as a little-endian value, that is 0x10. So now we are left with *((int*)0x10). That means we read an int-sized (4-byte) value from memory at address 0x10. 78 0c 00 00 interpretted in little-endian is the value 0xC78.
2.
struct i2c {
int i;
char *c;
} *p = (struct i2c*)0x10;
p->c[2] = ???
This one's a little trickier. I'll assume that you understand that structures are just a collection of variables that (excluding padding, which doesn't apply here) are laid out one after another in memory.
Our pointer p points to a struct i2c object at 0x10 in memory. That means at address 0x10 is the int, named p->i. And immediately following that, at address 0x14, is the char *, named p->c.
The expression p->c[2] means: "First get the char *c from the structure that p points to. Then, get the char at index 2 from the array that p->c points to."
So first, we'll get p->c. I already mentioned this char* is at address 0x14. There we find the pointer 08 00 00 00, or 0x8.
Now, we have a char * that points to address 0x8, and we want the char at index 2 in that array. To get the address of an array element, we use this formula:
&(x[y]) == (char*)x + (y * sizeof(x[0]))
In other words, the offset (from the start of the array) of the nth element in the array is n times the size of each element.
Since chars are 1 byte, p->c[2] is at 0x8 + 2 = 0xA. There we find the value 0x62, which is the ASCII character 'b'.
3.
short **pps=(short **)0x1c;
&(*pps)[1] = ???
With our knowledge operator precedence, we read &(*pps)[1] as "First dereference pps, which is a pointer-to-short (or an array of shorts). Then, we want the address of the element at index 1."
At address 0x1C we have 18 00 00 00, or 0x18. So now we have a pointer to an array of shorts, and this array starts at address 0x18. Using our formula from above, and knowing that shorts are 2 bytes in size, we calculate element 1 to be at address 0x18 + (1 * 2) == 0x1A.
At address 0x1A is 4c 03, or 0x034C. However, the problem wasn't asking us for the value of element 1 - that would be solving (*pps)[1]. Instead, it asked for &(*pps)[1] or the address of that element. So, we simply go back to the end of the previous paragraph, where we said the address was 0x1A.
4.
++p->i = ??
For this one, you really need to know the operator precedence. It should be clear that this could be interpreted two different ways:
a) Increment the pointer p 1, and then dereference p to get its member i
b) Dereference p to get its member i, then increment the integer value
From the precedence chart, we see that -> has a precedence of 2, while ++ (the Prefix increment), has a lower precedence of 3. That means we need to apply the -> first, then increment. Thus, option b) was correct.
So, first let's get p->i. We already said in part 2. that since p points to address 0x10, and i is the first member in struct i2c, p->i is at address 0x10. There we find 78 0c 00 00, or 0xC78.
Finally, we need to apply the ++ operator, and increment that value to 0xC79.
.....
1 - Pointer arithmetic means you treat a pointer like an array. So p + 3 doesn't mean "p plus 3 bytes", it means &p[3], or "p plus (3 * sizeof(*p)) bytes".

Assignment of pointers?

int arr[]={1,2,3};
char* p;
p=arr;
This is a fragment of a program and it works.
Can anyone explain how can an int variable address be assigned to char pointer?
This works, because size of char pointer is the same as size of an int pointer. However, when some pointer arithmetic will be applied, you won't get valid results. It means that if you would like to access second element of your array - p[1] then only one byte would added to the p address. If p would be of type int, then 4 byte would be added (since that's the size of int) and second element would be accessed properly.
#include <stdio.h>
int main(void)
{
int arr[] = {1,2,3};
char* p;
p = arr;
printf("%d\n", p[0]);
printf("%d\n", p[1]);
int* pp = arr;
printf("%d\n", pp[0]);
printf("%d\n", pp[1]);
return 0;
}
output:
1
0 - as you can see that is not the value from arr[1]
1
2
As I have intel processor, which is in little endian, arr[0] in memory is stored as following:
01 00 00 00
^
p[0]
p[1] which is equivalent to *(p + 1), will add one byte to p pointer, because size of p is char.
01 00 00 00
^
p[1]
and printf("%d\n, p[1]); gives 0. Note that if you were on big endian machine, the result of printing p[0] would be different (e.g. 0).
However, your compiler should warn you about what you're doing.
warning: assignment from incompatible pointer type [enabled by
default]
When you assign a char point to the address of integer array, many things can happen, depending on the size of the integer. Below is the memory location of the array in compile under 32 bit windows OS. You may also want to cast it p=(char*)arr.
01 00 00 00 02 00 00 00 03 00 00 00
int arr[]={1,2,3};
// arr[0] = 1;
// arr[1] = 2;
// arr[2] = 3;
char* p;
p=(char*)arr;
// p[0] = 1
// p[1] = 0
// p[2] = 0
// p[3] = 0
// p[4] = 2
// p[5] = 0
// p[6] = 0
// p[7] = 0
// p[8] = 3
// p[9] = 0
// p[10] = 0
// p[11] = 0
Your code is invalid. In C language an int * pointer cannot be assigned to a char * pointer without an explicit conversion. C language prohibits assignment of incompatible pointer types. (I.e. it prohibits implicit conversion between such types). Your compiler probably issued a diagnostic message telling you exactly the same thing. Your code contains constraint violation - it is not a valid C code.
This conversion was legal a long time ago, in archaic non-standardized versions of C language. For that reason, by default C compilers are rather permissive with regard to invalid code of this kind. They issue diagnostic messages as "warnings", but continue to compile the code, which misleads some people into believing that the code is valid.
Many C compilers offer you additional options that make them more explicit in detecting such errors. For example, in GCC you have to specify -pedantic-errors switch to have this constraint violation reported with an "error" message.
If your compiler accepted the code, its behavior is the same as if you wrote p = (char *) arr, i.e. it makes p point to the beginning of the arr array. That's all there is to it. If you attempt to access the array data through pointer p, you will simply reinterpret the memory occupied by arr as a sequence of chars. What you will see there will depend on many implementation-dependent factors. (I.e. there are no universal answers to your "Why do I see 0 there? Why do I see 2 there?" questions.) For example, the reinterpreted data will look different between big-endian and little-endian platforms.
Yes you can assign a address of variable of any type to pointer of any type (however you may get a warning about assigning incompatible pointer type). This is because sizeof pointer of any type is same.
For Example:-
int main(void)
{
char *c;
int *i;
float *f;
printf("%d %d %d",sizeof(c),sizeof(i),sizeof(f)); // Output: 4 4 4
}
Side effect of this:
Consider the following example:
int main(void)
{
int i1=127;
int i2=128;
char *c1=&i1;
char *c2=&i2;
printf("%d %d",*c1,*c2); //Output : 127 -128
}
In order to understand this output you need to consider binary values of variables.
Lets start with i1:- Binary value of i1 is
00000000 00000000 00000000 01111111
and by assigning address of this memory to char pointer causes char pointer c1 to point to first byte (Little Endian Method) .
Now binary value of i2 is
00000000 00000000 00000000 10000000
here again c2 points to first byte but why output is -128?
because first byte is 10000000 that sign bit is 1 which means number is negative
now perform a reverse operation of 2's complement
10000000 ---> 01111111(minus one) --->10000000(one's complement)
Your compiler should give you a warning that this is not compatible.
After assignment , lets say address of arr is 1000, arr[0] will be at address 1000, arr[1] will be at address 1004 and arr[2] will be at address 1008
Now p points to arr, so it points to address 1000, so every time you want to access arr[1] you will have to use *(p + 4) since char is 1 byte only (Also care should be taken if its a little endian or big endian machine)
Though its incompatible you can use it but you should be carefull when you use this assignment.
gdb output.
(gdb) p *p
$8 = 1 '\001'
(gdb) p *(p+1)
$9 = 0 '\000'
(gdb) p *(p+2)
$10 = 0 '\000'
(gdb) p *(p+3)
$11 = 0 '\000'
(gdb) p *(p+4)
$12 = 2 '\002'
(gdb) p *(p+5)
$13 = 0 '\000'
(gdb) p *(p+6)
$14 = 0 '\000'
(gdb) p *(p+7)
$15 = 0 '\000'
(gdb) p *(p+8)
$16 = 3 '\003'

Resources