Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Address of global variables

I was just playing around with Go interfaces and structs that suddenly found something weird to me. This is the case:

https://play.golang.org/p/FgvRFV9Lij9

package main

import (
    "fmt"
)

func main() {
    scopedInt := 100
    fmt.Printf("%p\n", &scopedInt)
    
    globalInt = 100
    fmt.Printf("%p\n", &globalInt)
}

var globalInt int

output:

0xc0000ba010
0x57b2a8

The value of the addresses doesn't matter. The point is that Why is the number of digits of first address more than second?

I think I have missed a point about the concept of global variables in Go.

like image 535
shayan rok rok Avatar asked Jul 02 '21 11:07

shayan rok rok


People also ask

Where are global variables located?

Global variables are stored in the data section. Unlike the stack, the data region does not grow or shrink — storage space for globals persists for the entire run of the program.

What is the address of a variable?

An address is a non-negative integer. Each time a program is run the variables may or may not be located in same memory locations. Each time you run the program above may or may not result in the same output.

How do you refer to global variables?

The global Keyword Normally, when you create a variable inside a function, that variable is local, and can only be used inside that function. To create a global variable inside a function, you can use the global keyword.

Where are global variables stored in C?

In C, global variables are stored with the program code. I.e. the space to hold them is part of the object file (either in the data or bss section), instead of being allocated during execution (to either the stack or heap).

What is a global variable?

Variables that are created outside of a function (as in all of the examples above) are known as global variables. Global variables can be used by everyone, both inside of functions and outside.

How to access a global variable everywhere in C?

By default, global variables are of global scope. Which means we can access a global variable everywhere in same as well as other C programs (using extern ). First let us create a C program that contains only global variables, save the below program with name global.c.

What are the properties of global variables in C?

Properties of a global variable Global variables are allocated within data segment of program instead of C stack. Memory for global variable is allocated once and persists throughout the program. They are accessible to all function of the same and other programs (using extern).

How to read or write a global variable in the debugger?

The debugger interprets the name of a global variable as a virtual address. Any command that accepts an address as a parameter also accepts the name of a variable. Therefore, you can use all of the commands that are described in Accessing Memory by Virtual Address to read or write global variables. In addition, you can use the ?


Video Answer


2 Answers

The "length" of the addresses differs by many digits because those variables are allocated on different areas of the memory, which have different offsets (starting locations).

scopedInt will likely be allocated on the heap because it "escapes" from main() (its address is passed to fmt.Printf()), while globalInt is a package level variable and as such will be allocated in one of the fixed sized segments, the data segment.

The "length" of addresses doesn't matter as long as they point to a valid memory area. Go has automatic memory management, so unless you touch package unsafe, you don't have to worry about addresses and pointers being valid or not.

To read more about memory management, see Doug Richardson: Go Memory Management. Quoting from it:

What Goes Where?

The Go Programming Language Specification does not define where items will be allocated. For example, a variable defined as var x int could be allocated on the stack or the heap and still follow the language spec. Likewise, the integer pointed to by p in p := new(int) could be allocated on the stack or the heap.

However, certain requirements will exclude some choices of memory in certain conditions. For instance:

  • The size of the data segment cannot change at run time, and therefore cannot be used for data structures that change size.
  • The lifetime of items in the stack are ordered by their position on the stack. If the top of the stack is address X then everything above X will be deallocated while everything below X will remain allocated. Memory allocated by a function can escape that function if referenced by an item outside the scope of the function and therefore cannot be allocated on the stack (because it’s still being referenced), and neither can it be allocated in the data segment (because the data segment cannot grow at runtime), thus it must be allocated on the heap – although inlining can remove some of these heap allocations.
like image 192
icza Avatar answered Jan 04 '23 12:01

icza


Global uninitialized symbols like globalInt are stored in the BSS segment, close to the data segment, in the lower address space of the program.

You can check the addresses of program symbols with the nm utility:

$ go build main.go
$ go tool nm main | grep globalInt

Outputs:

118e210 B main.globalInt

The first hex in the output is the address of the symbol, which is what will be printed when you run the program. E.g. on my machine:

$ ./main
0xc000124008
0x118e210      <--- same as nm output

The letter B after the hex stands for bss segment symbol.

If you explicitly initialize the variable in the source code, like var globalInt int = 600 the output of nm will show:

114b268 D main.globalInt

where now D stands for data segment symbol.

Anyway all these are in the lower address space of the program. The scopedInt doesn't pass escape analysis and is allocated on the heap, where its address will be higher than the global var.


Consider that all this is implementation-dependent. The specs don't mandate where to allocate objects.

If you compile and run the same program with TinyGo, the output will not look alike:

$ tinygo build -o tinymain main.go
$ ./tinymain 
0x1158bf040
0x1061f3ca8
like image 35
blackgreen Avatar answered Jan 04 '23 12:01

blackgreen