The Joy of Programming
Allocation of Local Variables in Stack

It is always fun to understand the low-level details of a p...
*(p - 1) = 10;
*(p + 1) = 10;
printf(quot;i = %d j = %d k = %dquot;, i, j, k);
// most compilers will print
// i = 10 j = ...
Upcoming SlideShare
Loading in...5
×

11 Jo P Nov 07

203

Published on

Published in: Technology
0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total Views
203
On Slideshare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
3
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

11 Jo P Nov 07

  1. 1. The Joy of Programming Allocation of Local Variables in Stack It is always fun to understand the low-level details of a program and to explore how compilers work. In this column, we’ll see some details on how local variables are allocated in stack and some interesting issues on how compilers handle it. Every month, I get lots of mails and queries from LFY readers. This column seems to have become popular, particularly among students. This month, we’ll cover an interesting question from Saravanan swamy (GCT, Coimbatore). His question is: Why does the following program print 10? int i = 10; printf(“%d”); Note that the printf doesn’t obviously have i as matching argument for the format string “%d”. So, the program is incorrect and you can reasonably expect the program to print some garbage value. But note that it is equally possible that you might get 10 as output. Why? To understand this, let us see how compiler might transform these statements to low-level executable code. Local variables are allocated in the stack frame of a function. When the main method is invoked, the storage space for i is also allocated in the stack frame. A compiler should generate code to initialize that value i with 10. The machine code will have instructions to load the value 10 in a register and store it in the location for i in the stack frame. The printf statement is a function call. The printf function takes a variable length argument list. Based on the first argument – the format string – the rest of the arguments are “interpreted”. Here the format string (“%d”) indicates that it should read the second argument and interpret it as an integer. The printf takes variable length argument list as argument and the compiler has no knowledge about the internal affairs of routines like printf, so, it will happily compile the code without complaining (but a tool like Lint will warn, but that’s a different story). The compiler generates code for invoking printf. Conventionally, a compiler would generate code to pass the arguments in the processor registers. For Unix/Linux, the code for printf will be in a shared library (typically named as libc). That printf code, with “%d” as argument expects an integer to follow it. It has code to read the argument from a processor register. It happens that most of the compilers generate code by reusing the same registers (for example, an accumulator register in processors that are accumulator based). So, it is likely that the same register that is used for initializing the value of i is reused for reading the argument passed to printf. So 10 it might get printed! Yes, compilers differ considerably in ways in which they generate code, but they are also similar in many ways in which they make use of the processor resources. So, it is not pure coincidence if you get 10 printed even when you try different compilers (even in different platforms). If you are not convinced with my explanation, (1) try out this code in different platforms (by platform, I mean a microprocessor, operating system and compiler combination) (2) take a look at the generated assembly code by different compilers for small pieces of code like this. Even if this program doesn’t work and print 10 in your platform, there is much to learn from this exercise. Now let us discuss one more aspect on local variables. If you declare some local variables, they would typically be allocated contiguously in the stack. Try this: int i = 0, j = 10, k = 0; int *p = &j;
  2. 2. *(p - 1) = 10; *(p + 1) = 10; printf(quot;i = %d j = %d k = %dquot;, i, j, k); // most compilers will print // i = 10 j = 10 k = 10 Here we attempt to check if the local variables i, j and k are contiguously allocated by modifying local variables i and k through a pointer p which points to the address of j. It is most likely that you’ll get values 10 for i, j and k. But things can go wrong also; for example, compilers can do optimizations and can decide that i and k are not modified in the program and statically replace i and k in printf with 0’s! S.G. Ganesh is a research engineer in Siemens (Corporate Technology). He has authored a book “Deep C” (ISBN 81-7656-501-6). You can reach him at sgganesh@gmail.com.

×