Quais são todos os comportamentos indefinidos comuns que um programador C ++ deve saber? [fechadas]

StackOverflow https://stackoverflow.com/questions/367633

Pergunta

O que são todos os comportamentos indefinidos comuns que um programador C ++ deve saber?

Say, como:

a[i] = i++;

Foi útil?

Solução

Pointer

  • desreferenciação um NULL ponteiro
  • Dereferencing um ponteiro retornado por um "novo" alocação de tamanho zero
  • Usando ponteiros para objetos cuja vida útil tenha terminado (por exemplo, pilha alocados objetos ou objetos excluídos)
  • Dereferencing um ponteiro que ainda não foi definitivamente inicializado
  • Performing aritmética ponteiro que se obtém um resultado fora dos limites (acima ou abaixo) de uma matriz.
  • Dereferencing o ponteiro numa localização para além da extremidade de uma matriz.
  • Conversão de ponteiros para objetos de tipos incompatíveis
  • Usando memcpy para copiar sobreposição buffers .

buffer overflows

  • a leitura ou escrita de um objecto ou da matriz em um deslocamento que é negativa, ou para além do tamanho do referido objecto (pilha / pilha overflow)

Integer Overflows

  • Assinado integer overflow
  • Ao avaliar uma expressão que não está definido matematicamente
  • Esquerda de mudança de valores por um valor negativo (turnos direito por valores negativos são a implementação definido)
  • Mudando os valores por uma quantidade maior do que ou igual ao número de bits no número (por exemplo int64_t i = 1; i <<= 72 é indefinido)

Tipos, Gesso e Const

  • Fundição um valor numérico para um valor que não pode ser representado pelo tipo de alvo (quer directamente ou através de static_cast)
  • Usando uma variável automática antes de ser definitivamente atribuído (por exemplo, int i; i++; cout << i;)
  • Usando o valor de qualquer objeto do tipo diferente de volatile ou sig_atomic_t na recepção de um sinal
  • A tentativa de modificar um literal string ou qualquer outro objeto const durante sua vida útil
  • A concatenação de um estreito com uma ampla literal de cadeia durante a pré-processamento

Função e Template

  • Não retornar um valor de uma função que retorna valor (direta ou por que flui fora de um try-bloco)
  • várias definições diferentes para a mesma entidade (classe, modelo, enumeração, função inline, a função membro estático, etc.)
  • recursão infinita na instanciação de modelos
  • Chamar uma função usando diferentes parâmetros ou ligação com os parâmetros e ligação que a função é definida como a utilização.

OOP

  • destruições em cascata de objetos com duração de armazenagem estática
  • O resultado da atribuição de objectos parcialmente sobrepostos
  • Recursively re-entrar uma função durante a inicialização de seus objetos estáticos
  • Fazer chamadas de função virtual para funções virtuais puras de um objeto de seu construtor ou destrutor
  • No que se refere aos membros nonstatic de objetos que não foram construídos ou já tenham sido destruído

arquivo de origem e pré-processamento

  • Um arquivo não-vazia fonte que não termine com uma mudança de linha, ou extremidades com uma barra invertida (antes C ++ 11)
  • Uma barra invertida seguida por um caractere que não faz parte dos códigos de escape especificados em uma cadeia de caracteres ou constante (isto é implementação-definido em C ++ 11).
  • Exceder limites de implementação (número de blocos aninhados, número de funções em um programa, espaço de pilha disponível ...)
  • valores numéricos pré-processador que não podem ser representados por um long int
  • Pré-processamento directiva no lado esquerdo de uma função semelhante a definição de macro
  • dinamicamente gerar o definido token em uma expressão #if

Para ser classificado

  • Chamada de saída durante a destruição de um programa com duração de armazenagem estática

Outras dicas

A fim de que os parâmetros de função são avaliados é não especificado comportamento . (Isso não vai fazer o seu programa falhar, explodir, ou ordem pizza ... ao contrário de indefinido comportamento .)

A única exigência é que todos os parâmetros devem ser completamente avaliada antes que a função é chamada.


Este:

// The simple obvious one.
callFunc(getA(),getB());

Pode ser equivalente a esta:

int a = getA();
int b = getB();
callFunc(a,b);

Ou este:

int b = getB();
int a = getA();
callFunc(a,b);

Pode ser qualquer um; é até o compilador. O resultado pode ser importante, dependendo dos efeitos colaterais.

O compilador é livre para reordenar as partes avaliação de uma expressão (assumindo que o significado é inalterado).

A partir da pergunta original:

a[i] = i++;

// This expression has three parts:
(a) a[i]
(b) i++
(c) Assign (b) to (a)

// (c) is guaranteed to happen after (a) and (b)
// But (a) and (b) can be done in either order.
// See n2521 Section 5.17
// (b) increments i but returns the original value.
// See n2521 Section 5.2.6
// Thus this expression can be written as:

int rhs  = i++;
int lhs& = a[i];
lhs = rhs;

// or
int lhs& = a[i];
int rhs  = i++;
lhs = rhs;

dobro verificado bloqueio. E um erro fácil de fazer.

A* a = new A("plop");

// Looks simple enough.
// But this can be split into three parts.
(a) allocate Memory
(b) Call constructor
(c) Assign value to 'a'

// No problem here:
// The compiler is allowed to do this:
(a) allocate Memory
(c) Assign value to 'a'
(b) Call constructor.
// This is because the whole thing is between two sequence points.

// So what is the big deal.
// Simple Double checked lock. (I know there are many other problems with this).
if (a == null) // (Point B)
{
    Lock   lock(mutex);
    if (a == null)
    {
        a = new A("Plop");  // (Point A).
    }
}
a->doStuff();

// Think of this situation.
// Thread 1: Reaches point A. Executes (a)(c)
// Thread 1: Is about to do (b) and gets unscheduled.
// Thread 2: Reaches point B. It can now skip the if block
//           Remember (c) has been done thus 'a' is not NULL.
//           But the memory has not been initialized.
//           Thread 2 now executes doStuff() on an uninitialized variable.

// The solution to this problem is to move the assignment of 'a'
// To the other side of the sequence point.
if (a == null) // (Point B)
{
    Lock   lock(mutex);
    if (a == null)
    {
        A* tmp = new A("Plop");  // (Point A).
        a = tmp;
    }
}
a->doStuff();

// Of course there are still other problems because of C++ support for
// threads. But hopefully these are addresses in the next standard.

O meu favorito é "recursão infinita na instanciação de modelos", porque eu acredito que é o único onde o comportamento indefinido ocorre em tempo de compilação.

Atribuir a uma constante após a remoção constness usando const_cast<>:

const int i = 10; 
int *p =  const_cast<int*>( &i );
*p = 1234; //Undefined

Além de comportamento indefinido , há também o comportamento igualmente desagradável definido pela implementação .

comportamento indefinido ocorre quando um programa faz algo cujo resultado não é especificado pela norma.

comportamento definido pela implementação é uma ação por um programa cujo resultado não é definido pela norma, mas que a implementação é necessária para documento. Um exemplo é "literais de caracteres multibyte", de pergunta Stack Overflow Existe um compilador C que não consegue compilar este? .

comportamento definido pela implementação única morde você quando você começar a portar (mas o upgrade para nova versão do compilador também está portando!)

variáveis ??só pode ser actualizada de vez em uma expressão (tecnicamente, uma vez entre os pontos de sequência).

int i =1;
i = ++i;

// Undefined. Assignment to 'i' twice in the same expression.

Uma compreensão básica dos diferentes limites ambientais. A lista completa na secção 5.2.4.1 da especificação C. Aqui estão alguns;

  • 127 parâmetros em uma função de fi nição
  • 127 argumentos em uma chamada de função
  • 127 parâmetros em uma macro de definição
  • 127 argumentos em uma chamada de macro
  • 4095 caracteres em uma linha fonte lógico
  • 4095 caracteres em uma cadeia de caracteres literal string literal ou larga (depois concatenação)
  • 65535 bytes em um objeto (em um ambiente hospedado apenas)
  • níveis 15nesting para #included fi les
  • 1023 rótulos de caso para um interruptor declaração (excluindo aqueles para instruções switch anynested)

Eu estava realmente um pouco surpreso com o limite de 1023 casos de etiquetas para uma instrução switch, eu posso prever que ser ultrapassado por código / lex / analisadores gerados bastante easially.

Se estes limites forem ultrapassados, você tem um comportamento indefinido (travamentos, falhas de segurança, etc ...).

Certo, eu sei que isto é a partir da especificação C, mas as ações C ++ esses suportes básicos.

Usando memcpy para copiar entre sobreposição regiões de memória. Por exemplo:

char a[256] = {};
memcpy(a, a, sizeof(a));

O comportamento não está definida de acordo com o padrão C, o qual é absorvido pela C ++ 03 padrão.

7.21.2.1 A função memcpy

Sinopse

1 / #include void * memcpy (void * restringir s1, const void * restringir s2, size_t n);

Descrição

2 / A função memcpy cópias n caracteres do objeto apontado por s2 para o objecto apontada por s1. Se a cópia ocorre entre objetos que se sobrepõem, o comportamento é indefinido. Devoluções 3 A função memcpy retorna o valor de s1.

7.21.2.2 A função memmove

Sinopse

1 #include void * memmove (void * s1, const void * s2, size_t n);

Descrição

2 A função memmove cópias n caracteres do objeto apontado por s2 para o objeto apontado por s1. Copiar se passa como se o n caracteres do objeto apontado por s2 são primeiro copiados para uma matriz temporária de n caracteres que não se sobrepõe à objectos apontada por S1 e S2, e, em seguida, os n caracteres do temporária matriz são copiados para o objeto apontado por s1. Retorna

3 A função memmove retorna o valor de s1.

O único tipo para o qual C ++ garante um tamanho é char. E o tamanho é 1. O tamanho de todos os outros tipos é dependente de plataforma.

Namespace de nível objetos em um diferentes unidades de compilação nunca deve dependem uns dos outros para inicialização, porque a sua ordem de inicialização é indefinido.

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