O código é o que veremos abaixo:
#include <stdio.h>
#include <stdlib.h>
void func()
{
*(int *)0 = 0;
return 0;
}
int main(int argc, char **argv)
{
func();
return 0;
}
Bem, para testar a compilação basta compilar. Porém, se estivermos em uma entrevista, geralmente não existe nenhum compilador em um raio de uma sala de reunião senão seu próprio cérebro.
E é nessas horas que os entrevistadores testam se você tem um bom cérebro ou um bom currículo.
Por isso, vamos analisar passo a passo cada bloco de código e entender o que pode estar errado. Se não encontrarmos, iremos supor que está tudo certo.
#include <stdio.h>
#include <stdlib.h>
Dois includes padrões, ultranormal, nada de errado aqui.
void func()
{
*(int *)0 = 0;return 0;
}
Duas ressalvas aqui: a primeira quanto ao retorno da função é void, porém a função retorna um inteiro. Na linguagem C, isso funciona, no máximo um warning do compilador. Em C++, isso é erro brabo de tipagem.
A segunda ressalva diz respeito à linha obscura, sintaticamente correta, mas cuja semântica iremos guardar para o final, já que ainda falta o main para analisar.a
int main(int argc, char **argv)
{
func();
return 0;
}
A clássica função inicial, nada de mais aqui. Retorna um int, e de fato retorn. Chama a função func, definida acima.
A linha obscura
A linha que guardamos para analisar contém uma operação de casting, atribuição e deferência, sendo o _casting _executado primeiro, operador unário que é, seguido pelo segundo operador unário, a deferência. Como sempre, a atribuição é uma das últimas. Descomprimida a expressão dessa linha, ficamos com algo parecido com as duas linhas abaixo:
int* p = (int*) 0;
*p = 0;
Não tem nada de errado em atribuir o valor 0 a um ponteiro, que é equivalente ao define NULL da biblioteca C (e C++). De acordo com a referência GNU, é recomendado o uso do define, mas nada impede utilizar o 0 "hardcoded".
Porém, estamos escrevendo em um ponteiro nulo, o que com certeza é um comportamento não-definido de conseqüências provavelmente funestas. O ponteiro nulo é um ponteiro inválido que serve apenas para marcar um ponteiro como inválido. Se escrevermos em um endereço inválido, bem, não é preciso ler o padrão para saber o que vai acontecer =)
Atualização
Em código. Isso pode:
int* p = 0; // atribuindo nulo a um ponteiro int* p2 = p; // isso também pode
Isso não pode:
*p = 15; // <strong>NUNCA</strong> acessar ponteiros nulos int x = *p; // isso também <strong>não pode</strong>, ler de um ponteiro nulo
Dito isso, me sinto melhor =)