Jak przekonwertować std :: string na const char * lub char *?


893

Jak mogę przekonwertować A std::stringna A char*lub A const char*?


2
Zamiast: char * writable = new char [str.size () + 1]; Możesz użyć char writable [str.size () + 1]; Dzięki temu nie musisz się martwić usunięciem możliwości zapisu lub obsługi wyjątków.

7
Nie możesz użyć str.size (), chyba że rozmiar jest znany w czasie kompilacji, może również przepełnić twój stos, jeśli stała wartość rozmiaru jest ogromna.
paulm

1
char * wynik = strcpy ((char *) malloc (str. długość () + 1), str.c_str ());
cegprakash

7
@cegprakash strcpyi malloctak naprawdę nie są w języku C ++.
boycy

4
Nie, ale char* dest = new char[str.length() + 1]; std::copy(str.begin(), str.end(), dest)byłby bardziej idiomatyczny C ++. strcpy()i malloc()nie są złe lub problematyczne, ale wydaje się niespójne używanie łańcucha C ++ i narzędzi biblioteki C z odpowiednikami C ++ w tym samym bloku kodu.
boycy

Odpowiedzi:


1055

Jeśli chcesz tylko przekazać std::stringfunkcję, która potrzebuje const char*, możesz jej użyć

std::string str;
const char * c = str.c_str();

Jeśli chcesz uzyskać zapisywalną kopię char *, możesz to zrobić w ten sposób:

std::string str;
char * writable = new char[str.size() + 1];
std::copy(str.begin(), str.end(), writable);
writable[str.size()] = '\0'; // don't forget the terminating 0

// don't forget to free the string after finished using it
delete[] writable;

Edycja : Zauważ, że powyższe nie jest wyjątkowo bezpieczne. Jeśli coś między newpołączeniem a deletepołączeniem zostanie rzucone, nastąpi wyciek pamięci, ponieważ nic nie zadzwoni deleteautomatycznie. Istnieją dwa bezpośrednie sposoby rozwiązania tego problemu.

boost :: scoped_array

boost::scoped_array usunie dla ciebie pamięć po wyjściu poza zakres:

std::string str;
boost::scoped_array<char> writable(new char[str.size() + 1]);
std::copy(str.begin(), str.end(), writable.get());
writable[str.size()] = '\0'; // don't forget the terminating 0

// get the char* using writable.get()

// memory is automatically freed if the smart pointer goes 
// out of scope

std :: wektor

Jest to standardowy sposób (nie wymaga żadnej biblioteki zewnętrznej). Używasz std::vector, który całkowicie zarządza dla ciebie pamięcią.

std::string str;
std::vector<char> writable(str.begin(), str.end());
writable.push_back('\0');

// get the char* using &writable[0] or &*writable.begin()

41
Po prostu użyj char * result = strdup (str.c_str ());
Jasper Bekkers,

63
mogłeś, ale strdup nie jest standardową funkcją ac lub c ++, pochodzi od posix :)
Johannes Schaub - litb

14
to, co prawdopodobnie wolałbym ogólnie, to std :: vector <char> writable (str.begin (), str.end ()); writable.push_back ('\ 0'); char * c = & zapisywalny [0];
Johannes Schaub - litb

17
std :: copy jest sposobem na zrobienie tego w c ++, bez potrzeby sięgania po wskaźnik łańcucha. Staram się unikać używania funkcji C tak bardzo, jak tylko mogę.
Johannes Schaub - litb

16
Począwszy od C ++ 17, std::string::data()teraz zwraca CharT*zamiast zamiast const CharT*. Dobrym pomysłem może być aktualizacja tej odpowiedzi :)
Rakete1111

192

Biorąc pod uwagę powiedz ...

std::string x = "hello";

Uzyskiwanie `char *` lub `const char *` z `string`

Jak uzyskać wskaźnik znaku, który jest prawidłowy, dopóki xpozostaje w zasięgu i nie jest dalej modyfikowany

C ++ 11 upraszcza rzeczy; następujące zapewniają dostęp do tego samego wewnętrznego bufora ciągów:

const char* p_c_str = x.c_str();
const char* p_data  = x.data();
char* p_writable_data = x.data(); // for non-const x from C++17 
const char* p_x0    = &x[0];

      char* p_x0_rw = &x[0];  // compiles iff x is not const...

Wszystkie powyższe wskaźniki będą miały tę samą wartość - adres pierwszego znaku w buforze. Nawet pusty ciąg ma „pierwszy znak w buforze”, ponieważ C ++ 11 gwarantuje, że zawsze zachowa dodatkowy znak terminatora NUL / 0 po jawnie przypisanej zawartości ciągu (np. std::string("this\0that", 9)Będzie miał bufor "this\0that\0").

Biorąc pod uwagę którykolwiek z powyższych wskaźników:

char c = p[n];   // valid for n <= x.size()
                 // i.e. you can safely read the NUL at p[x.size()]

Tylko dla non- constpoint p_writable_datai od &x[0]:

p_writable_data[n] = c;
p_x0_rw[n] = c;  // valid for n <= x.size() - 1
                 // i.e. don't overwrite the implementation maintained NUL

Napisanie NUL gdzie indziej w ciągu nie zmienia string's size(); stringmogą zawierać dowolną liczbę wartości NUL - nie są traktowane przez std::string(tak samo jak w C ++ 03).

W C ++ 03 rzeczy były znacznie bardziej skomplikowane ( podkreślono kluczowe różnice ):

  • x.data()

    • wraca const char*do wewnętrznego bufora łańcucha, który nie był wymagany przez Standard do zakończenia z NUL (tzn. mogą wystąpić ['h', 'e', 'l', 'l', 'o']wartości niezainicjowane lub śmieci, z przypadkowym dostępem do nich o nieokreślonym zachowaniu ).
      • x.size()znaki są bezpieczne do odczytania, tzn. x[0]poprzezx[x.size() - 1]
      • w przypadku pustych ciągów masz gwarancję, że wskaźnik nie jest równy NULL, do którego można bezpiecznie dodać 0 (hurra!), ale nie powinieneś odrywać tego wskaźnika.
  • &x[0]

    • w przypadku pustych ciągów zachowanie to jest niezdefiniowane (21.3.4)
      • np. biorąc pod uwagę, f(const char* p, size_t n) { if (n == 0) return; ...whatever... }że nie wolno dzwonić, f(&x[0], x.size());kiedy x.empty()- po prostu użyj f(x.data(), ...).
    • w przeciwnym razie, zgodnie z, x.data()ale:
      • dla nie- const xdaje to const char*wskaźnik niepowodujący; możesz zastąpić treść ciągu
  • x.c_str()

    • powraca const char*do reprezentacji wartości ASCIIZ (zakończonej przez NUL) (tj. [„h”, „e”, „l”, „l”, „o”, „\ 0”]).
    • chociaż niewiele, jeśli jakieś implementacje zdecydowały się to zrobić, standard C ++ 03 został sformułowany tak, aby umożliwić implementacji łańcucha swobodę tworzenia w locie odrębnego bufora zakończonego NUL , z potencjalnie nie zakończonego NUL bufora „odsłoniętego” przez ix.data()&x[0]
    • x.size() + 1 znaki są bezpieczne do odczytania.
    • gwarantowane bezpieczeństwo nawet dla pustych ciągów (['\ 0']).

Konsekwencje dostępu do zewnętrznych wskaźników prawnych

Niezależnie od tego, w jaki sposób uzyskasz wskaźnik, nie możesz uzyskać dostępu do pamięci dalej od wskaźnika niż znaki gwarantowane obecne w powyższych opisach. Próby tego mają nieokreślone zachowanie , z bardzo realną szansą na awarie aplikacji i śmieciowe wyniki nawet dla odczytów, a dodatkowo hurtową sprzedaż danych, uszkodzenie stosu i / lub luki w zabezpieczeniach zapisu.

Kiedy wskaźniki te zostaną unieważnione?

W przypadku wywołania stringfunkcji członkowskiej, która modyfikuje stringlub rezerwuje dodatkową pojemność, wszelkie wartości wskaźnika zwrócone wcześniej za pomocą dowolnej z powyższych metod zostaną unieważnione . Możesz użyć tych metod ponownie, aby uzyskać inny wskaźnik. (Zasady są takie same, jak dla iteratorów na strings).

Zobacz także Jak uzyskać poprawność wskaźnika znaku nawet po xopuszczeniu zasięgu lub dalszej modyfikacji poniżej ...

Którego lepiej użyć?

Od C ++ 11 używaj .c_str()danych ASCIIZ i danych .data()„binarnych” (wyjaśnione poniżej).

W C ++ 03, korzystanie .c_str()chyba pewne, że .data()jest odpowiednia, i wolą .data()nad &x[0]jak to jest bezpieczne dla pustych strunach ....

... postaraj się zrozumieć program na tyle, aby użyć go w data()razie potrzeby, w przeciwnym razie prawdopodobnie popełnisz inne błędy ...

Znak ASCII NUL „\ 0” gwarantowany przez .c_str()jest używany przez wiele funkcji jako wartość wartownika oznaczająca koniec odpowiednich i bezpiecznych danych. Dotyczy to zarówno funkcji C ++, takich jak say, jak fstream::fstream(const char* filename, ...)i funkcji współdzielonych z C, takich jak strchr()i printf().

Biorąc pod uwagę, .c_str()że gwarancje C ++ 03 dotyczące zwróconego bufora są super zestawem .data(), zawsze możesz bezpiecznie używać .c_str(), ale ludzie czasami nie, ponieważ:

  • używanie .data()komunikuje się innym programistom odczytującym kod źródłowy, że dane nie są ASCIIZ (raczej używasz ciągu do przechowywania bloku danych (który czasami nie jest nawet tekstowy)), lub że przekazujesz go do kolejna funkcja, która traktuje to jako blok danych „binarnych”. Może to być kluczowy wgląd w zapewnienie, że zmiany kodu innych programistów będą nadal poprawnie obsługiwać dane.
  • Tylko C ++ 03: istnieje niewielka szansa, że ​​twoja stringimplementacja będzie musiała dokonać dodatkowej alokacji pamięci i / lub kopiowania danych, aby przygotować bufor zakończony NUL

Jako kolejna wskazówka, jeśli parametry funkcji wymagają ( const), char*ale nie nalegają na uzyskanie x.size(), funkcja prawdopodobnie wymaga wejścia ASCIIZ, więc .c_str()jest to dobry wybór (funkcja musi wiedzieć, gdzie tekst się kończy, więc jeśli nie jest oddzielnym parametrem może być tylko konwencja, taka jak prefiks długości lub wartownik lub niektóre ustalone oczekiwane długości).

Jak uzyskać poprawność wskaźnika znaku nawet po xopuszczeniu zasięgu lub dalszej modyfikacji

Musisz skopiować zawartość string xdo nowego obszaru pamięci na zewnątrz x. Ten bufor zewnętrzny może znajdować się w wielu miejscach, takich jak inna stringzmienna lub tablica znaków, może mieć inny okres istnienia niż xw innym zakresie (np. Przestrzeń nazw, globalna, statyczna, sterty, pamięć współdzielona, ​​plik odwzorowany w pamięci) .

Aby skopiować tekst z std::string xniezależnej tablicy znaków:

// USING ANOTHER STRING - AUTO MEMORY MANAGEMENT, EXCEPTION SAFE
std::string old_x = x;
// - old_x will not be affected by subsequent modifications to x...
// - you can use `&old_x[0]` to get a writable char* to old_x's textual content
// - you can use resize() to reduce/expand the string
//   - resizing isn't possible from within a function passed only the char* address

std::string old_x = x.c_str(); // old_x will terminate early if x embeds NUL
// Copies ASCIIZ data but could be less efficient as it needs to scan memory to
// find the NUL terminator indicating string length before allocating that amount
// of memory to copy into, or more efficient if it ends up allocating/copying a
// lot less content.
// Example, x == "ab\0cd" -> old_x == "ab".

// USING A VECTOR OF CHAR - AUTO, EXCEPTION SAFE, HINTS AT BINARY CONTENT, GUARANTEED CONTIGUOUS EVEN IN C++03
std::vector<char> old_x(x.data(), x.data() + x.size());       // without the NUL
std::vector<char> old_x(x.c_str(), x.c_str() + x.size() + 1);  // with the NUL

// USING STACK WHERE MAXIMUM SIZE OF x IS KNOWN TO BE COMPILE-TIME CONSTANT "N"
// (a bit dangerous, as "known" things are sometimes wrong and often become wrong)
char y[N + 1];
strcpy(y, x.c_str());

// USING STACK WHERE UNEXPECTEDLY LONG x IS TRUNCATED (e.g. Hello\0->Hel\0)
char y[N + 1];
strncpy(y, x.c_str(), N);  // copy at most N, zero-padding if shorter
y[N] = '\0';               // ensure NUL terminated

// USING THE STACK TO HANDLE x OF UNKNOWN (BUT SANE) LENGTH
char* y = alloca(x.size() + 1);
strcpy(y, x.c_str());

// USING THE STACK TO HANDLE x OF UNKNOWN LENGTH (NON-STANDARD GCC EXTENSION)
char y[x.size() + 1];
strcpy(y, x.c_str());

// USING new/delete HEAP MEMORY, MANUAL DEALLOC, NO INHERENT EXCEPTION SAFETY
char* y = new char[x.size() + 1];
strcpy(y, x.c_str());
//     or as a one-liner: char* y = strcpy(new char[x.size() + 1], x.c_str());
// use y...
delete[] y; // make sure no break, return, throw or branching bypasses this

// USING new/delete HEAP MEMORY, SMART POINTER DEALLOCATION, EXCEPTION SAFE
// see boost shared_array usage in Johannes Schaub's answer

// USING malloc/free HEAP MEMORY, MANUAL DEALLOC, NO INHERENT EXCEPTION SAFETY
char* y = strdup(x.c_str());
// use y...
free(y);

Inne powody, aby chcieć char*lub const char*wygenerowane zstring

Powyżej widziałeś, jak uzyskać ( const) char*i jak zrobić kopię tekstu niezależną od oryginału string, ale co możesz z tym zrobić ? Losowe rozproszenie przykładów ...

  • daj kodowi „C” dostęp do stringtekstu C ++ , jak wprintf("x is '%s'", x.c_str());
  • skopiuj xtekst do bufora określonego przez program wywołujący twoją funkcję (np. strncpy(callers_buffer, callers_buffer_size, x.c_str())) lub pamięć ulotną używaną dla urządzeń I / O (np. for (const char* p = x.c_str(); *p; ++p) *p_device = *p;)
  • Dołącz xdydaktycznego tekst do tablicy znaków zawierający już jakiś tekst ASCIIZ (np strcat(other_buffer, x.c_str())) - uważać, by nie przekroczyć bufor (w wielu sytuacjach może zajść potrzeba użycia strncat)
  • zwraca a const char*lub char*z funkcji (być może z powodów historycznych - klient używa istniejącego API - lub ze względu na kompatybilność z C nie chcesz zwracać a std::string, ale chcesz skopiować stringdane gdzieś dla dzwoniącego)
    • uważaj, aby nie zwrócić wskaźnika, który może zostać odwołany przez wywołującego po stringzmiennej lokalnej , na którą wskazany wskaźnik opuścił zakres
    • niektóre projekty ze skompilowanymi / połączonymi obiektami dla różnych std::stringimplementacji (np. STLport i natywny dla kompilatora) mogą przekazywać dane jako ASCIIZ, aby uniknąć konfliktów

4
Niezłe. Innym powodem, aby chcieć char * (non const) jest praca z transmisją MPI. Wygląda ładniej, jeśli nie musisz kopiować tam i z powrotem. Osobiście zaoferowałbym ciąg znaków char * const. Stały wskaźnik, ale edytowalny ciąg. Chociaż mogło to mieć wpływ na niejawną konwersję z const char * na string ...
bartgol 30.10.14

33

Użyj .c_str()metody dla const char *.

Możesz użyć, &mystring[0]aby uzyskać char *wskaźnik, ale jest kilka gotcha: niekoniecznie otrzymasz ciąg zakończony zerami i nie będziesz w stanie zmienić jego rozmiaru. Należy szczególnie uważać, aby nie dodawać znaków poza koniec łańcucha, w przeciwnym razie wystąpi przepełnienie bufora (i prawdopodobna awaria).

Nie było żadnej gwarancji, że wszystkie znaki będą częścią tego samego ciągłego bufora aż do C ++ 11, ale w praktyce wszystkie znane implementacje std::stringdziałały w ten sposób; zobacz Czy „& s [0]” wskazuje na ciągłe znaki w std :: string? .

Zauważ, że wiele stringfunkcji składowych przenosi bufor wewnętrzny i unieważnia wszelkie zapisane wskaźniki. Najlepiej użyć ich natychmiast, a następnie odrzucić.


1
powinieneś zauważyć, że data () zwraca const char * :), co masz na myśli, to & str [0], która zwraca ciągły, ale nie konieczny ciąg zakończony zerą.
Johannes Schaub - litb

1
@litb, Argh! To właśnie dostaję za próbę szybkiego uzyskania odpowiedzi. Korzystałem z twojego rozwiązania w przeszłości, nie wiem, dlaczego nie była to pierwsza rzecz, która przyszła mi do głowy. Zredagowałem swoją odpowiedź.
Mark Ransom,

2
Technicznie, przechowywanie std :: string będzie ciągłe tylko w C ++ 0x.
MSalters,

1
@MSalters, dzięki - nie wiedziałem o tym. Trudno byłoby mi jednak znaleźć implementację, w której tak nie było.
Mark Ransom,

2
char * wynik = strcpy (malloc (str. długość () + 1), str.c_str ());
cegprakash

21

C ++ 17

C ++ 17 (nadchodzący standard) zmienia streszczenie szablonu, basic_stringdodając nieobciążone przeciążenie data():

charT* data() noexcept;

Zwraca: Wskaźnik p taki, że p + i == i operator dla każdego i w [0, size ()].


CharT const * od std::basic_string<CharT>

std::string const cstr = { "..." };
char const * p = cstr.data(); // or .c_str()

CharT * od std::basic_string<CharT>

std::string str = { "..." };
char * p = str.data();

C ++ 11

CharT const * od std::basic_string<CharT>

std::string str = { "..." };
str.c_str();

CharT * od std::basic_string<CharT>

Począwszy od C ++ 11 standard mówi:

  1. Przedmioty podobne do znaków w basic_stringobiekcie należy przechowywać w sposób ciągły. Oznacza to, że dla każdego basic_stringprzedmiotu stożsamość &*(s.begin() + n) == &*s.begin() + nzachowuje wszystkie wartości tego nrodzaju 0 <= n < s.size().

  1. const_reference operator[](size_type pos) const;
    reference operator[](size_type pos);

    Zwraca: *(begin() + pos)jeśli pos < size(), w innym przypadku odniesienie do obiektu typu CharTo wartości CharT(); wartości odniesienia nie można modyfikować.


  1. const charT* c_str() const noexcept;
    const charT* data() const noexcept;

    Zwraca: Wskaźnik p taki, że p + i == &operator[](i)dla każdego iw [0,size()].

Istnieją możliwe do oddzielenia sposoby uzyskania wskaźnika postaci niezmiennej.

1. Użyj ciągłego przechowywania C ++ 11

std::string foo{"text"};
auto p = &*foo.begin();

Zawodowiec

  • Prosty i krótki
  • Szybka (tylko metoda bez kopii)

Cons

  • Ostateczne '\0'nie ma być zmieniane / niekoniecznie częścią pamięci non-const.

2. Użyj std::vector<CharT>

std::string foo{"text"};
std::vector<char> fcv(foo.data(), foo.data()+foo.size()+1u);
auto p = fcv.data();

Zawodowiec

  • Prosty
  • Automatyczna obsługa pamięci
  • Dynamiczny

Cons

  • Wymaga kopii ciągu

3. Użyj, std::array<CharT, N>jeśli Njest stała czasowa kompilacji (i wystarczająco mała)

std::string foo{"text"};
std::array<char, 5u> fca;
std::copy(foo.data(), foo.data()+foo.size()+1u, fca.begin());

Zawodowiec

  • Prosty
  • Obsługa pamięci stosu

Cons

  • Statyczny
  • Wymaga kopii ciągu

4. Przydział pamięci Raw z automatycznym usuwaniem pamięci

std::string foo{ "text" };
auto p = std::make_unique<char[]>(foo.size()+1u);
std::copy(foo.data(), foo.data() + foo.size() + 1u, &p[0]);

Zawodowiec

  • Mały ślad pamięci
  • Automatyczne usuwanie
  • Prosty

Cons

  • Wymaga kopii ciągu
  • Statyczne (dynamiczne użycie wymaga dużo więcej kodu)
  • Mniej funkcji niż wektor lub tablica

5. Przydział pamięci surowej z obsługą ręczną

std::string foo{ "text" };
char * p = nullptr;
try
{
  p = new char[foo.size() + 1u];
  std::copy(foo.data(), foo.data() + foo.size() + 1u, p);
  // handle stuff with p
  delete[] p;
}
catch (...)
{
  if (p) { delete[] p; }
  throw;
}

Zawodowiec

  • Maksymalna „kontrola”

Kon

  • Wymaga kopii ciągu
  • Maksymalna odpowiedzialność / podatność na błędy
  • Złożony

9

Pracuję z interfejsem API z wieloma funkcjami dostępnymi jako wejście char*.

Stworzyłem małą klasę, aby zmierzyć się z tego rodzaju problemem, zaimplementowałem idiom RAII.

class DeepString
{
        DeepString(const DeepString& other);
        DeepString& operator=(const DeepString& other);
        char* internal_; 

    public:
        explicit DeepString( const string& toCopy): 
            internal_(new char[toCopy.size()+1]) 
        {
            strcpy(internal_,toCopy.c_str());
        }
        ~DeepString() { delete[] internal_; }
        char* str() const { return internal_; }
        const char* c_str()  const { return internal_; }
};

I możesz użyć go jako:

void aFunctionAPI(char* input);

//  other stuff

aFunctionAPI("Foo"); //this call is not safe. if the function modified the 
                     //literal string the program will crash
std::string myFoo("Foo");
aFunctionAPI(myFoo.c_str()); //this is not compiling
aFunctionAPI(const_cast<char*>(myFoo.c_str())); //this is not safe std::string 
                                                //implement reference counting and 
                                                //it may change the value of other
                                                //strings as well.
DeepString myDeepFoo(myFoo);
aFunctionAPI(myFoo.str()); //this is fine

Wywołałem klasę, DeepStringponieważ tworzy ona głęboką i unikalną kopię DeepStringistniejącego łańcucha (której nie można skopiować ).


3
Unikałbym tej konwencji nazewnictwa. c_str()używany przez stdjest skrótem dla „C-string”, a nie „const string” i str()zawsze zwraca a std::basic_string, not char*(na przykład std::stringstream::str())
bcrist

8
char* result = strcpy((char*)malloc(str.length()+1), str.c_str());

1
wygląda fantazyjnie, ale naprawdę trudno zrozumieć ... Proste jest najlepsze IMO
Naeem A. Malik

4
strcpy (), malloc (), length () i c_str () są podstawowymi funkcjami i nie ma w tym nic trudnego. Po prostu przydzielanie pamięci i kopiowanie.
cegprakash

5
tak, funkcje są podstawowe, ale skręciłeś je i zgięłeś, aby wyglądały jak miska spaghetti lub potwór z jednej linijki :)
Naeem A. Malik,

4
Tak, funkcje są podstawowe, ale ... czy pamiętasz, kiedy zaczynasz zajmować się językiem programowania? Kilka linii do wyjaśnienia i naprawdę pomoże neoficie dowiedzieć się, dlaczego na przykład jest inna lub lepsza niż ta odpowiedź :)
Hastur

2
@cegprakash: Ilekroć istnieje malloc (), musi istnieć również free (). W przeciwnym razie kod wycieka pamięć, podobnie jak rozwiązanie w odpowiedzi. Przydzielanie pamięci bez wskazywania przynajmniej wymaganego zwolnienia jest złą praktyką w przypadku takich pytań.
Striezel,

7

Zobacz to:

string str1("stackoverflow");
const char * str2 = str1.c_str();

Pamiętaj jednak, że to zwróci a const char *.

W przypadku a char *użyj przycisku, strcpyaby skopiować go do innej chartablicy.


23
Cześć, to, co napisałeś, zostało już powiedziane wiele razy, z dodatkowymi szczegółami, w innych odpowiedziach na pięcioletnie pytanie. Odpowiadanie na starsze pytania jest w porządku, ale tylko jeśli dodasz nowe informacje. W przeciwnym razie to tylko hałas.
Mat

7
Osobiście doceniam prostotę.
TankorSmash

-4

Spróbuj tego

std::string s(reinterpret_cast<const char *>(Data), Size);
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.