O operador && é um booleano e com avaliação de curto-circuito. Isso significa que ele só executa o segundo comando se o primeiro for bem-sucedido (ou seja, ele retorna 0 ). Uma maneira típica usada é algo assim:
tar xf file.tar && rm file.tar
Isso só remove o arquivo se a extração for bem-sucedida.
Seu script também contém um bom exemplo disso:
gcc foo.c && ./a.out
tentará executar o programa somente se gcc for bem-sucedido.
Se você deseja exibir $? , independentemente do sucesso de a.out , pode escrever:
gcc foo.c && { ./a.out ; echo $? ; }
O {...} agrupa o comando para que ambos sejam executados se a compilação for bem-sucedida.