Perché i puntatori non sono consigliati quando si codifica con C ++?


45

Ho letto da qualche parte che quando si utilizza C ++ si consiglia di non utilizzare i puntatori. Perché i puntatori sono una cattiva idea quando si utilizza C ++? Per i programmatori C che sono abituati a usare i puntatori, qual è l'alternativa e l'approccio migliori in C ++?


40
si prega di collegarsi a "da qualche parte". Il contesto potrebbe essere molto rilevante.

1
Questa domanda si spera sia utile per te.
Garet Claborn,

La maggior parte di queste risposte si riferisce all'evitare perdite di memoria come motivo principale. Non ricordo l'ultima volta che una delle nostre app ha avuto un problema di perdita di memoria nonostante l'utilizzo di puntatori. Se hai problemi di perdita di memoria, non stai utilizzando gli strumenti giusti o non sai cosa stai facendo. La maggior parte degli ambienti di sviluppo ha un modo per verificare automaticamente la presenza di perdite integrate. Penso che i problemi di perdita di memoria siano molto più difficili da rintracciare nei linguaggi di raccolta dei rifiuti perché la loro occorrenza è molto più sottile e spesso è necessario uno strumento di terze parti per rintracciare il colpevole .
Dunk

1
Aggiungendo al commento di @Dunk, a volte i garbage collection integrati in linguaggi di livello superiore semplicemente non funzionano correttamente. Il Garbage Collector di ActionScript 3 non lo fa, ad esempio. In questo momento c'è un bug che ha a che fare con le NetConnectionistanze che si disconnettono dal server ( stackoverflow.com/questions/14780456/… ), nonché un problema con la presenza di più oggetti in un programma che rifiuterà specificamente di raccogliere mai ...
Panzercrisis,

... ( adobe.com/devnet/actionscript/learning/as3-fundamentals/… - cerca GCRoots are never garbage collected.e il paragrafo è iniziato da The MMgc is considered a conservative collector for mark/sweep.). Tecnicamente questo è un problema in Adobe Virtual Machine 2, non in AS3 stesso, ma quando hai problemi come questo in linguaggi di livello superiore, che hanno essenzialmente la garbage collection integrata, spesso non hai un vero modo all'interno del linguaggio per eseguire il debug questi problemi completamente fuori dal programma. ...
Panzercrisis,

Risposte:


58

Penso che significhino che dovresti usare puntatori intelligenti invece di puntatori regolari.

Nell'informatica, un puntatore intelligente è un tipo di dati astratto che simula un puntatore fornendo allo stesso tempo funzionalità aggiuntive, come la garbage collection automatica o il controllo dei limiti. Queste funzionalità aggiuntive hanno lo scopo di ridurre i bug causati dall'uso improprio dei puntatori mantenendo l'efficienza. I puntatori intelligenti in genere tengono traccia degli oggetti a cui puntano ai fini della gestione della memoria.

L'uso improprio dei puntatori è una delle principali fonti di bug: l'allocazione, la deallocazione e il riferimento costanti che devono essere eseguiti da un programma scritto utilizzando i puntatori introducono il rischio che si verifichino perdite di memoria. I puntatori intelligenti tentano di prevenire perdite di memoria rendendo automatica la deallocazione delle risorse: quando il puntatore (o l'ultimo di una serie di puntatori) su un oggetto viene distrutto, ad esempio perché esce dall'ambito, anche l'oggetto appuntito viene distrutto.

In C ++ l'enfasi sarebbe sulla garbage collection e sulla prevenzione delle perdite di memoria (solo per citarne due). I puntatori sono una parte fondamentale del linguaggio, quindi non usarli è praticamente impossibile se non nella maggior parte dei programmi.


22
in genere non è strettamente una raccolta dei rifiuti in senso classico, più conteggio dei riferimenti. Almeno nel ptr intelligente a cui sono abituato ([boost | std] :: shared_ptr)
Doug T.

3
Questa risposta è molto limitata al puntatore intelligente, che è solo un piccolo aspetto del problema. Inoltre, i puntatori intelligenti non sono garbage collection.
deadalnix

3
Anche RAII in generale.
Klaim

3
No, non è questo il significato. È solo un aspetto, e non il più importante.
Konrad Rudolph,

Penso che i puntatori intelligenti siano l'aspetto più importante, ma sono d'accordo che ce ne sono molti altri.
DeadMG

97

Dal momento che sono io che ho pubblicato la polemica "non usare i puntatori di cazzate", sento che dovrei commentare qui.

Innanzitutto, come polemica rappresenta ovviamente un punto di vista estremo. Ci sono sicuramente legittimi usi di puntatori (prime). Ma io (e molti programmatori professionisti C ++) ritengo che questi casi siano estremamente rari. Ma ciò che intendiamo veramente è il seguente:

Primo:

I puntatori non elaborati non devono in alcun caso possedere memoria.

Qui, "propria memoria" significa essenzialmente che ad un certo punto deleteviene chiamato quel puntatore (ma è più generale di così). Questa affermazione può essere tranquillamente considerata come assoluta. L' unica eccezione è quando si implementa il proprio puntatore intelligente (o altra strategia di gestione della memoria). E anche lì normalmente dovresti comunque usare un puntatore intelligente a basso livello.

La logica di ciò è abbastanza semplice: i puntatori non elaborati che possiedono la memoria introducono una fonte di errore. E questi errori sono prolifici nel software esistente: perdite di memoria e doppia eliminazione - entrambe conseguenze dirette della proprietà poco chiara delle risorse (ma andando nella direzione opposta).

Questo problema può essere completamente eliminato, praticamente a costo zero, semplicemente utilizzando i puntatori intelligenti anziché i puntatori non elaborati (avvertenza: ciò richiede ancora pensiero, ovviamente; i puntatori condivisi possono portare a cicli e quindi ancora una volta a perdite di memoria - ma questo è facilmente evitabile).

Secondo:

La maggior parte degli usi dei puntatori in C ++ non sono necessari.

A differenza di altri linguaggi, C ++ ha un supporto molto forte per la semantica di valore e semplicemente non ha bisogno dell'indirizzamento indiretto dei puntatori. Ciò non è stato immediatamente realizzato: storicamente, il C ++ è stato inventato per facilitare il facile orientamento degli oggetti in C e si basava pesantemente sulla costruzione di grafici di oggetti collegati da puntatori. Ma nel moderno C ++, questo paradigma è raramente la scelta migliore e i moderni idiomi del C ++ spesso non hanno bisogno di puntatori . Operano su valori anziché su puntatori.

Sfortunatamente, questo messaggio non si è ancora diffuso in gran parte della comunità di utenti C ++. Di conseguenza, la maggior parte del codice C ++ scritto è ancora disseminato di puntatori superflui che rendono il codice complesso, lento e difettoso / inaffidabile.

Per qualcuno che conosce moderna C ++, è chiaro che è molto raramente bisogno di alcun puntatori (o intelligenti o crude, tranne quando li utilizzano come iteratori). Il codice risultante è più breve, meno complesso, più leggibile, spesso più efficiente e più affidabile.


5
Sigh ... questa dovrebbe davvero essere la risposta con oltre 30 voti ... soprattutto per il secondo punto. I puntatori sono raramente necessari anche nel C ++ moderno.
Charles Salvia,

1
che dire ad es. L'oggetto Gui possiede un mucchio di oggetti doc. Ha questi come puntatori sia in modo che la classe possa essere dichiarata in avanti (evita i ricompilamenti) sia in modo che l'oggetto possa essere inizializzato quando creato (con nuovo) piuttosto che essere creato nello stack in qualche stato vuoto e poi archiviato in seguito? Questo sembra un uso perfettamente valido dei puntatori: tutti gli oggetti incapsulati non dovrebbero essere nell'heap?
Martin Beckett,

4
Gli oggetti GUI di @Martin sono un caso in cui i grafici degli oggetti puntatore sono davvero la soluzione migliore. Ma l'editto contro i puntatori grezzi che possiedono la memoria è ancora valido. Utilizzare i puntatori condivisi in tutto o sviluppare un modello di proprietà appropriato e avere solo relazioni deboli (= non possedute) tra i controlli tramite puntatori non elaborati.
Konrad Rudolph,

1
@ VF1 std::unique_ptr. Inoltre, perché no ptr_vec? Ma di solito un vettore di valore con si scambia ancora più velocemente (specialmente con la semantica di movimento).
Konrad Rudolph,

1
@gaazkam Nessuno ti ha costretto a usare solo contenitori standard. Boost, ad esempio, ha un'implementazione della mappa con supporto per tipi incompleti. Un'altra soluzione è utilizzare la cancellazione del tipo; boost::variantcon a recursive_wrapperè probabilmente la mia soluzione preferita per rappresentare un DAG.
Konrad Rudolph,

15

Semplicemente perché ci sono astrazioni disponibili per te che nascondono gli aspetti più temperanti dell'uso dei puntatori, come l'accesso alla memoria grezza e la pulizia dopo le allocazioni. Con i puntatori intelligenti, le classi di contenitori e i modelli di progettazione come RAII, viene ridotta la necessità di utilizzare i puntatori non elaborati. Detto questo, come ogni astrazione, dovresti capire come funzionano effettivamente prima di andare oltre.


11

Relativamente semplicemente, la mentalità C è "Hai un problema? Usa un puntatore". Puoi vederlo in stringhe C, puntatori a funzione, puntatori-come-iteratori, puntatore-puntatore, puntatore vuoto- anche nei primi giorni di C ++ con puntatori membri.

Ma in C ++ puoi usare i valori per molte o tutte queste attività. Hai bisogno di una funzione di astrazione? std::function. È un valore che è una funzione. std::string? È un valore, è una stringa. Puoi vedere approcci simili in tutto il C ++. Questo rende l'analisi del codice molto più semplice sia per gli umani che per i compilatori.


In C ++: hai un problema? Usa un puntatore. Ora hai 2 problemi ...
Daniel Zazula,

10

Uno dei motivi è un'applicazione troppo ampia di puntatori. Possono essere utilizzati per l'iterazione su contenitori, per evitare la copia di oggetti di grandi dimensioni quando si passa alla funzione, la gestione della vita non banale, l'accesso a luoghi casuali in memoria, ecc. E una volta utilizzati per uno scopo, diventano disponibili altre funzionalità immediatamente indipendentemente sull'intenzione.

La selezione di uno strumento per lo scopo esatto rende il codice più semplice e l'intento più visibili: iteratori per iterazioni, puntatori intelligenti per la gestione della vita, ecc.


3

Oltre ai motivi già elencati, ce n'è uno ovvio: migliori ottimizzazioni. L'analisi di aliasing è troppo complicata in presenza di un'aritmetica del puntatore, mentre i riferimenti suggeriscono un ottimizzatore, quindi un'analisi di aliasing molto più profonda è possibile se si utilizzano solo riferimenti.


2

Oltre al rischio di perdite di memoria dichiarate da @jmquigley, l'aritmetica del puntatore e del puntatore può essere considerata problematica perché i puntatori possono puntare ovunque nella memoria causando "bug difficili da trovare" e "vulnerabilità della sicurezza".

Ecco perché sono stati quasi abbandonati in C # e Java.


Aspettatevi che non siano stati "abbandonati" in C #. Questa risposta è scarsa, ha un'ortografia orribile e un'orribile dichiarazione inesatta.
Ramhound,

1
Erano quasi abbandonati. Mi scuso per non essere un madrelingua.
k3b,

1
Ehi, possiamo aiutarti con l'ortografia. Intendevi dire aspettarti o tranne?
Sviluppatore:

1
Quasi abbandonato in C #, puoi comunque abilitare il puntatore specificando la unsafeparola chiave
linquize il

-1

C ++ supporta la maggior parte delle funzionalità C , oltre a oggetti e classi. C aveva già puntatori e altre cose.

I puntatori sono una tecnica molto utile, che può essere combinata con l'orientamento agli oggetti e C ++ li supporta. Ma questa tecnica è difficile da insegnare e difficile da capire, ed è molto facile causare errori indesiderati.

Molti nuovi linguaggi di programmazione fingono di non utilizzare i puntatori con oggetti, come Java, .NET, Delphi, Vala, PHP, Scala. Ma i puntatori sono ancora usati, "dietro le quinte". Queste tecniche di "puntatore nascosto" sono chiamate "riferimenti".

In ogni caso, considero i puntatori come un modello di programmazione, come un modo valido per risolvere determinati problemi, così come la programmazione orientata agli oggetti .

Altri sviluppatori potrebbero avere un'opinione diversa. Ma suggerisco a studenti e programmatori di imparare a:

(1) Usa i puntatori senza oggetti

(2) oggetti senza puntatori

(3) puntatori espliciti agli oggetti

(4) puntatori "nascosti" agli oggetti ( riferimento AKA ) ;-)

In questo ordine.

Anche se è difficile da insegnare e difficile da imparare. Object Pascal (Delphi, FreePascal, altri) e C++(non Java o C #) possono essere usati per quegli obiettivi.

E, in seguito, i programmatori alle prime armi, possono passare a linguaggi di programmazione "puntatori nascosti agli oggetti" come: Java, C #, PHP orientato agli oggetti e altri.


19
Il C ++ è molto più della "C con classi" da cui è iniziato.
David Thornley,

Perché stai racchiudendo C ++ e C tra virgolette? E "nascosto", "riferimenti" e tutto il resto? Sei un "venditore" e non partecipi alla "programmazione"?
galleria

Dovrei metterli in grassetto. Le virgolette possono essere utilizzate per evidenziare ma anche il contrario
umlcat,

-6

Parlando di VC6, quando si lancia un puntatore di una classe (che si crea un'istanza) in una variabile (ad es. DWORD), anche se questo puntatore è locale è possibile accedere alla classe su tutte le funzioni che utilizzano lo stesso heap. La classe istanziata è definita come locale ma in realtà non lo è. Per quanto ne so, qualsiasi indirizzo di una variabile, struttura o classe di heap è unico per tutta la vita della classe di hosting.

Esempio:

class MyClass1 {
    public:
        void A (void);
        void B (void);
        void C (void);
    private:
        DWORD dwclass;
};

class MyClass2 {
    public:
        int C (int i);
};

void MyClass1::A (void) {
    MyClass2 *myclass= new MyClass2;
    dwclass=(DWORD)myclass;
}

void MyClass1::B (void) {
    MyClass2 *myclass= (MyClass2 *)dwclass;
    int i = myclass->C(0); // or int i=((MyClass2 *)dwclass)->C(0);
}

void MyClass1::B (void) {
    MyClass2 *myclass= (MyClass2 *)dwclass;
    delete myclass;
}

EDIT Questa è una parte molto piccola del codice originale. La classe CSRecodset è solo una classe di casting di CXdbRecordset, dove si trova tutto il codice reale. In questo modo posso consentire all'utente di trarre vantaggio da ciò che ho scritto senza perdere i miei diritti. Non pretendo di dimostrare che il mio motore di database è professionale ma funziona davvero.

//-------------------------------------
class CSRecordSet : public CSObject
//-------------------------------------
{
public:
    CSRecordSet();
    virtual ~CSRecordSet();
    // Constructor
    bool Create(CSDataBase* pDataBase,CSQueryDef* pQueryDef);
    //Open, find, close
    int OpenRst(bool bReadBlanks=0,bool bCheckLastSql=0,bool bForceLoad=0,bool bMessage=1);     // for a given SQL
    int FindRecord(bool bNext);         // for a given SQL
    // TABLE must be ordered by the same fields that will be seek
    bool SeekRecord(int nFieldIndex, char *key, int length=0);  // CRT bsearch
    bool SeekRecord(int nFieldIndex, long key);     
    bool SeekRecord(int nFieldIndex, double key, int decimals);     
    bool SeekRecord(XSEK *SEK);     
    bool DeleteRecord(void);
    bool Close(void);
    // Record Position:
    bool IsEOF(void);           // pointer out of bound
    bool IsLAST(void);          // TRUE if last record
    bool IsBOF(void);           // pointer out of bound
    bool IsOpen(void);
    bool Move(long lRows);      // returns FALSE if out of bound
    void MoveNextNotEof(void);  // eof is tested
    void MoveNext(void);        // eof is not tested
    void MovePrev(void);        // bof is tested
    void MoveLast(void);
    void MoveFirst(void);
    void SetAbsolutePosition(long lRows);
    long GetAbsolutePosition(void);
    void GoToLast(void); // Restore position after a Filter
    // Table info
    long GetRecordCount(void);
    int GetRstTableNumber(void);
    int GetRecordLength(void); //includes stamp (sizeof char)
    int GetTableType(void);
    // Field info
    int GetFieldCount(void);
    void GetFieldName(int nFieldIndex, char *pbuffer);
    int GetFieldIndex(const char *sFieldName);
    int GetFieldSize(int nFieldIndex);
    int GetFieldDGSize(int nFieldIndex); // String size (i.e. dg_Boolean)
    long GetRecordID(void);
    int GetStandardFieldCount(void);
    bool IsMemoFileTable(void);
    bool IsNumberField(int nFieldIndex);
    int GetFieldType(int nFieldIndex);
    // Read Field value
    bool GetFieldValue(int nFieldIndex, XdbVar& var);
    bool GetFieldValueIntoBuffer(int nFieldIndex,char *pbuffer);
    char *GetMemoField(int nMemoFieldIndex, char *pbuffer, int buf_size);
    bool GetBinaryField(unsigned char *buffer,long *buf_size);
    // Write Field value
    void Edit(void); // required
    bool SetFieldValue(int nFieldIndex, XdbVar& var);
    bool SetFieldValueFromBuffer(int nFieldIndex,const char *pbuffer);
    bool Update(void); // required
    // pointer to the same lpSql
    LPXSQL GetSQL(void);
};

//---------------------------------------------------
CSRecordSet::CSRecordSet(){
//---------------------------------------------------
    pClass |= (CS_bAttach);
}
CSRecordSet::~CSRecordSet(){
    if(pObject) delete (CXdbRecordset*)pObject;
}
bool CSRecordSet::Create(CSDataBase* pDataBase,CSQueryDef* pQueryDef){
    CXdbQueryDef *qr=(CXdbQueryDef*)pQueryDef->GetObject();
    CXdbTables *db=(CXdbTables*)pDataBase->GetObject();
    CXdbRecordset *rst = new CXdbRecordset(db,qr);
    if(rst==NULL) return 0;
    pObject = (unsigned long) rst;
    return 1;
}
bool CSRecordSet::Close(void){
    return ((CXdbRecordset*)pObject)->Close();
}
int CSRecordSet::OpenRst(bool bReadBlanks,bool bCheckLastSql,bool bForceLoad, bool bMessage){
    unsigned long dw=0L;
    if(bReadBlanks) dw|=SQL_bReadBlanks;
    if(bCheckLastSql) dw|=SQL_bCheckLastSql;
    if(bMessage) dw|=SQL_bRstMessage;
    if(bForceLoad) dw|=SQL_bForceLoad;

    return ((CXdbRecordset*)pObject)->OpenEx(dw);
}
int CSRecordSet::FindRecord(bool bNext){
    return ((CXdbRecordset*)pObject)->FindRecordEx(bNext);
}
bool CSRecordSet::DeleteRecord(void){
    return ((CXdbRecordset*)pObject)->DeleteEx();
}
bool CSRecordSet::IsEOF(void){
    return ((CXdbRecordset*)pObject)->IsEOF();
}
bool CSRecordSet::IsLAST(void){
    return ((CXdbRecordset*)pObject)->IsLAST();
}
bool CSRecordSet::IsBOF(void){
    return ((CXdbRecordset*)pObject)->IsBOF();
}
bool CSRecordSet::IsOpen(void){
    return ((CXdbRecordset*)pObject)->IsOpen();
}
bool CSRecordSet::Move(long lRows){
    return ((CXdbRecordset*)pObject)->MoveEx(lRows);
}
void CSRecordSet::MoveNextNotEof(void){
    ((CXdbRecordset*)pObject)->MoveNextNotEof();
}
void CSRecordSet::MoveNext(void){
    ((CXdbRecordset*)pObject)->MoveNext();
}
void CSRecordSet::MovePrev(void){
    ((CXdbRecordset*)pObject)->MovePrev();
}
void CSRecordSet::MoveLast(void){
    ((CXdbRecordset*)pObject)->MoveLast();
}
void CSRecordSet::MoveFirst(void){
    ((CXdbRecordset*)pObject)->MoveFirst();
}
void CSRecordSet::SetAbsolutePosition(long lRows){
    ((CXdbRecordset*)pObject)->SetAbsolutePosition(lRows);
}
long CSRecordSet::GetAbsolutePosition(void){
    return ((CXdbRecordset*)pObject)->m_AbsolutePosition;
}
long CSRecordSet::GetRecordCount(void){
    return ((CXdbRecordset*)pObject)->GetRecordCount();
}
int CSRecordSet::GetFieldCount(void){
    return ((CXdbRecordset*)pObject)->GetFieldCount();
}
int CSRecordSet::GetRstTableNumber(void){
    return ((CXdbRecordset*)pObject)->GetRstTableNumber();
}
void CSRecordSet::GetFieldName(int nFieldIndex, char *pbuffer){
    ((CXdbRecordset*)pObject)->GetFieldName(nFieldIndex,pbuffer);
}
int CSRecordSet::GetFieldIndex(const char *sFieldName){
    return ((CXdbRecordset*)pObject)->GetFieldIndex(sFieldName);
}
bool CSRecordSet::IsMemoFileTable(void){
    return ((CXdbRecordset*)pObject)->IsMemoFileTable();
}
bool CSRecordSet::IsNumberField(int nFieldIndex){
    return ((CXdbRecordset*)pObject)->IsNumberField(nFieldIndex);
}
bool CSRecordSet::GetFieldValueIntoBuffer(int nFieldIndex,char *pbuffer){
    return ((CXdbRecordset*)pObject)->GetFieldValueIntoBuffer(nFieldIndex,pbuffer);
}
void CSRecordSet::Edit(void){
    ((CXdbRecordset*)pObject)->Edit();
}
bool CSRecordSet::Update(void){
    return ((CXdbRecordset*)pObject)->Update();
}
bool CSRecordSet::SetFieldValue(int nFieldIndex, XdbVar& var){
    return ((CXdbRecordset*)pObject)->SetFieldValue(nFieldIndex,var);
}
bool CSRecordSet::SetFieldValueFromBuffer(int nFieldIndex,const char *pbuffer){
    return ((CXdbRecordset*)pObject)->SetFieldValueFromBuffer(nFieldIndex,pbuffer);
}
bool CSRecordSet::GetFieldValue(int nFieldIndex, XdbVar& var){
    return ((CXdbRecordset*)pObject)->GetFieldValue(nFieldIndex,var);
}
bool CSRecordSet::SeekRecord(XSEK *SEK){
    return ((CXdbRecordset*)pObject)->TableSeek(SEK);
}
bool CSRecordSet::SeekRecord(int nFieldIndex,char *key, int length){
    return ((CXdbRecordset*)pObject)->TableSeek(nFieldIndex,key,length);
}
bool CSRecordSet::SeekRecord(int nFieldIndex,long i){
    return ((CXdbRecordset*)pObject)->TableSeek(nFieldIndex,i);
}
bool CSRecordSet::SeekRecord(int nFieldIndex, double d, int decimals)
{
    return ((CXdbRecordset*)pObject)->TableSeek(nFieldIndex,d,decimals);
}
int CSRecordSet::GetRecordLength(void){
    return ((CXdbRecordset*)pObject)->GetRecordLength();
}
char *CSRecordSet::GetMemoField(int nMemoFieldIndex,char *pbuffer, int BUFFER_SIZE){
    return ((CXdbRecordset*)pObject)->GetMemoField(nMemoFieldIndex,pbuffer,BUFFER_SIZE);
}
bool CSRecordSet::GetBinaryField(unsigned char *buffer,long *buf_size){
    return ((CXdbRecordset*)pObject)->GetBinaryField(buffer,buf_size);
}
LPXSQL CSRecordSet::GetSQL(void){
    return ((CXdbRecordset*)pObject)->GetSQL();
}
void CSRecordSet::GoToLast(void){
    ((CXdbRecordset*)pObject)->GoToLast();
}
long CSRecordSet::GetRecordID(void){
    return ((CXdbRecordset*)pObject)->GetRecordID();
}
int CSRecordSet::GetStandardFieldCount(void){
    return ((CXdbRecordset*)pObject)->GetStandardFieldCount();
}
int CSRecordSet::GetTableType(void){
    return ((CXdbRecordset*)pObject)->GetTableType();
}
int CSRecordSet::GetFieldType(int nFieldIndex){
    return ((CXdbRecordset*)pObject)->GetFieldType(nFieldIndex);
}
int CSRecordSet::GetFieldDGSize(int nFieldIndex){
    return ((CXdbRecordset*)pObject)->GetFieldDGSize(nFieldIndex);
}
int CSRecordSet::GetFieldSize(int nFieldIndex){
    return ((CXdbRecordset*)pObject)->GetFieldSize(nFieldIndex);
}

EDIT: richiesto da DeadMG:

void nimportequoidumomentquecaroule(void) {

    short i = -4;
    unsigned short j=(unsigned short)i;

}

1
Questa descrizione potrebbe essere migliorata in modo significativo da alcuni codici per illustrare ciò che stai descrivendo. Ho la sensazione che si riferisse alla domanda originale, ma se ci avvertissi di un pericolo in questo scenario, aiuterebbe a elaborare l'argomento dell'interrogatore.
Sviluppatore:

1
Quel cast DWORDè abusivo e probabilmente errato (DWORD non è necessariamente abbastanza largo da contenere un puntatore). Se hai bisogno di un puntatore non tipizzato, usa void*- ma quando ti accorgi che ne hai bisogno in C ++, hai spesso un problema di progettazione nel tuo codice che dovresti risolvere.
Mat

Salvador, penso che tu stia cercando di dire qualcosa su VC6 e su come ha una gestione dei puntatori insolita e inaspettata. L'esempio potrebbe trarre vantaggio dai commenti e, se vengono visualizzati avvisi dal compilatore, potrebbero essere informativi in ​​merito al collegamento della risposta alla domanda.
Sviluppatore:

@Mat Questo esempio è per un sistema operativo a 32 bit e il compilatore VC6 ++.
Salvador,

6
Parlando di codice errato in un compilatore assolutamente antico? No grazie.
DeadMG
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.