Tablica char a char* i string

0

Jeżeli chodzi o wskaźniki i tablice to lecąc z książką mam wiedzę tylko o tym że:

  1. char zmienna; może przechowywać poszczególny jeden znak
  2. char zmienna[] = "napis"; sam wylicza wielkośc tablicy i dodaje 0 na koniec.
  3. char* zmienna = "napis"; wskaźnik do char pokazuje na adres łańcucha w cudzysłowie, dokładniej na początek łańcucha.
    teraz mogę odnosić się do *char zmienna w sposoby:
    3a) cout << zmienna;
    3b) cout << *(zmienna+2); - trzeci znak napisu
    3c) cout << zmienna[2]; - to samo
  4. tablica typu char*.

Pytania:
do pkt 3. Czemu mogę zmienna traktować jak tablicę jeśli to tylko wskaźnik do typu char? Nie ma tu żadnej tablicy.
I w ogóle jak wskaźnik do typu char to czemu mogę przypisać całe słowo a nie pojedyńczy znak?

do pkt4. Na necie nie widzę info, w książce jest zadanie, a nic nie napisał o tym wcześniej. Co to w ogóle jest? Jak to działa? W książce jest zadanie aby wpisać do tablicy typu char* 12 nazw miesiąca, a potem z nich korzystać dalej w programie.
Mogę to też zrobić za pomocą tablicy stringów. Stringi to łatwo

string nazwa[12];

for(int i=0;i<12;i++)
cin >> string[i];

Trochę nie czaję jak to zrobić z char* []

może ktoś dopomóc?

1
#include <iostream>

using namespace std;

int main()
{
    const char * miesiac[] = { "Styczen", "Luty", "Marzec",
                               "Kwiecien", "Maj", "Czerwiec",
                               "Lipiec", "Sierpień", "Wrzesień",
                               "Pazdziernik", "Listopad", "Grudzien"
                             };
    int num = ((unsigned)(sizeof(miesiac) / sizeof(miesiac[0]) ) );

    for(int i = 0; i < num; ++i)
    {
        const char ** wsk = &miesiac[i];
        cout<<*wsk;++wsk;
        if((i+1)!=num)cout<<", ";else cout<<".";
        if(!((i+1)%3))cout<<"\n";
    }

    return 0;
}
2

Trochę nie czaję jak to zrobić z char* []

A spójrz jak wygląda Twój domyślny main i uświadom sobie, że argv przechowuje tablicę do wejściowych parametrów w postaci stringów. A najczęściej wygląda to tak:

int main(int argc, char *argv[])

Można to również zapisać w ten sposób

int main(int argc, char **argv)

ale ja preferuje ten pierwszy, bo lepiej obrazuje intencje.

Nie ma tu żadnej tablicy.

W C/C++ tak naprawdę nie było prawdziwej tablicy aż do pojawienia się std::array. Wcześniej rolę tablicy pełniły wskaźniki do ciągłego obszaru pamięci i tak trzeba o nich myśleć, to zawsze są tylko wskaźniki, które udają tablicę bo został do nich dodany składniowy cukier w postaci [].

1

do pkt 3. Czemu mogę zmienna traktować jak tablicę jeśli to tylko wskaźnik do typu char? Nie ma tu żadnej tablicy.
I w ogóle jak wskaźnik do typu char to czemu mogę przypisać całe słowo a nie pojedyńczy znak?

Możesz tak ją traktować, ponieważ kompilator i tak rozbierze "zmienna[2]" w to "*(zmienna+2)", to są wyrażenia tożsame.

Możesz przypisać całe słowo, ponieważ jak zauważyłeś jest to wskaźnik, on tylko wskazuje na dane miejsce w pamięci, gdzie zaczyna się napis, a nie przechowuje dane.
Typ wskaźnika jest tylko drogowskazem dla kompilatora, jak ma traktować przechowywane w pamięci dane, iterując po tablicy np. "char", wie że żeby trafić na kolejny "char" w pamięci musi przesunąć się o 8 bitów, czyli bajt.

Masz powiedzmy napis "dupa" i wskaźnik char który posiada adres początku tego napisu, czyli litery "d" - załóżmy że jest to 0x8000.
Chcąc wyświetlić ten napis, przekazujesz do funkcji wskaźnik, kompilator wie, że jest to wskaźnik typu char, czyli żeby dotrzeć do kolejnej
litery musi przeskoczyć o 8 bitów, czyli przejść do adresu 0x8001.
Tak sobie sunie po pamięci, aż nie natrafi na znak null - "\0" - który informuje o końcu napisu.

Może coś popieprzyłem, ale mniej więcej tak to wygląda.

1
char* zmienna = "napis"; 

W C++11 i dalej taki zapis jest w zasadzie błędny. W C++98/03 jest poprawny, podobnie jak w C. ALE w praktyce zarówno w C, jak i C++98 taki zapis de facto jest tożsamy z:

const char* zmienna = "napis"; 

ergo, nie wolno tego napisu modyfikować, ponieważ będzie to zachowanie niezdefiniowane.

Jeśli chcesz móc modyfikować ten napis, musisz zapisać go tak:

char zmienna[] = "napis"; 

W C++ i C tablica "rozkłada" (ang decay) się do adresu jej początkowego elementu, ale za żadne skarby to NIE JEST to samo.

Jeżeli jesteś zainteresowany co tam implementacyjnie siedzi:

{
   char zmienna[] = "napis";
   int i = 3;
   doStuff();
   //cos tam dalej
}

z reguły da po kompilacji coś takiego (tzn. traktuj to jako model myślowy):

{
  char z0 ='n'; //z od zmienna... w sensie: pola tablicy są lokalne
  char z1 ='a';
  char z2 ='p';
  char z3 ='i';
  char z4 ='s';
  char z5 ='\0'; //znak konca napisu
  int i = 3;
  doStuff(); 
}

Natomiast:

{
   char* zmienna = "napis";
   int i = 3;
   doStuff();
   //cos tam dalej
}

da coś takiego

const char GLOBAL_NAPIS[] = "napis";

{
  char* zmienna = &GLOBAL_NAPIS[0];
  int i =3;
  doStuff();
  // i dalej
}

@several:

W C/C++ tak naprawdę nie było prawdziwej tablicy aż do pojawienia się std::array. Wcześniej rolę tablicy pełniły wskaźniki do ciągłego obszaru pamięci i tak trzeba o nich myśleć, to zawsze są tylko wskaźniki, które udają tablicę bo został do nich dodany składniowy cukier w postaci [].

ośmielę się nie zgodzić. Zapis [] + init powodował inicjalizację na stosie (czy raczej: automatic storage duration). Zapis z gwiazdką inicjował całkiem inaczej. A std::array to przeca nic innego niż wrapper na [] który nie powoduje implicit decay to pointer.

@NewbieKodeRR: jak znasz angielski możesz jeszcze tu zajrzeć, https://stackoverflow.com/questions/69376685/is-a-char-array-more-efficient-than-a-char-pointer-in-c/69377211#69377211 (autopromocja alert), tam OP miał podobny problem.

0

padło wiele szczegółowych objaśnień, jeszcze warto powiedzieć, kiedy wskaźnik wskazuje na dane tylko do odczytu (czego łamanie rzadko daje twardy wyjątek, częściej UB)

const char GLOBAL_NAPIS[] = "napis";

{
  **const** char* zmienna = &GLOBAL_NAPIS[0];
  int i =3;
  doStuff();
  // i dalej
}
1

ośmielę się nie zgodzić. Zapis [] + init powodował inicjalizację na stosie (czy raczej: automatic storage duration). Zapis z gwiazdką inicjował całkiem inaczej.

No ok, statyczny storage to nie faktyczna pamięć jak już chcemy się czepiać. Tym nie mniej wskaźnik do takiego literału będzie miał taką samą mechanikę jak ten wskazujący na miejsce do faktycznej pamięci, czy na stosie czy na stercie.

A std::array to przeca nic innego niż wrapper na [] który nie powoduje implicit decay to pointer.

Dokładnie. O tablicy możemy mówić jeśli mamy do czynienia z T[]. W C/C++ taka tablica niemal od razu rozpada nam się do T* więc tablicą już nie jest, poza paroma wyjątkami jak sizeof czy przekazywanie przez referencje. std::array rozpaść się nie może, więc to w końcu pełnoprawna tablica a nie wskaźnik, który próbuje udawać coś innego.

0

@several:

W C/C++ tak naprawdę nie było prawdziwej tablicy aż do pojawienia się std::array
O tablicy możemy mówić jeśli mamy do czynienia z T[].

nie traktuj tego jako atak ale widzisz czemu się czepiałem ;)

1 użytkowników online, w tym zalogowanych: 0, gości: 1