How is a class organized in memory?

Asked

Viewed 1,008 times

16

According to this definition of memory:

In computing, memory refers to the physical devices used to store programs (sequence of instructions) or data (State information, for example) in a character program temporary or permanent for use on one computer or another digital electronic device.

source.

Following this definition was generated the following doubts that will be addressed at the end of the question. Below follows a class to serve as an example and illustration.

public class Pessoa
{
    //Constante
    private const string caminhoFoto = @"C:\fotos"; 

    //Propriedades 
    public string Nome { get; set; } = "";
    public int Idade { get; set; } = 0;

    public Pessoa(string nome, int idade)
    {
        Nome = nome;
        Idade = idade;
    }

    public Pessoa() { }

    //Metodos

    public List<String> Validar()
    {
        List<String> mensagemValidacao = new List<string>();
        //Metodo de "validação" para exemplificar.

        return mensagemValidacao;
    }

    public void Salvar()
    {
        //Metodo para "salvar no banco" para exemplificar.
    }
}

Example of Person class implementation:

Pessoa pessoa = new Pessoa("Jao", 21);
pessoa.Salvar();

Doubts

Following the definition at the beginning of the question, I would like you to clarify the following doubts:

  1. How is organized in the main memory a class (after being instaciated) and its characteristics as properties, constants and methods?
  2. Who is responsible for organizing the data in memory, the compiler of the language or the operating system?

I chose to use the C# language in the example, but you can cite examples of other languages as well.

3 answers

15


TL;DR

In fact the object is only a sequence of bytes equivalent to its fields instance and is totally isolated from the rest. The instance is just the simple object. The rest of what is in the class is something separate and stays in a static area in a unique way. The object (from your example), roughly speaking, will look like this:

|0A|B5|68|4F|00|00|00|00|
\-----------/\----------/
   string        int

This is the address (kicked) of the text (string) and then the number 0 as 32-bit integer. I considered that you are using 32-bit architecture to know the pointer size.

Introducing

I will make some simplifications, including the details may vary depending on the language. I’m going to talk about statically typed languages, the others may have a more abstract organization yet. Some things are implementation details and may change or be slightly different from what I will talk about.

The subject matter is complex and important for those who want to master computing more deeply. I could write a book here. I strongly recommend following the links of the answer and seek new information on its own to deepen and better understand some concepts. Unfortunately not everything that needs more details has an answer here on the site. Fit new questions.

I’m going to focus on C#. Java is very similar, but it lacks some resources that I will talk about. C++ is different in a few points: there is no difference between struct and class, instantiation defines where to allocate, the object header varies according to the allocator used and has nothing to facilitate the Garbage Collector, locking and polymorphism (unless you use it, but it’s out of allocation), and there are some facilities that C# lacks. Other languages, like D for example, work very similarly. It won’t be much different in Rust or Go or Pascal.

The class after being instantiated is the object. It has only been, that is, it will only have the data of the instance variables. The behaviors do not stay in the instance, even if it seems to be together. It is illusion.

Functioning of the CLR

C#, at least in its standard form, works with a Runtime complex that takes care of memory management, the Jitter, competition control, security, among other things. It has more details on What is really the "Runtime Environment"?. Java also, C++ has a Runtime very, very simple.

That goes for the .NET Core also. It is a little different, but are details. There is also the .NET Native that does not have the Jitter and is more simplified in some aspects (the Runtime of Mono may work similar).

Better understand about the "interpretation" of the binary code.

The Jitter takes the bytecode generated by the compiler and stored in what would be the executable and turns into native code. So every executable code ends up staying in a part of the heap. Even if I talk about methods in static area, it’s a heap static that is generated at runtime. See more in How the execution of a. NET application works?.

I won’t go into detail about the heap, your 3 generations of memory manager (Garbage Collector) and area of large objects, nor the heap unmanaged.

Read What are and where are the "stack" and "heap"?.

The CLR is responsible for the entire organization of the memory. Of course he needs to ask the operating system for permission to start working and allocate memory, but the organization is basically his, when he needs it he asks the OS to help him in something.

The organization can be seen here (image obtained according to the link posted by Bruno Costa):

Heap CLR

The . NET Core and Native do not have application domains.

Has a question with a C code that can help understand what it’s like to create a function on-the-fly. After Jitter does its post-compilation work, it generates a sequence of bytes and stores in heap static I spoke earlier.

The parts in memory

Methods

It is a mistake for people to imagine that the methods are next to the object. In fact the methods are simply functions and reside in a static area. And there is only one method for the class, instances share what is there, there is no reason to have one for each instance. After the Jitter creates it it is fixed (to optimize more is can be exchanged for an improved version when the Jitter realizes that it is widely used and has opportunities to make a more aggressive optimization).

It may sound strange, but all methods are actually static, using the terminology that C# programmers generally know. The difference between the declared static method and the instance method is that the instance method has a hidden parameter called this which receives the object to be manipulated. Yes, what you manipulate within the method is only manipulating the normal object received as parameter. Even when you don’t need to use the this, compiler puts it to you. I’m talking about something in When in fact I need to use this operator in Java? (I couldn’t find a more canonical one).

The functions only have the code to be executed. The spaces for the local variables of the functions are in the stack (one by one thread) and are piled up, as the name says, according to the calls. In the stack are only the basic values of the data. If it is a type by value the object will be there, and if it is a type by reference there will only have a reference to the object that will be in the heap (in C#).

Importantly, types by value can also be allocated to loggers because of Jitter optimizations, or on heap by being embedded in other objects that are there.

I have written more details about this in How Methods Management in C Memory Works#?.

All methods (Validar(), Salvar(), constructors, including the 4 methods that don’t seem to exist - see below) stay in static area, but in a heap special. These areas are abstract, do not think of a cute separation in memory.

The declared property will create two public methods to do the get and set of the private variables then have 4 methods to create (2 for each property) that don’t seem obvious. More details on New C# 6 functionality "Auto-Property initializers" is just a facilitator?. Ali does not speak in detail, but the cil code below demonstrates that the initialization of property fields occurs in the construction, in a method, actions (behavior) can only be within methods. I talk about in Methods and properties in C# - advantages and disadvantages

I have already said that functions, which are actually instance methods, receive a parameter this. A parameter is still a local variable and it will save "the object". Actually being a class what the variable actually holds is a reference (a pointer) to the object itself. Apparently the focus of the question is on this object, but it is important to show where the other things go "constant".

Constants

The constant caminhoFoto is in static area. Note that this specific variable will have a pointer and separately the object with the string in another part of the static area (for being string).

Constants or anything that is fully defined at compile time stay in this static area of code that will eventually go into memory. Obviously everything that stays in this area will never be modified, hence the static name.

The same goes for static variables in general (at least its value). The difference to a constant is that the latter can be optimized, that is, its value can be used in consumer codes directly. You can copy the value, do not need to create a reference for it as is common in variables and do not need to store its value somewhere, there is no indirect. Note that the value of a string in fact is the pointer to the actual text, the text would have to exist somewhere always.

Be careful not to confuse constant with immutable variable. Variables cannot be in static area, never. Some immutable objects can even, is quite the case strings. Obviously strings built at runtime will never be in static area.

All literals are quite common string stay in the static area, including making interning.

In C# and Java even relevant information about the class (or any other type of data) can stay in this area. These are the metadata, the polymorphism table (vtable), among other things. Bruno Costa’s response has an infographic showing in detail.

Object

The general allocation of a type by reference occurs in parts: the reference, which can be in several places (always in one variable), including by having several references to the same object; and the same object. Not everything you see there is together.

A diagram can help understand what goes on stack (what is not the object itself) and what goes on heap (without regard to generations). I will speak of the object itself.

Alocação CLR

Header

When the object is instantiated with the structure defined in the class the precise allocation of the initialized data, usually by the constructor, and of internal control data.

On that object, which is stored in heap, usually has a header. Each language will be in a way.

Has a word (size of a pointer) indicating what type that object is (Typehandle). This pointer is important for a number of reasons, from being used by garbage collector management to knowing what to do with it, to reflection (taking data of the type) and polymorphism (vtable, the virtual table of methods). I will not go into detail about this here, the subject goes far.

The type (an information-rich complex object (according to Bruno’s response infographic) is stored somewhere in the static area (as said above), so it has an address, it is this address that goes there in the header.

C# has one more word (Syncblock) for other Garbage Collector, lock when there is competition, cache hashcode and other things. Read more on article in English.

It’s a little more complex than that, but that’s the basis.

The minimum object will be 12 or 24 bytes (32/64 bit), even if there is no data on it (Empty class). One word extra is reserved for this case, but if you have data - the usual - that word is used for them. Object allocations are always done in groups of words, so in multiples of 4 or 8 bytes. If you need it makes an alignment (padding).

Object data

Then what comes into it is the object itself and will vary according to what has been defined within the class. What you have there is a simple sequence of bytes that correspond to the members declared in the class. That’s why a copy, called bit by bit, copies the whole object very simply (essentially it makes a memcpy() of the C).

For those who know C well it is easy to understand, since it is the equivalent of a struct. I think I can already understand that these higher-level languages only created new abstractions on top of what already existed.

So if you have a string and a int, the object of the example class in the question will have 8 or 12 bytes (but its total allocation will be increased by 8 or 16 bytes of the header).

The variable that is a string will occupy the space of a pointer since strings are by reference, so the truth text object will be elsewhere in the heap (yes, there will be a new allocation with a separate object, *string is already a class, although there may be an optimization pointing to a static area of memory). So I don’t know if its size is 4 or 8 bytes, it depends on whether it’s on 32-bit or 64-bit architecture.

The guy int in C# always has 4 bytes and is a type by value, so the object is already there, because it is like a struct and not a class.

Behold What’s the difference between Struct and Class?.

Property fields

C#, in this case, creates members with internal names that will resemble the one used in the property (Ex.: <Nome>k__BackingField), in the order they were declared in the class, or in a reorganized form to give better alignment in memory (joining members with less than one word to try to allocate them together in just one word). Such members shall be private and they shall be state of the object. It is not the property, which is the set of this private field with the pair of access methods.

When the compiler needs to access a private variable, within the class or even the Assembly (compilation unit), it only calculates where the access should be, it does not need to know the name. It is like in local variables, the name exists more for the programmer to understand, the compiler only needs addresses.

Variables that support properties are usually only indirectly accessed through their access and modifier methods, in theory only they will access their associated fields, but nothing prevents it from being different internally.

Access to the object

Access to these fields is controlled internally and determined by the compiler. It will always be the address of the object, which will be stored in some application variable, plus the member displacement (is a sum even).

In case you catch the string the displacement is zero. If we make an analogy with a array is element 0. You can only not make the full analogy because each member may have a different size whereas in array the size of all elements is equal.

The second instance variable would be like index 1, so to access it would be the address of the object plus the size of the first field (4 or 8 bytes).

Completion

I didn’t talk about much, for example I didn’t talk about the special conditions of the builders, when it has destroyers and finishers. Something may change in the future, and I may have simplified so much that I may have slipped into something.

It’s hard to keep track of all this without seeing, and understanding the whole process at once seems crazy. But if you think about it, it’s very simple. It doesn’t have as much ingenuity as it might seem, a little bit has :).

Recommended book.

If you want to understand better how CLR works and does all this has the source available. It’s no easy task :)

If you are interested in knowing the CIL code that will be generated, it can help to see some of the things I’m talking about. Note that the methods have space information that you need to reserve on stack which is automatically managed by the environment. Code is just a textual representation (even with comments) that a minimally trained human can understand. In fact the same code are just a few bytes that indicate this, the jumps of address numbers between the instructions gives an idea of how many bytes each takes.

.class private auto ansi '<Module>'
{
} // end of class <Module>

.class public auto ansi beforefieldinit Program
    extends [mscorlib]System.Object
{
    // Methods
    .method public hidebysig static 
        void Main () cil managed 
    {
        // Method begins at RVA 0x2050
        // Code size 22 (0x16)
        .maxstack 2
        .locals init (
            [0] class Pessoa
        )

        IL_0000: nop                  // Do nothing (No operation)
        IL_0001: ldstr "Jao"          // Push a string object for the literal string
        IL_0006: ldc.i4.s 21          // Push num onto the stack as int32, short form
        IL_0008: newobj instance void Pessoa::.ctor(string, int32) // Allocate an uninitialized object or value type and call ctor
        IL_000d: stloc.0              // Pop a value from stack into local variable 0
        IL_000e: ldloc.0              // Load local variable 0 onto stack
        IL_000f: callvirt instance void Pessoa::Salvar() // Call a method associated with an object
        IL_0014: nop                  // Do nothing (No operation)
        IL_0015: ret                  // Return from method, possibly with a value
    } // end of method Program::Main

    .method public hidebysig specialname rtspecialname 
        instance void .ctor () cil managed 
    {
        // Method begins at RVA 0x2072
        // Code size 8 (0x8)
        .maxstack 8

        IL_0000: ldarg.0              // Load argument 0 onto the stack
        IL_0001: call instance void [mscorlib]System.Object::.ctor() // Call method indicated on the stack with arguments
        IL_0006: nop                  // Do nothing (No operation)
        IL_0007: ret                  // Return from method, possibly with a value
    } // end of method Program::.ctor

} // end of class Program

.class public auto ansi beforefieldinit Pessoa
    extends [mscorlib]System.Object
{
    // Fields
    .field private static literal string caminhoFoto = "C:\\fotos"
    .field private string '<Nome>k__BackingField'
    .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = (
        01 00 00 00
    )
    .custom instance void [mscorlib]System.Diagnostics.DebuggerBrowsableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggerBrowsableState) = (
        01 00 00 00 00 00 00 00
    )
    .field private int32 '<Idade>k__BackingField'
    .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = (
        01 00 00 00
    )
    .custom instance void [mscorlib]System.Diagnostics.DebuggerBrowsableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggerBrowsableState) = (
        01 00 00 00 00 00 00 00
    )

    // Methods
    .method public hidebysig specialname 
        instance string get_Nome () cil managed 
    {
        .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = (
            01 00 00 00
        )
        // Method begins at RVA 0x207b
        // Code size 7 (0x7)
        .maxstack 8

        IL_0000: ldarg.0              // Load argument 0 onto the stack
        IL_0001: ldfld string Pessoa::'<Nome>k__BackingField' // Push the value of field of object (or value type) obj, onto the stack
        IL_0006: ret                  // Return from method, possibly with a value
    } // end of method Pessoa::get_Nome

    .method public hidebysig specialname 
        instance void set_Nome (
            string 'value'
        ) cil managed 
    {
        .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = (
            01 00 00 00
        )
        // Method begins at RVA 0x2083
        // Code size 8 (0x8)
        .maxstack 8

        IL_0000: ldarg.0              // Load argument 0 onto the stack
        IL_0001: ldarg.1              // Load argument 1 onto the stack
        IL_0002: stfld string Pessoa::'<Nome>k__BackingField' // Replace the value of field of the object obj with value
        IL_0007: ret                  // Return from method, possibly with a value
    } // end of method Pessoa::set_Nome

    .method public hidebysig specialname 
        instance int32 get_Idade () cil managed 
    {
        .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = (
            01 00 00 00
        )
        // Method begins at RVA 0x208c
        // Code size 7 (0x7)
        .maxstack 8

        IL_0000: ldarg.0              // Load argument 0 onto the stack
        IL_0001: ldfld int32 Pessoa::'<Idade>k__BackingField' // Push the value of field of object (or value type) obj, onto the stack
        IL_0006: ret                  // Return from method, possibly with a value
    } // end of method Pessoa::get_Idade

    .method public hidebysig specialname 
        instance void set_Idade (
            int32 'value'
        ) cil managed 
    {
        .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = (
            01 00 00 00
        )
        // Method begins at RVA 0x2094
        // Code size 8 (0x8)
        .maxstack 8

        IL_0000: ldarg.0              // Load argument 0 onto the stack
        IL_0001: ldarg.1              // Load argument 1 onto the stack
        IL_0002: stfld int32 Pessoa::'<Idade>k__BackingField' // Replace the value of field of the object obj with value
        IL_0007: ret                  // Return from method, possibly with a value
    } // end of method Pessoa::set_Idade

    .method public hidebysig specialname rtspecialname 
        instance void .ctor (
            string nome,
            int32 idade
        ) cil managed 
    {
        // Method begins at RVA 0x209d
        // Code size 43 (0x2b)
        .maxstack 8

        IL_0000: ldarg.0              // Load argument 0 onto the stack
        IL_0001: ldstr ""             // Push a string object for the literal string
        IL_0006: stfld string Pessoa::'<Nome>k__BackingField' // Replace the value of field of the object obj with value
        IL_000b: ldarg.0              // Load argument 0 onto the stack
        IL_000c: ldc.i4.0             // Push 0 onto the stack as int32
        IL_000d: stfld int32 Pessoa::'<Idade>k__BackingField' // Replace the value of field of the object obj with value
        IL_0012: ldarg.0              // Load argument 0 onto the stack
        IL_0013: call instance void [mscorlib]System.Object::.ctor() // Call method indicated on the stack with arguments
        IL_0018: nop                  // Do nothing (No operation)
        IL_0019: nop                  // Do nothing (No operation)
        IL_001a: ldarg.0              // Load argument 0 onto the stack
        IL_001b: ldarg.1              // Load argument 1 onto the stack
        IL_001c: call instance void Pessoa::set_Nome(string) // Call method indicated on the stack with arguments
        IL_0021: nop                  // Do nothing (No operation)
        IL_0022: ldarg.0              // Load argument 0 onto the stack
        IL_0023: ldarg.2              // Load argument 2 onto the stack
        IL_0024: call instance void Pessoa::set_Idade(int32) // Call method indicated on the stack with arguments
        IL_0029: nop                  // Do nothing (No operation)
        IL_002a: ret                  // Return from method, possibly with a value
    } // end of method Pessoa::.ctor

    .method public hidebysig specialname rtspecialname 
        instance void .ctor () cil managed 
    {
        // Method begins at RVA 0x20c9
        // Code size 27 (0x1b)
        .maxstack 8

        IL_0000: ldarg.0              // Load argument 0 onto the stack
        IL_0001: ldstr ""             // Push a string object for the literal string
        IL_0006: stfld string Pessoa::'<Nome>k__BackingField' // Replace the value of field of the object obj with value
        IL_000b: ldarg.0              // Load argument 0 onto the stack
        IL_000c: ldc.i4.0             // Push 0 onto the stack as int32
        IL_000d: stfld int32 Pessoa::'<Idade>k__BackingField' // Replace the value of field of the object obj with value
        IL_0012: ldarg.0              // Load argument 0 onto the stack
        IL_0013: call instance void [mscorlib]System.Object::.ctor() // Call method indicated on the stack with arguments
        IL_0018: nop                  // Do nothing (No operation)
        IL_0019: nop                  // Do nothing (No operation)
        IL_001a: ret                  // Return from method, possibly with a value
    } // end of method Pessoa::.ctor

    .method public hidebysig 
        instance class [mscorlib]System.Collections.Generic.List`1<string> Validar () cil managed 
    {
        // Method begins at RVA 0x20e8
        // Code size 13 (0xd)
        .maxstack 1
        .locals init (
            [0] class [mscorlib]System.Collections.Generic.List`1<string>,
            [1] class [mscorlib]System.Collections.Generic.List`1<string>
        )

        IL_0000: nop                  // Do nothing (No operation)
        IL_0001: newobj instance void class [mscorlib]System.Collections.Generic.List`1<string>::.ctor() // Allocate an uninitialized object or value type and call ctor
        IL_0006: stloc.0              // Pop a value from stack into local variable 0
        IL_0007: ldloc.0              // Load local variable 0 onto stack
        IL_0008: stloc.1              // Pop a value from stack into local variable 1
        IL_0009: br.s IL_000b         // Branch to target, short form
        IL_000b: ldloc.1              // Load local variable 1 onto stack
        IL_000c: ret                  // Return from method, possibly with a value
    } // end of method Pessoa::Validar

    .method public hidebysig 
        instance void Salvar () cil managed 
    {
        // Method begins at RVA 0x2101
        // Code size 2 (0x2)
        .maxstack 8

        IL_0000: nop                  // Do nothing (No operation)
        IL_0001: ret                  // Return from method, possibly with a value
    } // end of method Pessoa::Salvar

    // Properties
    .property instance string Nome()
    {
        .get instance string Pessoa::get_Nome()
        .set instance void Pessoa::set_Nome(string)
    }
    .property instance int32 Idade()
    {
        .get instance int32 Pessoa::get_Idade()
        .set instance void Pessoa::set_Idade(int32)
    }

} // end of class Pessoa
  • It was an excellent Peek-view of CLR.

  • Both in my question and in your absence a mandatory reference to the ECMA. Stay here for anyone interested in seeing in detail the CLI

  • It’s very useful, yes.

  • I didn’t quite understand this object header issue. In this case the object has a header that is initialized when the object is instantiated ?

  • 1

    @Diegofarias yes, every object by reference has this before the given itself. This is used for internal control of the Runtime. In fact on any platform you need a header at least to inform the size of the object. The only way to avoid this cost is to have separate arenas for each size you can allocate, which is virtually impossible to achieve in many situations and has several drawbacks. It would make a beautiful mess of memory and destroy performance.

5

What are the memory zones

To answer this question I think it’s a good idea to go back to the old man assembly.

In a program (which lately appears in instructions assembly), there are several memory zones, among which:

  • BSS and Date (data not initiated and initiated respectively, shared with other applications.)
  • Stack
  • Heap

Source

How are these memory zones used in C#

In C# the following operations result in the consequent memory zones

  • new of a class is always allocated in the heap
  • whenever you call a function, you increase the stack
  • within the functions new of a struct occupies space in the stack the size of this struct. While new of a class occupies only the size of a pointer in the stack

In C# there is no concept of uninitiated data, all data is started with default values, all 0, or null, therefore the BSS may not be used.

It is simple to imagine that constants may end up in the area Date.

Who are responsible for specifying memory zones and managing them

In a way, it can be said that there is no single person responsible for organising the data in these areas. Instead it can be said that each one deals with different things

  • The program specifies the amount of each type of memory (at compile time it is only important to know the memory Data and BSS)
  • The operating system tries to store these memory zones in different sections of the memory RAM
  • The programme shall manage the heap

The stack is normally managed automatically in languages other than low Assembly)

How methods are stored

In C# all kinds have Metadata, this is the same Metadata which can be obtained through API of reflection.

Within that Metadata, there is a table for the virtual methods, a table for the explicit implementations of Interface, note that this table contains only a pointer to the methods and not their code.

The code is stored in dll, in the Java is compiled to bytecodewhile in theC# the code is compiled to IL (which is also a bytecode but it has a different format than Java). The bytecode is then compiled to Assembly with the compiler JIT (just-in-time).

inserir a descrição da imagem aqui

Source

How is the object represented in memory

This image shows quite interesting information. You can see that an object occupies at least 8 bytes in 32 bits, or 16 bytes in 64 bits.

This is because all objects store a pointer that has information about the monitors (the monitors are synchronization objects), as well as a pointer to their type.

To this is added memory for each member of your class. It is usually also done padding so that the processor has the work facilitated to obtain the information from the memory instance.

How all this is shown in your example

When you create an instance of Pessoa

var pessoa = new Pessoa("Jao", 21);

An object is created with the following structure:

inserir a descrição da imagem aqui

Already the caminhoFoto is stored in the section Data and is not part of the object information.

You can use the image that has the method table to know what the structure of its type is Pessoa. And how their methods and interfaces are organized.

  • A better link to the first image would be https://web.archive.org/web/20150515023057/https://msdn.microsoft.com/en-us/magazine/cc163791.aspx. This link also has a lot of information related to the subject.

  • There are still a few things that may give a misinterpretation, but nothing that mine also does not have. I will delete here what is not relevant more.

3

In memory, everything is stored in the "address" = "value" format. No information is stored regarding the type or purpose for that value. Who takes care of checking the validity of each value (such as typing) and validity of each operation (such as validating sum between two numbers and invalidating sum of number with character in the case of a compiler of a strictly typed language) is the compiler.

Responding:

  1. In memory, variables will be stored in "address" = "value".

  2. The operating system is responsible for selecting the location in memory and allocating the assigned value. The compiler is responsible for validating all operations of the program to be compiled and providing control (read-in-memory instructions to the CPU, where it specifies how many bytes for each data type) between the variables to be used and the addresses selected by the OS at runtime (among a thousand other things).

Browser other questions tagged

You are not signed in. Login or sign up in order to post.