Vivek A
Vivek A

Reputation: 107

How is memory managed for non-declared entities in the C language?

For example: In the following code, how and where is the number '10' used for the comparison stored?

#include<stdio.h>
#include<conio.h>

int main()
{
    int x = 5;
    if (x > 10)
        printf("X is greater than 10");
    else if (x < 10)
       printf("X is lesser than 10");
    else
        printf("x = 10");
    getch();
    return 0;
}

Pardon me for not giving enough details. Instead of initializing 'x' directly with '5', if we scan and get it from the user we know how memory is allocated for 'x'. But how memory is allocated for the literal number '10' which is not stored in any variable?

Upvotes: 9

Views: 277

Answers (2)

In your particular code, x is initialized to 5 and is never changed. An optimizing compiler is able to constant fold and propagate that information. So it probably would generate the equivalent of

int main() {
 printf("X is lesser than 10");
 getch();
 return 0;
}

notice that the compiler would also have done dead code elimination.

So both constants 5 and 10 would have disappeared.

BTW, <conio.h> and getch are not in standard C99 or C11. My Linux system don't have them.

In general (and depending upon the target processor's instruction set and the ABI) small constants are often embedded in some single machine code instruction (as an immediate operand), as Kilian answered. Some large constants (e.g. floating point numbers, literal strings, most const global or static arrays and aggregates) might get inserted and compiled as read only data in the code segment (then the constant inside machine register-load instructions would be an address or some offset relative to PC for PIC); see also this. Some architectures (e.g. SPARC, RISC-V, ARM, and other RISC) are able to load a wide constant in a register by two consecutive instructions (loading the constant in two parts), and this impacts the relocation format for the linker (e.g. in object files and executables, often in ELF).

I suggest to ask your compiler to emit assembler code, and have a glance at that assembler code. If using GCC (e.g. on Linux, or with Cygwin or MinGW) try to compile with gcc -Wall -O -fverbose-asm -S ; on my Debian/Linux system if I replace getch by getchar in your code I am getting:

        .section        .rodata.str1.1,"aMS",@progbits,1
.LC0:
        .string "X is lesser than 10"
        .text
        .globl  main
        .type   main, @function
main:
.LFB11:
        .cfi_startproc
        subq    $8, %rsp        #,
        .cfi_def_cfa_offset 16
        movl    $.LC0, %edi     #,
        movl    $0, %eax        #,
        call    printf  #
        movq    stdin(%rip), %rdi       # stdin,
        call    _IO_getc        #
        movl    $0, %eax        #,
        addq    $8, %rsp        #,
        .cfi_def_cfa_offset 8
        ret
        .cfi_endproc
.LFE11:
        .size   main, .-main
        .ident  "GCC: (Debian 4.9.2-10) 4.9.2"
        .section        .note.GNU-stack,"",@progbits

If you are using a 64 bits Windows system, your architecture is very likely to be x86-64. There are tons of documentation describing the ISA (see answers to this) and the x86 calling conventions (and also the Linux x86-64 ABI; you'll find the equivalent document for Windows).

BTW, you should not really care how such constants are implemented. The semantics of your code should not change, whatever the compiler choose to do for implementing them. So leave the optimizations (and such low level choices) to the compiler (i.e. your implementation of C).

Upvotes: 33

Kilian Foth
Kilian Foth

Reputation: 14366

The constant 10 is probably stored as an immediate constant in the opcode stream. Issuing a CMP AX,10, with the constant included in the opcode, is usually both smaller and faster than a CMP AX, [BX], where the comparison value must be loaded from memory.

If the constant is too large to fit into the opcode, the alternative is to store it in memory like a static variable, but if the instruction set allows embedded constants, a good compiler should use it - after all, that addressing mode was presumably added because it has advantages over the others.

Upvotes: 13

Related Questions