A marcação do ponteiro em C é indefinida de acordo com o padrão?

Algumas linguagens de tipo dinâmico usammarcação de ponteiro como uma maneira rápida de identificar ou restringir o tipo de tempo de execução do valor que está sendo representado. Uma maneira clássica de fazer isso é converter ponteiros em um número inteiro de tamanho adequado e adicionar um valor de tag sobre os bits menos significativos que são assumidos como zero para objetos alinhados. Quando o objeto precisa ser acessado, os bits da tag são ocultados, o número inteiro é convertido em um ponteiro e o ponteiro é desreferenciado normalmente.

Isso, por si só, está em ordem, exceto que tudo depende de uma suposição colossal: que o ponteiro alinhado será convertido em um número inteiro que garante zero bits nos lugares certos.

É possível garantir isso de acordo com ascarta do padrão?

Embora a seção 6.3.2.3 padrão (as referências sejam ao rascunho C11) diga que o resultado de uma conversão de ponteiro em número inteiro é definido pela implementação, o que eu quero saber é se a aritmética do ponteiro governa efetivamente em 6.5.2.1 e 6.5.6 restrinja o resultado da conversão ponteiro-> inteiro a seguir as mesmas regras aritméticas previsíveis que muitos programas já assumem. (6.3.2.3, nota 67 aparentemente sugere que este é o objetivoespírito do padrão de qualquer maneira, não que isso signifique muito.)

Estou pensando especificamente no caso em que alguém pode alocar uma matriz grande para atuar como uma pilha para a linguagem dinâmica e, portanto, os ponteiros de que estamos falando são os elementos dessa matriz. Estou assumindo que o início da própria matriz alocada em C possa ser colocado em uma posição alinhada por alguns meios secundários (por todos os meios, discutimos isso também). Digamos que temos uma matriz de "células contras" de oito bytes; podemos garantir que o ponteiro para qualquer célula seja convertido em um número inteiro com os três bits mais baixos livres para uma tag?

Por exemplo:

typedef Cell ...; // such that sizeof(Cell) == 8
Cell heap[1024];  // such that ((uintptr_t)&heap[0]) & 7 == 0

((char *)&heap[11]) - ((char *)&heap[10]); // == 8
(Cell *)(((char *)&heap[10]) + 8);         // == &heap[11]
&(&heap[10])[0];                           // == &heap[10]
0[heap];                                   // == heap[0]

// So...
&((char *)0)[(uintptr_t)&heap[10]];        // == &heap[10] ?
&((char *)0)[(uintptr_t)&heap[10] + 8];    // == &heap[11] ?

// ...implies?
(Cell *)((uintptr_t)&heap[10] + 8);        // == &heap[11] ?

(Se bem entendi, se uma implementação forneceruintptr_t então o comportamento indefinido sugerido no item 6.3.2.3, parágrafo 6, é irrelevante, certo?)

Se tudo isso for válido, eu suporia que isso significa que você pode, de fato, confiar nos bits baixos de qualquer ponteiro convertido em um elemento de um alinhadoCell matriz para ser livre para marcação. Eles fazem isso?

(Pelo que sei, essa pergunta é hipotética, já que a suposição normal vale para plataformas comuns de qualquer maneira, e se você encontrou uma onde não encontrou, provavelmente não gostaria de procurar o padrão C para obter orientação, e não o documentos da plataforma; mas isso não vem ao caso.)

questionAnswers(2)

yourAnswerToTheQuestion