Reputation: 125
I was trying to wrap my head around the concept of a variable.
Obviously it is implicitly clear how a variable works. However, I was trying to explicate my implicit knowledge and ran into some difficulties. Here is what I came up with:
A variable is a container of a certain size. The size is both dependant of the data type in the declaration of the variable and the hardware (what specifically? word size?). A variable has an address in memory that is stored within the preallocated size of that container (how is the name of the variable connected to its memory address?). Within the reserved spot in memory for that variable, a value that corresponds to the data type of the declaration can be stored.
What of that is wrong or not precise (I'm sure much)? How can it be explained better?
Upvotes: 0
Views: 67
Reputation: 223389
In C, a variable consists of two things: an identifier and an object.
An identifier is a string of text that is used in source code to denote the object. (Identifiers may also denote functions, structure members, and other things.)
An object is “a region of data storage in the execution environment, the contents of which can represent values” (C 2018 3.1.15 1).
We generally think of an object has having a certain type. The type determines the meaning of the value stored in an object—the same bits may mean 3.75 when interpreted as a float
but 1,081,081,856 when interpreted as an int
. The C standard defines some properties of how types are represented (such as that some form of binary is used for integers) and requires C implementations to define the rest (except for certain aspects of bit-fields).
Therefore the “final say” on how any object is represented is up to each C implementation. Most C implementations are influenced by the hardware, as they are designed to work efficiently on their target systems, but a C implementation may provide 37-bit int
objects on hardware that uses 32-bit words.
Earlier, I said we generally think of an object has having a certain type. When an identifier for an object is defined, storage is reserved for it. The amount of that storage is determined by the type. However, the actual interpretation of the value of an object depends on the expression used to access it. Almost all the time, we access an object using its declared type: After declaring float x;
, we use x = 3.75; printf("%g\n", x);
, and so on, and the type used to access the object in these expressions is float
, the declared type of x
. But C is flexible and allows us to set a char
pointer to the memory using char *p = (char *) &x;
, and then we can access the bytes of x
using c[0]
, c[1]
, and so on. In this case, the type of the expression used to access the object, or its parts, is char
, so we get char
values instead of float
values when using these expressions to access the object.
The compiler knows and arranges the connection between an identifier and its storage (memory). When an identifier for an object is defined, the compiler will plan storage for it (subject to program optimization by the compiler). That storage may be in a data section of the program or in the stack section or somewhere else. The compiler knows of ways to refer to the storage. Locations in the stack may be referred to by offsets relative to a stack pointer or a frame pointer. Locations in data sections may be referred to by offsets relative to a base address stored in a particular register by the program loader. Locations may be referred to by offsets relative to section starts or by absolute memory addresses. Whatever the case may be, when the compiler needs to generate instructions that access an object, it generates suitable instructions. This may be an instruction that includes in the instruction itself an offset relative to the stack pointer. Or it could be two or more instructions that add the offset to a base register and then use the result to access memory. Or it could be a partially generated instruction that is later completed when the program loader adjusts it to have the final address.
Upvotes: 3