2016-01-13 17 views
8

Secondo http://www.cplusplus.com/reference/cstdlib/strtol/ questa funzione ha una firma di long int strtol (const char* str, char** endptr, int base).Come si implementa strtol con cost-correctness?

Mi chiedo, tuttavia: se viene passato un const char * all'inizio della stringa, come riesce a trasformarlo in un puntatore non costante al primo carattere non elaborato senza imbrogliare? Cosa sarebbe un'implementazione di strtol che non esegua un const_cast?

+0

Che dire "giocare" con un offset prendendo come direzione 'str' di base? – kazbeel

+3

Ci vorrebbe 'const char ** endptr' e abbiamo un'implementazione altrimenti uguale, ma vedi http://stackoverflow.com/questions/993700/are-strtol-strtod-unsafe perché no. –

+2

La risposta è "non può". Questo è stato risposto in un'altra domanda più generale, però. – dasblinkenlight

risposta

2

Molto probabilmente utilizza solo la trasmissione.

Esistono numerose funzioni che hanno questa stessa proprietà nella libreria standard. La possibilità di sacrificare la sicurezza sulla semplicità è probabilmente una ragione, dal momento che non è possibile sovraccaricare le funzioni come in C++.

Si aspettano che il programmatore si assuma la responsabilità e non modifichi endptr se str è, ad esempio, una stringa letterale.

Con il suo sistema di tipo limitato, C è un linguaggio pratico per le persone pratiche.

1

strtol fa un const_cast (o equivalente). La trasmissione di uno const via non è un problema, utilizzando il puntatore risultante per modificare il punto di origine originariamente - const.

Ma strtol restituisce questo puntatore a te senza alterarlo, quindi tutto va bene.

+1

Non tutto è a posto. È possibile passare un puntatore a un array const a 'strtol', e usare il risultante puntatore' * endptr' su (tentare di) modificare quell'array, il tutto senza usare un cast. È un difetto nella libreria standard C. –

+0

@KeithThompson è stato fatto un compromesso e un po 'di sicurezza del tipo è stato eliminato in favore di un'interfaccia migliore. Tuttavia, dal punto di vista dello standard, tutto va bene davvero: nessun UB o altri pericoli. – Quentin

+0

Guarda il programma di esempio nella mia risposta. La funzione stessa non viola la sicurezza 'const', ma abilita il codice utente che lo usa per farlo senza cast o avvertimenti. Un'interfaccia migliore fornirebbe due diverse funzioni, una per i dati 'const' e una per i dati non-' constst'. –

1

Come si implementa strtol con cost-correctness?

uso di C11 _Generic consentirebbe codice chiamare uno dei

// when passed argument for `str` is `char *` and for `endptr` is `char **` 
long strotol(const char* str, char** endptr, int base); 
// or 
// when passed argument for `str` is `const char *` and for `endptr` is `const char **` 
long strotol_c(const char* str, const char** endptr, int base); 
// and warn/error otherwise 

Un'implementazione, come sotto sarebbe identico è necessaria solo la conservazione firma funzione. Poiché questo differisce da strtol(), dovrebbe essere chiamato qualcos'altro come strtol_s().

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 

long int strtol_c(const char * restrict nptr, const char ** restrict endptr, int base) { 
    return strtol((char *) nptr, (char **) endptr, base); 
} 

#define strtol_s(n,e,b) _Generic(n, \ 
    char *: strtol((n), (e), (b)), \ 
    const char *: strtol_c((n), (e), (b)), \ 
    default: 0 \ 
) 

int main(void) { 
    char *src = malloc(100); 
    strcpy(src, "456"); 
    const char *srcc = "123"; 
    char *endptr; 
    const char *endcptr; 
    long L[6] = { 0 }; 

    // OK - matching str and *endptr 
    L[0] = strtol_s(src, &endptr, 0); 

    // warning: passing argument 2 of 'strtol' from incompatible pointer type 
    L[1] = strtol_s(src, &endcptr, 0); 

    // warning: passing argument 2 of 'strtol_c' from incompatible pointer type 
    L[2] = strtol_s(srcc, &endptr, 0); 

    // OK - matching str and *endptr 
    L[3] = strtol_s(srcc, &endcptr, 0); 

    L[4] = strtol(src, &endptr, 0); 
    // warning passing argument 2 of 'strtol' from incompatible pointer type 

    // OK 
    L[5] = strtol(src, &endcptr, 0); 
    return !L[0]; 
} 

ciò che è perduto: strtol_s() non è una vera funzione, in modo da un puntatore ad esso non può essere fatta.

come riesce a trasformarlo in un puntatore non costante al primo carattere non elaborato senza imbrogliare?

strtol(), anche se ci vuole un char **endptr come secondo argomento, non modifica *endptr.

4

Come implementare strtol in modo corretto?

non lo fate, perché la definizione 's strtol non è intrinsecamente const -correct.

Questo è un difetto nella libreria standard C.

Esistono diverse funzioni standard che accettano un argomento const char* (si prevede che puntino all'inizio di un array di caratteri) e restituiscano un puntatore non constchar* che può essere utilizzato per modificare quell'array.

strchr è un esempio:

char *strchr(const char *s, int c); 

Ad esempio:

comportamento
#include <string.h> 
int main(void) { 
    const char *s = "hello"; 
    char *ptr = strchr(s, 'h'); 
    *ptr = 'H'; 
} 

Questo programma è indefinito. Sul mio sistema, muore con un errore di segmentazione.

Il problema non si verifica nello stesso strchr. Promette di non modificare la stringa che gli passi, e non lo fa. Ma restituisce un puntatore che il chiamante può quindi utilizzare per modificarlo.

Il comitato ANSI C, di nuovo alla fine del 1980, potrebbe hanno dividere ogni tale funzione in due versioni, una che agisce sul const array di caratteri e un altro per i non const array:

char *strchr(char *s, int c); 
const char *strcchr(const char *s, int c); 

Ma che avrebbe rotto il codice pre-ANSI esistente, scritto prima dello const esistente. Questa è la stessa ragione per cui C non ha creato stringhe letterali const.

C++, che eredita la maggior parte della libreria standard di C, si occupa di ciò fornendo versioni sovraccaricate di alcune funzioni.

La riga di fondo è che è, come programmatore C, sono responsabili per non modificare gli oggetti definiti come const. Nella maggior parte dei casi, la lingua ti aiuta a far rispettare questo, ma non sempre.

Quanto a come queste funzioni riescono a restituire un puntatore non const ai dati const, probabilmente basta usare un cast internamente (non un const_cast, che esiste solo in C++). Ciò presuppone che siano implementati in C, che è probabile ma non richiesto.

Problemi correlati