Diz-se em C que quando os ponteiros se referem à mesma matriz ou a um elemento após o final dessa matriz, a aritmética e as comparações são bem definidas. Então, que tal um antes do primeiro elemento da matriz? Tudo bem desde que eu não desreferencie isso?
Dado
int a[10], *p;
p = a;
(1) É legal escrever --p
?
(2) É legal escrever p-1
em uma expressão?
(3) Se (2) estiver correto, posso afirmar isso p-1 < a
?
Existe alguma preocupação prática para isso. Considere uma reverse()
função que reverte uma string C que termina com '\0'
.
#include <stdio.h>
void reverse(char *p)
{
char *b, t;
b = p;
while (*p != '\0')
p++;
if (p == b) /* Do I really need */
return; /* these two lines? */
for (p--; b < p; b++, p--)
t = *b, *b = *p, *p = t;
}
int main(void)
{
char a[] = "Hello";
reverse(a);
printf("%s\n", a);
return 0;
}
Eu realmente preciso fazer a verificação no código?
Por favor, compartilhe suas idéias do ponto de vista jurídico / jurídico, e como você lidaria com essas situações.
c
language-lawyer
pointer-arithmetic
aafulei
fonte
fonte
Respostas:
É "legal", como a sintaxe C permite, mas invoca um comportamento indefinido. Com o objetivo de encontrar a seção relevante na norma,
--p
é equivalente ap = p - 1
(exceto quep
é avaliada apenas uma vez). Então:A avaliação invoca um comportamento indefinido, o que significa que não importa se você desassocia o ponteiro ou não - você já invocou um comportamento indefinido.
Além disso:
C17 6.5.6 / 9:
Se o seu código violar um "deve" no padrão ISO, ele invocará um comportamento indefinido.
O mesmo que (1), comportamento indefinido.
Como exemplos de como isso pode causar problemas na prática: imagine que a matriz seja colocada no início de uma página de memória válida. Quando você diminui fora dessa página, pode haver uma exceção de hardware ou uma representação de interceptação de ponteiro. Este não é um cenário completamente improvável para microcontroladores, principalmente quando eles estão usando mapas de memória segmentada.
fonte
*p == '\0'
no começo. Essa verificação pretende impedirp--
o loop for.'\0'
), haverá uma troca automática (troca consigo mesma) no final. Mas tudo bem. Além disso, aguarde um pouco mais para validação cruzada antes que eu possa fazer a marcação.p-1
em uma expressão for inválido,p=p-1
seria inválido. Ep--
ép=p-1
. Você argumentaria que decrementar um ponteiro é inválido?O uso desse tipo de aritmética de ponteiro é uma prática ruim de codificação, pois pode levar a um monte significativo de problemas difíceis de depurar.
Eu só precisava usar esse tipo de coisa uma vez em mais de 20 anos. Eu estava escrevendo uma função de retorno de chamada, mas não tinha acesso aos dados adequados. A função de chamada fornecia um ponteiro dentro de uma matriz adequada, e eu precisava do byte antes desse ponteiro.
Considerando que eu tinha acesso a todo o código-fonte e verifiquei o comportamento várias vezes para provar que consegui o que precisava e o revi por outros colegas, decidi que não há problema em deixá-lo ir para produção.
A solução adequada seria alterar a função de chamada para retornar o ponteiro adequado, mas isso não era viável, considerando tempo e dinheiro (a parte do software foi licenciada por terceiros).
Portanto,
a[-1]
é possível, mas deve ser usado SOMENTE com muito cuidado em situações muito particulares. Caso contrário, não há nenhuma boa razão para fazer esse tipo de vodu auto-prejudicial.Nota: em uma análise adequada, no meu exemplo, é óbvio que eu não acessei um elemento antes do início de uma matriz adequada, mas o elemento antes de um ponteiro, que estava garantido dentro da mesma matriz.
Referindo-se ao código fornecido:
reverse(a);
;reverse(a+1);
, porque você permanece dentro da matriz.fonte