Different outputs in different compilers

2

I have this code:

#include <iostream>
int main(int x=1) {
  while (x <= 1000 && std::cout << x++ << std::endl) {}
 }

I wrote it in Gedit, using Debian, and compiled it with Debian's own g ++, without ever changing anything. The compiler never presented problems with other codes. But in my code the output I get is the range from 2 to 1000.

And if I compile using this site link , for example, the output is from 1 to 1000.

Something interesting is that if I change the type of validation within while , less than or equal to 1000 to less than 1000, only, the program starts to display the number 1.

What's going on?

NOTE: The code was developed for a challenge. Therefore, there is no better way to develop the same output.

Assembly Code:

.file   "tes.cpp"
    .local  _ZStL8__ioinit
    .comm   _ZStL8__ioinit,1,1
    .text
    .globl  main
    .type   main, @function
main:
.LFB969:
    .cfi_startproc
    pushl   %ebp
    .cfi_def_cfa_offset 8
    .cfi_offset 5, -8
    movl    %esp, %ebp
    .cfi_def_cfa_register 5
    andl    $-16, %esp
    subl    $16, %esp
.L4:
    cmpl    $1000, 8(%ebp)
    jg  .L2
    movl    8(%ebp), %eax
    addl    $1, 8(%ebp)
    movl    %eax, 4(%esp)
    movl    $_ZSt4cout, (%esp)
    call    _ZNSolsEi
    movl    $_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_, 4(%esp)
    movl    %eax, (%esp)
    call    _ZNSolsEPFRSoS_E
    movl    (%eax), %edx
    subl    $12, %edx
    movl    (%edx), %edx
    addl    %edx, %eax
    movl    %eax, (%esp)
    call    _ZNKSt9basic_iosIcSt11char_traitsIcEEcvPvEv
    testl   %eax, %eax
    je  .L2
    movl    $1, %eax
    jmp .L3
.L2:
    movl    $0, %eax
.L3:
    testb   %al, %al
    jne .L4
    movl    $0, %eax
    leave
    .cfi_restore 5
    .cfi_def_cfa 4, 4
    ret
    .cfi_endproc
.LFE969:
    .size   main, .-main
    .type   _Z41__static_initialization_and_destruction_0ii, @function
_Z41__static_initialization_and_destruction_0ii:
.LFB978:
    .cfi_startproc
    pushl   %ebp
    .cfi_def_cfa_offset 8
    .cfi_offset 5, -8
    movl    %esp, %ebp
    .cfi_def_cfa_register 5
    subl    $24, %esp
    cmpl    $1, 8(%ebp)
    jne .L6
    cmpl    $65535, 12(%ebp)
    jne .L6
    movl    $_ZStL8__ioinit, (%esp)
    call    _ZNSt8ios_base4InitC1Ev
    movl    $__dso_handle, 8(%esp)
    movl    $_ZStL8__ioinit, 4(%esp)
    movl    $_ZNSt8ios_base4InitD1Ev, (%esp)
    call    __cxa_atexit
.L6:
    leave
    .cfi_restore 5
    .cfi_def_cfa 4, 4
    ret
    .cfi_endproc
.LFE978:
    .size   _Z41__static_initialization_and_destruction_0ii, .-_Z41__static_initialization_and_destruction_0ii
    .type   _GLOBAL__sub_I_main, @function
_GLOBAL__sub_I_main:
.LFB979:
    .cfi_startproc
    pushl   %ebp
    .cfi_def_cfa_offset 8
    .cfi_offset 5, -8
    movl    %esp, %ebp
    .cfi_def_cfa_register 5
    subl    $24, %esp
    movl    $65535, 4(%esp)
    movl    $1, (%esp)
    call    _Z41__static_initialization_and_destruction_0ii
    leave
    .cfi_restore 5
    .cfi_def_cfa 4, 4
    ret
    .cfi_endproc
.LFE979:
    .size   _GLOBAL__sub_I_main, .-_GLOBAL__sub_I_main
    .section    .init_array,"aw"
    .align 4
    .long   _GLOBAL__sub_I_main
    .hidden __dso_handle
    .ident  "GCC: (Debian 4.7.2-5) 4.7.2"
    .section    .note.GNU-stack,"",@progbits
    
asked by anonymous 18.05.2014 / 23:14

3 answers

3

Your code uses a main function with the signature int main(int) , which is illegal , as other answers have already quoted. However, GCC accepts such a signature by generating only a warning, not an error. The code is compiled smoothly and does as expected. The reason for the behavior is not there. (although it's something that should be fixed anyway).

Passing a default value for this argument makes no sense since the system will always call the main function by explicitly passing the value. In case it will be 1 if you call the program without any parameters on the command line, 2 if you use a parameter, 3 for two parameters and so on. Note that in assmbly it is clear and no shadow of doubt that the first value displayed on the screen is the argument passed to the function main .

main:                              // Início da função 'main'
.LFB969:                           // Prólogo: Alocar 16 bytes de stack
    .cfi_startproc
    pushl   %ebp
    .cfi_def_cfa_offset 8
    .cfi_offset 5, -8
    movl    %esp, %ebp
    .cfi_def_cfa_register 5
    andl    $-16, %esp
    subl    $16, %esp              // Fim do prólogo.
.L4:
    cmpl    $1000, 8(%ebp)         // Comparar o argumento com 1000
    jg  .L2                        // Se for maior, pule para L2. Não é.
    movl    8(%ebp), %eax          // Copie o argumento para EAX
    addl    $1, 8(%ebp)            // Incremente o argumento
    movl    %eax, 4(%esp)          // Prepare para passar EAX como argumento
    movl    $_ZSt4cout, (%esp)     // Prepare para passar std::cout como argumento
    call    _ZNSolsEi              // Chame 'std::cout << EAX';

Given this, there is no question that the first value that exits the screen is the argument of the main function. If you see 2 is why you passed a parameter on the command line when you invoked your executable. There is no other possibility.

    
21.05.2014 / 03:51
3

The problem is that the main function should only receive 0 or 2 arguments (int argc, char ** argv), according to the C ++ standard. Because your code does not conform to the C ++ rules, each compiler can treat you differently. G ++, for example, generates a warning with its prototype (use -Wmain, if it is not showing for you), but it seems to treat it as if it were argc.

As a general rule, make sure your code conforms to the C ++ standard and you will have less trouble switching compiler / platform.

The shapes defined by the pattern are as follows: int main() {

or

int main(int argc, char** argv) {

    
20.05.2014 / 19:14
2

When building your program in C ++ you have some options for declaring the input function called main . The two most common ones are:

1 - Without worrying about receiving and handling binary / executable command line arguments. For example:

#include <iostream>
int main() {
    std::cout << "Ola mundo!" << std::endl;
    return 0;
}

2 - Receiving and handling binary / executable command line arguments. For example:

#include <iostream>
int main(int argc, char** argv) {
    std::cout << "Ola mundo!" << std::endl;
    std::cout << "Total de argumentos: " << argc << std::endl;
    for(int i = 0; i < argc; i++)
        std::cout << "Argumento #" << i << " = [" << argv[i] << "]" << std::endl;
    return 0;
}

But the fact is that the definition of the prototype of this function in many compilers allows some variations. In Visual Studio 2013, for example, the "signature" of the prototype is described as in the documentation :

  

int main (int argc [ char * argv [] [ char * envp []]]);

The brackets (characters [ and ] ) serve in this context only to indicate the optionality of the function parameters (that is, parameters in brackets are optional).

Note: Note that it is also possible to receive and treat environment variables with the parameter envp .

Another detail is that the C ++ language allows you to indicate, when defining your own functions, parameters that are optional. This is done simply by setting a default value that should be used if the parameter is not used / informed in the call.

So when building your code the way you did, you simply used only the first parameter ( argc , which contains the number of binary command line arguments / executable, although you have renamed it to x ). Since the second (and third) parameter of main is optional, it will (most likely - this depends on the compiler implementation) receive the NULL value.

In addition, you simply reset the default value of this argument to 1 . In practice, simply having it there always means that you always get the path and name of your own binary / executable (according to the documentation, this is always the value of argv [0], regardless of whether you provide it or no additional arguments on the command line), so this default value is simply useless.

This all I've argued does not answer the fact (alleged by you, but still unproven: I simply could not reproduce in my local tests with VS 2013) of the result change depending on the use of minor ( < ) or less or equal (% with%). Still, knowing the documentation and the expected behavior of the <= function, I tend to believe that the value variation of its main variable between x and 1 stems from you being passing some argument on the command line (even without realizing it).

Anyway, if your interest is to receive via the command line the number that should be used to start your count, you can do as follows:

#include <iostream>
int main(int argc, char** argv)
{
    // Validação dos argumentos da linha de comando
    if(argc != 2)
    {
        std::cerr << "Sintaxe: " << argv[0] << " <valor inicial>" << std::endl;
        return -1;
    }

    int x = atoi(argv[1]);
    while (x <= 1000 && std::cout << x++ << std::endl) {}
    return 0;
}
    
20.05.2014 / 20:49