Un approccio comune è usare un intero (firmato). Tutto ciò che è negativo è "indefinito". Questo ha un vantaggio rispetto all'utilizzo di 0xFF come valore sentinella poiché è tecnicamente un numero positivo valido se si utilizza un char senza segno.
Puoi definire il sentinella come -1 oppure controllare che il tuo valore sia > 0.
#include <stdint.h>
#define UNDEFINED -1
uint8_t i = UNDEFINED;
...
i = some_value;
if (i>=0) { do something interesting } else { undefined }
Da una prospettiva di programmazione difensiva hai ancora un problema. Devi sempre controllare che la variabile sia > = 0 prima di usarla. È possibile ottenere un bug gnarly utilizzando un valore negativo in un calcolo.
L'altra soluzione di usare una struttura per contenere un bit "valido" ha lo stesso problema.
Per garantire la sicurezza, devi sempre accedere alla struttura dei dati tramite un'API per evitare di fare riferimento accidentalmente al valore dei dati quando la bandiera dice che non è valido.
typedef struct {
unsigned char valid:1;
unsigned char value:7;
} myType;
myType i ={0,0}; // initialize to invalid
...
i.valid=1;
i.value=some_value;
...
if (i.valid) {do something interesting } else { undefined }
Puoi vedere quanto sopra ha lo stesso potenziale per accedere a i.value quando non è valido. Quindi è necessario definire un'API per fare qualsiasi lavoro con questi dati per evitare che ciò accada. Ad esempio:
myType add_numbers(myType first, myType second)
{
myType returnValue = {0,0};
if (first.valid && second.valid)
{ returnValue.valid = TRUE;
returnValue.value = first.value + second.value;
// TODO deal with overflow here
}
return (returnValue);
}
Puoi evitare l'overhead della creazione di un'API complessa.
C'è un'altra soluzione con funziona bene con la lingua e impedirà qualsiasi riferimento di dati non intenzionale. Ti permette di avere veramente il concetto di un valore indefinito nel tuo codice. Questa soluzione è usare i puntatori. Un puntatore punterà a un elemento di dati valido o sarà indefinito, che è esattamente quello che stai cercando. Ovviamente, il rischio è che il tuo codice si arresti in modo anomalo se tenta di accedere a un valore indefinito a meno che tu non controlli sempre (quindi non puoi allontanarti da quello).
unsigned char * theData = NULL;
...
// data gets a value
unsigned char actualData = something;
theData = &actualData;
...
if (!theData) { do something interesting } else { undefined }
Suggerisco che la scelta dovrebbe essere regolata dalla tolleranza per l'errore e da come è possibile proteggersi dagli errori. L'approccio API è il più sicuro se ci si aspetta che altri utilizzino questo codice. Finché l'API viene seguita, i bug vengono ridotti al minimo. Gli altri due approcci sono aperti ai bug che determinano il calcolo del valore errato o il blocco del programma.