In C, we have data, and we have pointers. Pointers by themselves are binary data. So, at the hardware level there is no difference between data and pointers. Pointers must be an implementation. If we have a variable, that variable has two properties, the data and the address, which itself is a data, in a sense this creates an infinite pointer loop kind of thing. Since every pointer is a data, they must have a pointer that points to them, and the pointer that points to the pointer must have a pointer, and so on.
This type of implementation would only make sense if pointers are created on demand. Let's say we create a variable called a, does C immediately assign a pointer to this variable right after the variable being declared? Or is it when I explicitly try to pull the pointer by doing &a that C creates a pointer according to some internal algorithm?
in a sense this creates an infinite pointer loop kind of thing. Every pointer must have a parent pointer
This is logically incorrect. It's like saying: "A pointer is a street address. Every person has a street address. Therefore a street address is a pointer."
Pointers are variables containing addresses, but that doesn't make addresses pointers... something can have an address without a pointer used to access that address. Just like your machine code can contain integer values without them being stored in int variables.
Or is it when I explicitly try to pull the pointer by doing &a that C creates a pointer according to some internal algorithm?
Yes, kind of. Here the address is actually used by the program so the &a address must be stored somewhere. You can think of it as a temporary pointer variable if it helps. In practice, if we disassemble this code:
int a;
printf("%p\n", &a);
Then on gcc x86 this just results in a "load effective address" instruction. That is, the compiler stores the address in a register which is then passed on to printf as per that function's calling convention.
Your description is all wrong. It seems you are confusing C pointers and memory addresses at execution time. It's completely different things.
Pointers in C are no different from other data types. You get a pointer to type T only when you define one, i.e. like T* p; Just like you only get an int object when you define one (e.g. int a;).
At machine level it's completely different. In order to access objects (aka variables) stored in memory, the CPU needs a way to calculate the address of that object. The C standard does not care how it's done. It's an implementation detail that may differ from system to system.
Many implementations uses a "stack pointer" (stored in a CPU register) as reference for other variables. Instead of knowing the exact address of an object, the address is found as "value of stack pointer" plus an offset (i.e. SP+offset). This offset is then hardcoded into the execution binary, i.e. the instruction set of the CPU can have an instruction that do stuff like: "Read the memory at address SP+fixed_offset and store it in register X".
Take a look at this simple (and rather stupid) function:
unsigned long foo(unsigned long x)
{
unsigned long y = x;
putchar('a');
return y + x;
}
This defines two "unsigned long" objects and return their sum. Using godbolt.org, gcc 11.2, and flag -fomit-frame-pointer (no optimization to keep things simple), I get this machine code (with my comments added):
foo:
sub rsp, 40 // Change stack pointer to reserve 40 bytes
mov QWORD PTR [rsp+8], rdi // Save the passed value (i.e. register rdi)
// in object x at memory address rsp+8
mov rax, QWORD PTR [rsp+8] // Read object x into register rax
mov QWORD PTR [rsp+24], rax // Save rax in object y at memory address rsp+24
// So this is really y = x
mov edi, 97 // These are just
call putchar // putchar('a');
mov rdx, QWORD PTR [rsp+24] // Read object y into register rdx
mov rax, QWORD PTR [rsp+8] // Read object x into register rax
add rax, rdx // rax = rax + rdx, i.e. rax = y + x
add rsp, 40 // Restore stack pointer, i.e. release
// the 40 bytes
ret // Return. The returned value is in register rax
So on this specific system the memory address of x is found as "stack pointer + 8" and the memory address of y is found as "stack pointer + 24". From the machine code we can't tell the actual memory address of the variables as it depends on the value of the stack pointer (rsp) when the function is called.
The lesson is that - yes, at machine level there is a way to get the memory address of x and y but there is no such thing as an automatically created and stored pointer to any of them.
Now for fun - the same code compiled with -O2 gives:
foo:
push rbx
mov rsi, QWORD PTR stdout[rip]
mov rbx, rdi
mov edi, 97
call putc
lea rax, [rbx+rbx]
pop rbx
ret
Take a look at the code and see if you can find x and y ;-)
(but - to repeat - all this is not described by the C standard, it's just how it's done on this specific system).
BTW Also be aware that objects/variables define by the C code may exist only in CPU registers, i.e. the are never written to memory and consequently they don't even have an address.
Every variable is stored in the memory . The address (pointer) is not stored separately, it is simply the location of the variable.
(This is actually more complex than that: Some variables might normally stay in processor registers instead of RAM, but if you take their address, the compiler might store them in memory so that the address can be taken. Or if you just print the address, it might invent a fake address for them.)
If you take the address of a variable and store it in a pointer, you create a new variable, and assign the value of the address of the other variable. But the address of this new variable is not stored separately, it is simply the location of the variable.
There is no "infinite pointer loop", unless your code makes infinite number of variables and your computer has infinite memory.
What determines the address then? How does the program know where the variable is stored?
This is controlled by the operating system, which gives your program memory blocks based on the memory allocations you make. Your program determines (at compile time) which variable is in which address relative to this memory block.
I would not say "we have data, and we have pointers". Yes, pointers are different, but saying it this way doesn't really capture what's different about them.
Any variable has a location (also called an address), and a value (perhaps also called "contents"). If you say
int i = 5;
the value is 5, and we're not precisely sure where the location is (because we usually don't care), although the identifier i helps us keep track of it, whatever it is. But there are definitely two things, the location or address, and the contents or value.
If you say
int *ip = &i;
once again you have a variable named ip, and a value, which in this case is a pointer, or an address, and what it's the address of is the same as that other variable i. And then there's also a value in the pointed-to location. So you now have three things:
the variable ip, and
its value, which is "pointer to i", and
the pointed-to value, which is 5.
Almost nothing gets "created on demand". The variable i got created because you requested it. The variable ip got created because you requested it.
You can draw a picture like this to help you keep track of things:
+---------+
i: | 5 |
+---------+
^
|
+-------|-------+
ip:| * |
+---------------+
The only thing that happens automatically, behind your back, that you can't necessarily see, is the assignment of some actual, numeric addresses for your variables. You can see those if you print them out using %p:
printf("address of i: %p\n", &i);
printf("address of ip: %p\n", &ip);
and you will notice that ip holds i's address:
printf("value of ip: %p\n", ip);
or you can look at everything — i's two things, and ip's three things — like this:
printf("i: loc %p, value %d\n", &i, i);
printf("ip: loc %p, value %p, pointed-to value %d\n", &ip, ip, *ip);
To see what's going on a little more explicitly, let's write this as an actual program. For the moment, I'm going to have the variables i and ip be "global" variables, although this is unusual, because normally they'd be "local" variables, declared inside main. Also I'm going to declare a third variable j.
#include <stdio.h>
int i = 5;
int j = 66;
int *ip = &i;
int main()
{
printf(" i: loc %p, value %d\n", &i, i);
printf(" j: loc %p, value %d\n", &j, j);
printf("ip: loc %p, value %p, pointed-to value %d\n", &ip, ip, *ip);
}
the output I get is
i: loc 0x10837b018, value 5
j: loc 0x10837b01c, value 66
ip: loc 0x10837b020, value 0x10837b018, pointed-to value 5
You'd get different addresses on your computer, but basically a similar pattern.
If you're on a Unix-like system, you can run the nm command to see your program's "namelist", or symbol table, which is a list of all the identifiers in your program, and their addresses. When I run it on the program above, I get something like this:
$ nm a.out
000000010837b018 D _i
000000010837b01c D _j
000000010837b020 D _ip
0000000108370ec0 T _main
This shows me that my program has three things in the "data" segment, which are my variables, and one thing in the "text" segment, which is my main() function. Lo and behold, the locations listed by the nm program for my two variables exactly match what was printed when the program ran. (Although there's a complication here; see below.)
Now that we know where the variables actually are, we could draw a slightly different picture:
+---------+ +---------+
10837b018: | 5 | 10837b01c: | 66 |
+---------+ +---------+
^
|
+-------|-------+
10837b020:| * |
+---------------+
This shows us that, basically, the names or identifiers we use for things in our programs — like i, j, ip, and main — are like labels or shorthands for the addresses in memory where these things are stored. (In fact, these identifiers are therefore kind of like pointers in their own right, although I hesitate to say this, because it might confuse the issue.)
Another way to think of it is this. Imagine you live in a large apartment building. In the lobby is a large row of mailboxes, one for each apartment. Each mailbox, naturally, has a label on or next to it giving the apartment number. So the apartment numbers are like addresses, and the contents of the mailbox are like values.
Finally, two footnotes. On a modern system, the nm command probably won't print out addresses that are the same as your program did, after all, due to something called "address space randomization".
When you print pointers, strictly speaking you should cast them to void *, like this:
printf(" i: loc %p, value %d\n", (void *)&i, i);
printf("ip: loc %p, value %p, pointed-to value %d\n", (void *)&ip, (void *)ip, *ip);
C describes abstract machine, where pointer is pointer, and value is value (distinct from pointer).
When it comes to concrete machine, a pointer or a value may be saved in a register (which eliminates the need and the possibility of pointer to it). So you don't have infinite pointer loop, ultimately you have it in a CPU register.
When you compile your program, there may be no correspondence between C code and machine code, especially if optimizations ae enabled, so that &a actually may create creates no pointer (indirection optimized out), whereas b may create a pointer (added indirection to pass large structure).
You are worrying about things that the compiler handles.
When you assign a variable a, the compiler assigns it to a specific memory location or register - the address. The processor knows this address, and using the address, it can access the value of the variable. It doesn't need to keep a separate variable to remember the address.
There are multiple things a processor can do with the address, depending on the operations it supports, but the most common and the most relevant are direct addressing (access the value at the address number given to you in the operation), indirect addressing (access the value at the address number given to you in the operation AND THEN threat that value like an address and access ANOTHER value) and immediate addressing (treat the number given to you as a regular number). Respectively, they match a, *a and &a in C (this is way oversimplified though).
Conclusion - no, creating a pointer won't create an infinite amount of pointers, in order to keep track of the pointers. The processor treats everything the same - addresses are just values and values can be used as addresses.
This is an answer to your comment to S.Ptr answer.
The processor doesn't "know" anything.
The only thing a processor can do is execute instructions: things like "add two numbers together", "put this variable in RAM"... Before modern compiled programming languages like C all programs where created by listing instructions we wanted the processor to execute. A simple addition could have looked like this:
store 10 in memory at address 1
store 5 in memory at address 5
load the value at address 1 into register A
load the value at address 5 into register B
add B to A and store the result in B
store the result of B in memory at address 2
The programmer had to remember where they put everything because the CPU doesn't "understand" what you want it to do, it just follows your commands blindly.
As an example, let's say you made a mistake and you wrote this:
store 10 in memory at address 1
store 5 in memory at address 5
load the value at address 1 into register A
load the value at address 50 into register B
add B to A and store the result in B
store the result of B in memory at address 2
You may hope the CPU will correct you and use the right address but since it doesn't even check for this, the computer will happily run the code and add whatever was at address 50 to 10. When there's only 3 variables it's pretty easy to keep track but as you can guess, the more variables you add, the more difficult it is to remember what memory address corresponds to what data.
To help with this problem, we could use something like "address labels", basically allowing us to write the above code in this way:
please replace all instances of "number1" with "address 1"
please replace all instances of "number2" with "address 5"
please replace all instances of "result" with "address 2"
store 10 in memory at number1
store 5 in memory at number2
load the value at number1 into register A
load the value at number2 into register B
add B to A and store the result in B
store the result of B in memory at result
This way, there's no risk to use the wrong address! "number1", "number2" and "result" are meant to help the programmer so they doesn't have to remember where variables are but since the computer only understand addresses we would need to use a special software to convert the easier to understand code to instructions the machine would actually be able to run.
As time went by, we started creating more and more tools for humans to be able to write better code faster, the C programming language is one on them. Exactly like the "please replace" example, C code helps you avoid mistakes and simplifies programming a lot, at the cost of not being understood by the computer at all.
That's why you need to use a special software called a compiler to run your code, it takes your code and compiles it into the different instructions your dumb computer will execute blindly.
Through the answer I only used pseudo-code so just to get a glimpse at how things work in the real world, let's use some real C code and real CPU assembler. Here's a really simple C code to add two numbers:
int main()
{
int a = 10;
int b = 5;
int result = a + b;
return result;
}
and here's the x86_64 ASM code my compiler created from it:
pushq %rbp
movq %rsp, %rbp
movl $10, -12(%rbp)
movl $5, -8(%rbp)
movl -12(%rbp), %edx
movl -8(%rbp), %eax
addl %edx, %eax
movl %eax, -4(%rbp)
movl -4(%rbp), %eax
popq %rbp
ret
Note that the ASM listing is still not "raw", it may be a lot more difficult for us to understand than C but it's still too abstract for the CPU.
This minimal program
int r = 100;
return r;
generates just one instruction in main, before the ret:
mov eax,0x64
There is only an immediate and a register. No address. Good nobody asked for it.
With return (long)&r it is (after some stack-checking):
lea eax,[rsp+0x4]
Here there is no value, only the address of where one could (and would) be stored. It's a real living address, but not one containing "100".
Because C's main() returns int, the eax (not rax) is used, and the aligned half of the current stack position (+0x4). The version:
long r = 100;
return &r;
compiles to simply:
mov eax,esp
(i.e. no calculation with lea needed)
To get rid of the different warnings: return (int)(long)&r. This probably shows that addresses are not meant to be returned outside. Here it is only done to force -O3 to do something at all.
So pointers can be created on demand. But in a real program the compiler already has that address stored somewhere / in use.
An infinite pointer "loop" is prevented by:
error: lvalue required as unary '&' operand
6 | return &(&r);
You'd have to put &r into a fresh variable first, before you can take it's address. Fresh variable means new name (or array index) for the programmer and a new memory address ("lvalue") for the compiler.
I finally resolved my confusion(thanks to all the answers) which basically boils down to this.
In real hardware memory, there is a permanent address of each byte of data. At the software level, the operating system creates virtual addresses that map to those real addresses. It's a one-to-one mapping, for one hardware address, there is only one virtual address at a time.
In C, the compiler creates a hashmap-like data structure with variable names as keys and virtual addresses as values. For every new variable, a new entry is added to the hashmap. Whenever we want to get a virtual address of a variable, it's like asking the compiler, "hey, what is the corresponding virtual address associated with this variable name" and the compiler looks through the hashmap and returns the address corresponding to that variable name.
This is just a simplification, as the details are beyond my knowledge, but it nonetheless relieved my confusion for now.
I have some questions about stack appearance while calling functions, and I have some small examples that will help explain my confusion:
1)
Let's say we have these two functions
int h(int k){
k = k+3;
return k;
int f(int x, int y){
int q;
int z = 10; //Checkpoint_1
q = h(x); // Checkpoint_2
return z;
First question: how will the stack look like after we reach the line of the Checkpoint_1, will the stack have the all the local variables (x,y,q,z)? how would they look like inside the stack?
Second question:
How will the stack look like after we reach checkpoint_2 and enter the function h(x) and put x+3 in q, will q change to x+3 in the stack frame? or will it stay the same q (having x+3 as value)?
Third question:
What registers will these two functions use? I know that there's a register that will have the return value for each function in each frame in the stack (I think it's called %eax), but my confusion is, let's say in function h(int k), will %eax have value of k or k+3, or say in function f(int x, int y) will %eax have z or 10.
I would really appreciate any help and tips regarding my questions.
I'll preface this by saying that the compiler is free to do whatever it wants in some capacity - but all modern compilers are going to do it very similarly. I'm also going to assume we're talking about x86-64, utilizing the cdecl standard that's common on linux. It's different for 32-bit, and it's different for Windows.
Modern compilers usually allocate all space for stack variables at the beginning of the function call, regardless of scope. Usually this is done simply by decreasing the value in rsp. The stack grows down, which means decreasing the value is the same as growing the stack, hence, an allocation. In this case, you have two 4 byte values initialized on the stack, so the stack will have 8 bytes allocated on it. The two variables x and y will be stored in %rdi and %rsi respectively on 64-bit. (on 32-bit, they're stored above the return address on the stack.)
After the call to h(x), yes, the value of q in memory will change to (x+3).
The compiler will use whatever registers it feels like using. In this case, it's actually very likely for everything to be in registers, and for the stack to not be used at all except for the return address for h. When function h returns, the value of k (which is now k+3) will be stored in %eax and returned. When function f returns, the value in z will be stored in %eax and returned. Registers have no concept of variable names (like z), they simply have values. It is the responsibility of compilers to give those values meaning by associating that register with the concept of z.
i have limited RAM in my microController.(using arm gcc)
i should write my code as efficient as possible. consider function blew :
int foo(uint32_t a , float b)
{
.....
return 0;
}
so I have two arguments a and b. now if I change the arguments to *a and *b is the function takes less ram than the first function during execution :
int foo(uint32_t *a,float *b)
{
....
return 0;
}
what if the arguments become String or array ( of int, float ....)??
any references would be great. thanks
You can actually waste memory when using pointers. This is for 2 reasons.
1. Register optimization lost
Code which calls foo must take address of the variable to pass as parameter. If passed variable is local, it could be in register but because you take its address it must be placed on stack. In general, using variable in register is faster than using variable in stack.
2. Value of variable unknown after call
When you give address of variable function, compiler no longer knows if variable is modified, and must refresh it if it's read again.
uint32_t u = 1;
float f = 2.0f;
foo(&u, &f); // 1. Address taken, u and f cannot be register variables
// 2. What is value of u now? It must refreshed from memory before addition happens
u++;
Bottom line: do not take address of primitive types unless you have to.
Strings and arrays are already passed using address, so there is no other option.
ARM's Procedure Call Standard defined in Application Binary Interface (ABI) states that the first four word-sized parameters passed to a function will be transferred in registers R0-R3.
Since pointers are also 32-bit in size, there is not that much difference between these two signatures.
int foo(uint32_t a, float b)
int foo(uint32_t *a, float *b)
Both will have a in r0, b in r1 and return value in r0 again.
ARM is a RISC architecture it has many registers and with trivial functions you may even get away from touching any memory at all.
If you are working with micro controllers it is a better idea to check if you should really use floating points. Since they might not be natively supported by the core you are targeting for.
I would check ARM Cortex-A Series Programmer's Guide (most of it applies to micro controllers as well), especially chapters 15 and 17 to learn more about tricks for ARM application development.
#include<stdio.h>
int add(int,int);
int main()
{
int a;
a=add(5,7);
printf("%d",a);
}
int add(int x,int y)
{
x=x+y;
return(x);
}
Last night I got a doubt on return statement. See x is an auto variable defined inside the add function and as ANSI said an auto variable exists and has its lifetime only inside the function but here the return statement can make the variable a to exist even outside the function. Where does it store the value ? Stack or heap ?
Neither on the stack nor on the heap.
At least in the x86 architecture, the return value of functions is usually copied to the eax register, so it persists outside of the function. This is one of the reasons why you can't return an array but merely a pointer to it.
You can, however, return structs and other larger variables by value, in which case the compiler does some tricks by using the stack and additional registers such as edx.
What return actually does depends on the machine architecture to which the code is compiled. On a machine with registers the value of x is copied to a register. The caller will pick up the value from there and do something with it. Here, that value is copied to a variable named a.
If this code is compiled on a stack machine, the return value might be just left on the stack itself. The object code for calling the add function will look something like this:
push 7
push 5
call add
seti 0x28ccf4
add will pop the values from the stack, add them together and push the result back to the stack. (If the function deals with data structures that will not find into a slot on the stack, their addresses are pushed instead.) seti is an operation that will pop a value from the stack and assign it to the integer variable at a particular address. (Here, 0x28ccf4 is the address of a.). As the top of the stack will now contain the result of adding 5 and 7, the value of a will become 12.
It varies. In a typical case, you can count on anything up to at least the size of an int being returned in some register(s) (e.g., typically eax on x86, $v0-$v1 on MIPS, r0 on ARM).
On many machines, larger return values (e.g., a struct with several members) will result in the caller allocating space on the stack to hold the return value, and either passing a pointer to that space as a hidden argument to the function, or the function having implicit knowledge of the location of that space relative to (for example) the stack pointer when the function was entered.
Though the question is tagged C, I'll also mention that in C++ there are a few special rules about return values. Specifically, there's a return value optimization and a named return value optimization. These allow a compiler to elide code for copying a return value, even if/when (for example) the copy would normally have visible side effects. These are intended to allow optimization where the function constructs a return value where the calling code will need it, rather than constructing it locally in the function, and then making a copy back to where the calling code can use it.
It is stored in a register in most platforms I know about, but this could be changed by compiler implementation.
It is worth noticing that while return x where x is a value and not pointer, such as in you case is legitimate, if x is a pointer, you should make sure that the buffer / data it points to is valid when the function returns, otherwise it will be a valid pointer that points to invalid location.
This is most likely covered in programming language course if you are a CS student. Anyway, you can check out the Wikipedia.
And in fact, it is stored in the call stack, aka stack.
The return value of add function is copied back to 'a' in the main function. The auto variables declared within a function are present in the activation record for that function on the call stack and the activation record is removed at the end of function execution, after copying any return value back to the caller's variable..
I would like to know Where arugment passed to main() gets stored in memory ,Do they simply get store in stack .If so then how k's value is initialize in below code
#include<stdio.h>
int main(int k)
{
if(k<10)
printf("%d ",main(k+1));
return k;
}
O/p: 10 9 8 7 6 5 4 3 2
It'll typically be stored wherever parameters to other functions get stored -- which might be stack, register, or somewhere else entirely. Just for a few examples: on a SPARC, it'll almost certainly be a register; on an x86 (in 32-bit mode) it'll typically be the stack; on an IBM mainframe, it'll normally be in a stack frame that's dynamically allocated from the heap and linked together into a linked list that's constructed/destroyed FIFO fashion.
Also note that it can/does vary even on a single machine with a single compiler -- e.g., Microsoft VC++ can pass it on the stack or in a register depending on what compiler flag(s) you use. When/if you pass it in a register, it'll (probably) be pushed on the stack inside the function anyway (to allow the recursion).
As an aside, I'll also note that while your codecalling main is perfectly legal C, you are not allowed to call main in C++.
Edit: As for the initial value, that first parameter is traditionally called argc, and tells you how many arguments were passed on the command line. If you invoke it (as you apparently have) with no command line, it should normally start out as 1 (the one argument being the name of the program itself, traditionally passed as argv[0]). If, for example, you invoked the program something like:
prog a b c d e f g h i j k l m
It would normally exit without printing anything, because on the first entry to main, the parameter would be greater than 10 so the body of the if statement would never execute.
parameters to main() are like arguments to any other method. They are part of the stack (depending on the processor, that could be in memory, or in a CPU register). In this case, when you call main(k+1), the result of k+1 is put on the stack before the recursive call is performed.
Note that the behavior of this is undefined since calling main() from within a C program is unspecified.