LPCSTR, LPCTSTR i LPTSTR

109

Jaka jest różnica między LPCSTR, LPCTSTRi LPTSTR?

Dlaczego musimy to zrobić, aby przekonwertować ciąg na zmienną LV/ _ITEMstructure pszText:

LV_DISPINFO dispinfo;  
dispinfo.item.pszText = LPTSTR((LPCTSTR)string);
nicMaster
źródło
2
Czy możesz powiedzieć, jaki dokładnie jest typ „string”? (np. CString)
John Sibly

Odpowiedzi:

122

Aby odpowiedzieć na pierwszą część pytania:

LPCSTRjest wskaźnikiem na ciąg znaków const (LP oznacza długi wskaźnik )

LPCTSTRjest wskaźnikiem do const TCHARłańcucha ( TCHARbędącego szerokim znakiem lub znakiem w zależności od tego, czy w projekcie zdefiniowano UNICODE)

LPTSTRjest wskaźnikiem do (nie stałego) TCHARciągu

W praktyce, kiedy mówiliśmy o nich w przeszłości, pominęliśmy „wskaźnik do” wyrażenia dla uproszczenia, ale jak wspomniano w lekkich wyścigach na orbicie, wszystkie one są wskazówkami.

To jest świetny artykuł o projekcie kodu opisujący ciągi C ++ (patrz 2/3 drogi w dół, aby zobaczyć wykres porównujący różne typy)

John Sibly
źródło
18
Wszystko źle. Żadna z tych rzeczy nie jest strunami. Wszystkie są wskazówkami. -1
Wyścigi lekkości na orbicie
8
@LightnessRacesinOrbit Masz techniczną poprawność - chociaż z mojego doświadczenia wynika, że ​​powszechną praktyką jest pomijanie opisu „wskaźnik do ....” dla zwięzłości w odniesieniu do typów ciągów w C ++
John Sibly
2
@JohnSably: W C, tak. W C ++ absolutnie nie powinno być !!
Wyścigi lekkości na orbicie
4
Zauważ, że ten artykuł codeproject został napisany 15 lat temu i, jeśli nie zostanie zaktualizowany, zawiera mylące założenia, że ​​znaki Unicode zawsze mają 2 bajty. To jest całkowicie błędne. Nawet UTF16 ma zmienną długość ... znacznie lepiej jest powiedzieć, że szerokie znaki są kodowane w UCS-2, a „Unicode” w tym kontekście odnosi się do UCS-2.
u8it
1
Hmm ... w tym przypadku, @LightnessRacesinOrbit, dodałbym dodatek, że można pominąć „wskaźnik do ...” w odniesieniu do ciągów C w C ++, jeśli-i-tylko-jeśli odnosząc się konkretnie do (zepsute) literały ciągów lub podczas łączenia / pracy z kodem napisanym w C, polega na typach C zamiast na typach C ++ i / lub ma połączenie z C za pośrednictwem extern "C". Poza tym, tak, zdecydowanie powinien potrzebować bitu „wskaźnika” lub konkretnego opisu jako napisu w C.
Justin Time - Przywróć Monikę
87

Szybko i brudno:

LP== L ong P ointer. Po prostu pomyśl o wskaźniku lub znaku *

C= C onst, w tym przypadku myślę, że oznaczają one ciąg znaków, a nie wskaźnik będący stałą.

STRjest ciągiem

Tjest dla szerokiego znaku lub char (TCHAR) w zależności od opcji kompilacji.

Tim
źródło
16
T nie jest dla szerokich znaków, jest dla różnych typów znaków. W oznacza szerokość (jak w WCHAR). Jeśli zdefiniowano UNICODE, TCHAR == WCHAR, w przeciwnym razie TCHAR == CHAR. Więc jeśli UNICODE nie jest zdefiniowane, LPCTSTR == LPCSTR.
jalf
10
dlatego napisałem „w zależności od opcji kompilacji”
Tim
14
Uwielbiam tego typu wyjaśnienia :). Wielkie dzięki
Dzung Nguyen
@jalf, Więc co oznacza T?
Pacerier
3
T oznacza T ext
Ian Boyd
36

8-bitowe AnsiStrings

  • char: Znak 8-bitowy - podstawowy typ danych C / C ++
  • CHAR: alias char- typ danych Windows
  • LPSTR: zakończony zerem ciąg CHAR ( L ong P ointer)
  • LPCSTR: stały ciąg zakończony zerem CHAR ( L ong P ointer)

16-bitowe UnicodeStrings

  • wchar_t: Znak 16-bitowy - podstawowy typ danych C / C ++
  • WCHAR: alias wchar_t- typ danych Windows
  • LPWSTR: zakończony zerem ciąg WCHAR ( L ong P ointer)
  • LPCWSTR: stały ciąg zakończony zerem WCHAR ( L ong P ointer)

w zależności od UNICODEzdefiniowania

  • TCHAR: alias WCHARjeśli zdefiniowano UNICODE; InaczejCHAR
  • LPTSTR: zakończony zerem ciąg TCHAR ( L ong P ointer)
  • LPCTSTR: stały ciąg zakończony zerem TCHAR ( L ong P ointer)

Więc

| Item              | 8-bit        | 16-bit      | Varies          |
|-------------------|--------------|-------------|-----------------|
| character         | CHAR         | WCHAR       | TCHAR           |
| string            | LPSTR        | LPWSTR      | LPTSTR          |
| string (const)    | LPCSTR       | LPCWSTR     | LPCTSTR         |

Czytanie bonusowe

TCHARTekst Char ( archive.is )

Ian Boyd
źródło
4
Szkoda, że ​​ta odpowiedź nigdy nie dotrze na szczyt, ponieważ jest tak nowa ... to naprawdę coś, co SO musi naprawić. To zdecydowanie najlepsza odpowiedź.
Dan Bechard
To naprawdę bardzo mi pomaga w pracy nad projektem Unicode. Dzięki!
Yoon5oo
Niezła odpowiedź. Myślę, że warto dodać, że wersja unicode używa UTF16, więc każdy 16-bitowy fragment nie jest znakiem, ale jednostką kodu. Nazwy są historyczne (gdy Unicode === UCS2).
Margaret Bloom,
5

Dodawanie do odpowiedzi Johna i Tima.

Jeśli nie piszesz dla Win98, istnieją tylko dwa z ponad 6 typów ciągów, których powinieneś używać w swojej aplikacji

  • LPWSTR
  • LPCWSTR

Reszta jest przeznaczona do obsługi platform ANSI lub podwójnych kompilacji. Nie są one dziś tak aktualne, jak kiedyś.

JaredPar
źródło
2
@BlueRaja, w mojej odpowiedzi odnosiłem się głównie do ciągów opartych na C. Ale w przypadku C ++ unikałbym, std::stringponieważ nadal jest to ciąg oparty na ASCII i wolę std::wstringzamiast tego.
JaredPar
1
Powinieneś używać LPTSTR i LPCTSTR, chyba że bezpośrednio wywołujesz wersje funkcji ASCII (* A) lub widechar (* W). Są to aliasy o dowolnej szerokości znaków określonej podczas kompilacji.
osvein
... A teraz, gdy Microsoft pracuje nad *Adostosowaniem wersji WinAPI do strony kodowej UTF-8, nagle stają się one znacznie bardziej odpowiednie. ; P
Justin Time - Przywróć Monikę
4

Aby odpowiedzieć na drugą część pytania, musisz wykonać takie czynności jak

LV_DISPINFO dispinfo;  
dispinfo.item.pszText = LPTSTR((LPCTSTR)string);

ponieważ LVITEMstruktura MS ma plikLPTSTR , tj. zmienny wskaźnik łańcucha T, a nie LPCTSTR. To, co robisz, jest

1) przekonwertować string( CStringprzypuszczalnie a) na LPCTSTR(co w praktyce oznacza uzyskanie adresu bufora znaków jako wskaźnika tylko do odczytu)

2) przekonwertować ten wskaźnik tylko do odczytu na wskaźnik z możliwością zapisu, odrzucając jego const-ness.

Zależy to od tego, co dispinfozostanie użyte do tego, czy jest szansa, że ​​twoja ListViewrozmowa zakończy się próbą napisania przez to pszText. Jeśli tak, jest to potencjalnie bardzo zła rzecz: w końcu otrzymałeś wskaźnik tylko do odczytu i zdecydowałeś traktować go jako zapisywalny: może jest powód, dla którego był on tylko do odczytu!

Jeśli jest to plik, z CStringktórym pracujesz, masz możliwość użycia string.GetBuffer()- to celowo daje ci możliwość zapisuLPTSTR . Musisz wtedy pamiętać o wywołaniu, ReleaseBuffer()jeśli łańcuch się zmieni. Lub możesz przydzielić lokalny bufor tymczasowy i skopiować tam ciąg.

W 99% przypadków będzie to niepotrzebne i traktowanie tego LPCTSTRjako LPTSTRdobrego zadziała ... ale pewnego dnia, kiedy najmniej się tego spodziewasz ...

AAT
źródło
1
Powinieneś unikać rzutowania w stylu C i xxx_cast<>()zamiast tego używać .
harper
@harper Masz rację - ale ja cytowałem OP, czyli kod, o który pytał. Gdybym sam napisał kod, z pewnością użyłby go xxx_cast<>zamiast mieszania dwóch różnych stylów rzutowania opartych na nawiasach!
AAT