Pagina 1 di 2 1 2 ultimoultimo
Visualizzazione dei risultati da 1 a 10 su 11

Discussione: [c++]Capire stringhe

  1. #1
    Utente di HTML.it
    Registrato dal
    Jun 2003
    Messaggi
    4,826

    [c++]Capire stringhe

    Ciao.
    Devo colmare un altra delle mie tante lacune in c++ , le stringhe , mi potete consigliare una sezione di un libro(ho lo stroustroup e thinking c++)o un buon tutorial?
    Per es: cosa fanno L"" _T() FunctionA e FunctionW ecc....?

  2. #2

    Re: [c++]Capire stringhe

    Originariamente inviato da giuseppe500
    Per es: cosa fanno L"" _T() FunctionA e FunctionW ecc....?
    Qui però stai parlando fondnaamentalmente di stringhe C...
    L di fronte ad una stringa (o un carattere) indica al compilatore che si tratta di una stringa (o di un carattere) Unicode, e non ANSI; _T() è una macro che se è definito _UNICODE è definita come
    codice:
    #define _T(x)     L ## x
    (o qualcosa del genere), il che in sostanza fa sì che la stringa inserita tra le due parentesi sia trattata come una stringa Unicode, mentre se _UNICODE non è definito è definita come
    codice:
    #define _T(x)     x
    , per cui lascia la stringa ANSI. L'utilità di questa macro è di consentire di compilare il programma indifferentemente in ANSI o in Unicode semplicemente definendo o non definendo _UNICODE. Naturalmente per poter programmare con "stringhe generiche" bisogna avere anche a disposizione delle funzioni che funzionino con caratteri di tipo generico (ANSI o Unicode, a seconda della definizione della suddetta macro), anch'esse definite in TCHAR.H.
    Senza i generic text mappings per poter compilare indifferentemente in Unicode e in ANSI dovresti scrivere le due versioni per ogni blocco di codice che usa le stringhe, in questa maniera:
    codice:
    #ifdef _UNICODE
    wchar_t prova[256];
    wcscpy(prova, L"Ciao ajlasloajf");
    #else
    char prova[256];
    strcpy(prova, "Ciao ajlasloajf");
    #endif
    con i generic text mappings, invece, puoi fare semplicemente
    codice:
    TCHAR prova[256];
    _tcscpy(prova _T("Ciao ajlasloajf"));
    .
    Dovendo essere compatibile con il C tutto questo accrocchio funziona tramite macro: _tcscpy infatti è definita come strcpy quando si compila in ANSI, wcscpy quando si compila in Unicode.
    Trovandomi a dover interagire in un ambiente TCHAR con delle funzioni che accettano soltanto char * o wchar_t * ho scritto qualche macro che può risultare utile in queste situazioni:
    codice:
    //Mappings for conversions from and to TCHAR *
    #ifdef _UNICODE
    	//TCHAR *	->	char *		=>		wchar_t *	->	char *
    	#define _tcstombs	wcstombs
    	//TCHAR *	->	wchar_t *	=>		wchar_t *	->	wchar_t *
    	#define _tcstowcs	wcsncpy
    	//char *	->	TCHAR *		=>		char *		->	wchar_t *
    	#define _mbstotcs	mbstowcs
    	//wchar_t *	->	TCHAR *		=>		wchar_t *	->	wchar_t *
    	#define _wcstotcs	wcsncpy
    #else
    	//TCHAR *	->	char *		=>		char *		->	char *
    	#define _tcstombs	strncpy
    	//TCHAR *	->	wchar_t *	=>		char *		->	wchar_t *
    	#define _tcstowcs	mbstowcs
    	//char *	->	TCHAR *		=>		char *		->	char *
    	#define _mbstotcs	strncpy
    	//wchar_t *	->	TCHAR *		=>		wchar_t *	->	char *
    	#define _wcstotcs	wcstombs
    #endif
    In C++ si possono utilizzare i TCHAR in maniera piuttosto semplice, dal momento che tutte le classi che usano stringhe e caratteri sono template; nello stesso progetto ho definito qualche typedef per questo fine:
    codice:
    //Mappings for the STL string and string-related classes
    namespace std
    {
    	//String
    	typedef basic_string<TCHAR>				_tcstring;
    	//String streams
    	typedef basic_stringstream<TCHAR>		_tcstringstream;
    	typedef basic_ostringstream<TCHAR>		_tcostringstream;
    	typedef basic_istringstream<TCHAR>		_tcistringstream;
    	//String buffer
    	typedef basic_stringbuf<TCHAR>			_tcstringbuf;
    	//File streams
    	typedef basic_fstream<TCHAR>			_tcfstream;
    	typedef basic_ifstream<TCHAR>			_tcifstream;
    	typedef basic_ofstream<TCHAR>			_tcofstream;
    };
    Ho poi visto che non è un'idea così originale, e infatti in SmartWin++ hanno fatto la stessa cosa, definendo anche delle funzioni template per convertire le stringhe da un tipo all'altro.
    Nella programmazione Windows succede qualcosa di analogo: praticamente di ogni funzione che utilizza le stringhe esistono due versioni, una ANSI e una Unicode, le quali rispettivamente terminano con una A e una W. La versione senza lettera finale in realtà non esiste come funzione, è semplicemente una macro del preprocessore (analoga a quelle di TCHAR.H) che a seconda della macro _UNICODE è definita come la versione A o la versione W. Faccio un esempio: se guardi nelle dll di Windows di fatto non esiste nessuna funzione GetWindowText, ma esistono le funzioni GetWindowTextA e GetWindowTextW. Quando tu nel tuo programma scrivi GetWindowText, il preprocessore sostituisce GetWindowText con GetWindowTextA o GetWindowTextW a seconda di come è definita la macro _UNICODE. Lo stesso ragionamento vale per tutte le strutture che contengono stringhe o puntatori a stringhe.
    La documentazione delle singole funzioni (e delle strutture) di solito non menziona le due versioni, ma piuttosto ne specifica solo una fingendo che accetti dei TCHAR (cosa che naturalmente è impossibile, visto che un TCHAR si risolve in un char o in un wchar_t al momento della compilazione, ma le funzioni di Windows sono già compilate; naturalmente l'overloading non è un'opzione contemplata, visto che le API devono essere compatibili con il C).
    Alcune funzioni di Windows più recenti esistono solo in versione W, e in tal caso di solito non ne esiste una versione senza suffisso finale.
    Sui sistemi di famiglia NT conviene assolutamente compilare in Unicode, sia perché in questa maniera si evitano molti problemi con caratteri stranieri, sia perché le funzioni A non sono altro che wrapper attorno alle funzioni W, che convertono le stringhe in input in Unicode, le passano alla funzione W e quando questa ritorna riconvertono le stringhe di output in ANSI; questo naturalmente implica una maggiore lentezza delle funzioni A. Perché allora non si compila tutto in Unicode? Fondamentalmente per via della compatibilità con vecchio codice e vecchi sistemi operativi; riscrivere tutto il codice che usava char con i wchar_t è una gran menata, specie se si fa ampio uso di sizeof per ottenere il numero di caratteri dei buffer; lasciando il sizeof così com'è e cambiando il tipo a wchar_t infatti si aprono spesso problemi di buffer overflow. Considera ad esempio
    codice:
    char test[256];
    /* ... */
    strncpy(test, altrastringa, sizeof(test)-1);
    test[sizeof(test)-1]=0;
    ; convertendolo in Unicode in questa maniera
    codice:
    wchar_t test[256];
    /* ... */
    wcsncpy(test, altrastringa, sizeof(test)-1);
    test[sizeof(test)-1]=0;
    si commette un grave errore: infatti sizeof restituisce le dimensioni di una variabile in numero di char, mentre le funzioni stringa si aspettano il numero massimo di caratteri da copiare; in questo caso (considerando un wchar_t di 2 byte) quindi si va a passare a wcsncpy 511 invece di 255 come numero massimo di caratteri da copiare. Anche peggio, la linea successiva (che servirebbe a terminare con sicurezza il buffer con un NUL) va a scrivere su memoria non allocata.
    La soluzione consiste nell'utilizzare sempre i generic text mappings, o almeno nell'evitare l'uso di sizeof per determinare il numero di elementi di un array, ma di usare sempre la macro ARRSIZE
    codice:
    #define ARRSIZE(arr) (sizeof(arr)/sizeof(*(arr)))
    .
    Dicevo della compatibilità con sistemi operativi vecchi: il problema fondamentale qui è la famiglia 9x di Windows, che implementa un numero ridotto di API Unicode, a meno che non si installi MSLU, e che comunque internamente funziona in ANSI. Se non utilizzano MSLU (e se questo a sua volta non è installato) è facile che eseguibili compilati in Unicode vadano in crash all'avvio per la mancanza di qualche API Unicode su Windows 9x.
    La documentazione di Windows per specificare i puntatori a stringa utilizza una notazione particolare (e odiosa) che è bene imparare; ogni tipo di stringa si definisce in questa maniera:
    codice:
    LP[C][W|T]STR
    LP = puntatore
    [C] = opzionale, se presente indica che la stringa è costante (const)
    [W|T] = opzionale, se assente indica che la stringa è ANSI (char *), se è W indica che la stringa è Unicode (wchar_t *), se è T indica che la stringa è generica (TCHAR *)
    STR = indica che è una stringa
    Di conseguenza di fatto abbiamo i seguenti tipi:
    codice:
    LPSTR = char *
    LPCSTR = const char *
    LPWSTR = wchar_t *
    LPCWSTR = const wchar_t *
    LPTSTR = TCHAR *
    LPCTSTR = const TCHAR *
    .
    Per finire, ti lascio con un articolo che spiega più ampiamente quello che ho scritto qui (link).
    Amaro C++, il gusto pieno dell'undefined behavior.

  3. #3
    Utente di HTML.it L'avatar di XWolverineX
    Registrato dal
    Aug 2005
    residenza
    Prague
    Messaggi
    2,563
    Dicono che in Win32 le stringhe unicode sono piu' veloci da trattare.
    "Se proprio devono piratare, almeno piratino il nostro." (Bill Gates)

    "Non è possibile che 2 istituzioni statali mi mettano esami nello stesso giorno." (XWolverineX)

    http://xvincentx.netsons.org/programBlog

  4. #4
    Utente di HTML.it
    Registrato dal
    Feb 2009
    Messaggi
    502
    La risposta di MItaly dovrebbe essere una "pillola".
    Spesso ci si ritrova con warning o error in fase di compilazione per aver sbagliato a dichiarare una stringa, e la sua risposta mi sembra utile e esaustiva.

  5. #5
    Originariamente inviato da XWolverineX
    Dicono che in Win32 le stringhe unicode sono piu' veloci da trattare.
    In Windows NT sì, in Windows 9x no. In ogni caso le stringhe Unicode, se non consideriamo la faccenda del wrapper ANSI->Unicode delle API, sono intrinsecamente più lente, dal momento che per lo stesso numero di caratteri viene impiegato il doppio o il quadruplo dello spazio (a seconda delle dimensioni di wchar_t), e quindi il doppio di memoria e di tempo per copiarle. Inoltre se si tratta di una codifica UTF (e non UCS) il calcolo della lunghezza di una stringa e l'estrazione da essa di un singolo carattere diventa un'operazione non banale, dal momento che bisogna considerare le coppie surrogate. A titolo di informazione, Windows NT 3.11-4 usa UCS-2, da Windows 2000 in poi si utilizza UTF-16. Su Linux per quanto ho avuto modo di vedere finora si usa UTF-8 (usando i char) e UCS-4/UTF-32 (con i wchar_t).
    Originariamente inviato da Rubox
    La risposta di MItaly dovrebbe essere una "pillola".
    Spesso ci si ritrova con warning o error in fase di compilazione per aver sbagliato a dichiarare una stringa, e la sua risposta mi sembra utile e esaustiva.
    Grazie.
    Amaro C++, il gusto pieno dell'undefined behavior.

  6. #6
    Utente di HTML.it L'avatar di XWolverineX
    Registrato dal
    Aug 2005
    residenza
    Prague
    Messaggi
    2,563
    XP e Vista hanno un kernel simil NT, o comunque derivato quindi l'asserzione dovrebbe rimanere valida anche su tali sistemi.

    Poi ovviamente tutte le considerazioni sullo spazio doppio o quadruplo a causa dei caratteri in piu' è verissima.
    "Se proprio devono piratare, almeno piratino il nostro." (Bill Gates)

    "Non è possibile che 2 istituzioni statali mi mettano esami nello stesso giorno." (XWolverineX)

    http://xvincentx.netsons.org/programBlog

  7. #7
    Originariamente inviato da XWolverineX
    XP e Vista hanno un kernel simil NT, o comunque derivato quindi l'asserzione dovrebbe rimanere valida anche su tali sistemi.
    Certo; scrivendo Windows NT infatti intendevo "famiglia NT"; non a caso Windows 2000 è NT 5.0, XP è NT 5.1, 2003 Server è NT 5.2, Vista e 2008 Server è 6.0, Seven è 6.1.
    Amaro C++, il gusto pieno dell'undefined behavior.

  8. #8
    Utente di HTML.it L'avatar di Stoicenko
    Registrato dal
    Feb 2004
    Messaggi
    2,254
    ma seven non è... 7?

  9. #9
    No, è 6.1.7600.16385.
    Amaro C++, il gusto pieno dell'undefined behavior.

  10. #10
    Utente di HTML.it L'avatar di Stoicenko
    Registrato dal
    Feb 2004
    Messaggi
    2,254
    probabilmente faccio confusione col la versione del kernel, 7 è la major build o simil no?

Permessi di invio

  • Non puoi inserire discussioni
  • Non puoi inserire repliche
  • Non puoi inserire allegati
  • Non puoi modificare i tuoi messaggi
  •  
Powered by vBulletin® Version 4.2.1
Copyright © 2025 vBulletin Solutions, Inc. All rights reserved.