Pergunta

Existe uma maneira de portably determinar a limites superior e inferior em valores nulos-ponteiro em ANSI C89 / ISO C90? (atualmente eu não tenho uma cópia da norma comigo (eu tenho . um em casa) é claro que se os valores nulos-ponteiro está garantido para ser não assinado esta tarefa é trivial (via sizeof (void *)), no entanto, não me lembro se isso é garantido ou não posso pensar de alguns muito ineficiente. algoritmos (incremento até transbordar, etc.), mas eu gostaria de saber se alguém tem um relativamente barato (em termos de tempo de complexidade) e forma portátil para calcular estes limites.)

- EDIT -

Também:? Existe uma maneira portátil para determinar a validade dos valores de ponteiro

Por: Este surgiu em uma discussão com um colega de trabalho e isso me perplexo. Eu não sei o que ele está trabalhando, mas eu só quero saber porque eu estou interessado! : -)

Foi útil?

Solução

Não há nenhuma maneira portátil para determinar se um dado ponteiro é válido ou não. Você tem que saber que tipo de sistema de memória que você está lidando. Dependendo do sistema operacional e processador, que pode ou não pode ser uma maneira de consultar tabelas de páginas do gestor de memória virtual para determinar os intervalos válidos de ponteiros.

Por exemplo, no Linux, você pode examinar o arquivo mmap especial sob /proc para obter o mapa de memória virtual de um processo. Aqui está um exemplo de cat lendo em seu próprio mapa de memória:

$ cat /proc/self/mmap
08048000-0804c000 r-xp 00000000 09:00 5128276                            /bin/cat
0804c000-0804d000 rw-p 00003000 09:00 5128276                            /bin/cat
0804d000-0806e000 rw-p 0804d000 00:00 0                                  [heap]
f7ca7000-f7e40000 r--p 00000000 09:00 3409654                            /usr/lib/locale/locale-archive
f7e40000-f7e41000 rw-p f7e40000 00:00 0 
f7e41000-f7f68000 r-xp 00000000 09:00 2654292                            /lib/tls/i686/cmov/libc-2.3.6.so
f7f68000-f7f6d000 r--p 00127000 09:00 2654292                            /lib/tls/i686/cmov/libc-2.3.6.so
f7f6d000-f7f6f000 rw-p 0012c000 09:00 2654292                            /lib/tls/i686/cmov/libc-2.3.6.so
f7f6f000-f7f72000 rw-p f7f6f000 00:00 0 
f7f83000-f7f85000 rw-p f7f83000 00:00 0 
f7f85000-f7f9a000 r-xp 00000000 09:00 2637871                            /lib/ld-2.3.6.so
f7f9a000-f7f9c000 rw-p 00014000 09:00 2637871                            /lib/ld-2.3.6.so
ff821000-ff836000 rw-p 7ffffffea000 00:00 0                              [stack]
ffffe000-fffff000 r-xp ffffe000 00:00 0                                  [vdso]

Você pode ver os intervalos de ponteiros válidos, juntamente com os bits que indica se a memória é (r) eadable, ritable, e (x) ecutable, ou (p) reenviado (w) (ou seja, não paginada para o disco) .

Outras dicas

Os ponteiros são garantidos pela especificação para ser assinado. Mas por que diabos você quer encontrar os limites? "Tudo entre 0x00000001 e 0xffffffff" não é realmente um teste útil, pois o número de válido ponteiros será algum pequeno subconjunto do que isso.

void * é sempre grande o suficiente para manter um ponteiro para a memória endereçável. Qualquer outra utilização é estritamente proibida pela maior associação de beisebol da liga.

Exemplo: A dec-10 foi uma arquitetura de 36 bits com palavras de 36 bits. No entanto, os endereços eram 18 bits e você pode armazenar 2 ponteiros em qualquer registo / palavra.

Sim - que é um exemplo extremo. Se você deve fazer a matemática com ponteiros, sizeof é válido; mas fazer matemática ponteiro em outra coisa senão uma matriz contígua é dodgier do que desonesto.

Finalmente - nunca use um '* vazio' para armazenar um ponteiro para um objeto ou ponteiro para membro em C ++. Muitos implementação do compilador realmente usar vários ponteiros 'físico' para implementar múltiplos inheritence de concreto (ou parcialmente concreto) classes. Na realidade, isso quase nunca vem à tona porque muito poucas pessoas usam a herança múltipla, desta forma, e quando o fazem, muito raramente cortar e ponteiros unslice. Quando ele vem para cima, é realmente difícil descobrir o que aconteceu.

Você tem que discernir o valor inteiro em que uma void * pode ser lançado a partir do padrão de bits real na memória - lançando um void * para um tipo inteiro pode envolver conversões

Assumindo sizeof(void *) == sizeof(long), para uma void * p o seguinte pode muito bem ser falso:

((long)p) == *((long *)&p)

Além disso, a norma não especifica se há mesmo é um grande o suficiente tipo inteiro para manter os valores de todos os ponteiros válidos!

Portanto, não há apenas nenhuma maneira portátil para fazer o que você quer fazer ...

Além de uma região que corresponde a NULL, não há nenhuma restrição (portátil) nos endereços de memória em tudo. Um SO suficientemente endurecido pode utilizar vários mecanismos de CPU / OS para fornecer a cada processo com endereços aleatórios e bem distribuídos com cada chamada para malloc (), e a posição executável independente mais ASLR pode permitir a execução de código a partir de qualquer endereço bem.

Eu sei que no Win32, ponteiros de 64 bits são estendidos-sinal. É divertido para inspecionar um minidump de 32 bits a partir de uma máquina de 64 bits se você não assinar estender ponteiros.

Consulte aqui de como um 64 ponteiro bits (POINTER_64) funciona em Win32.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top