wskaźniki a tablice wielowymiarowe

wskaźniki a tablice wielowymiarowe
Gynvael Coldwind
  • Rejestracja:ponad 21 lat
  • Ostatnio:11 dni
  • Lokalizacja:Zurich, Switzerland
  • Postów:457
5

Odskakując trochę od dyskusji, szperam sobie co tam ludzie na necie piszą o tym. Znalazłem m.in. taki cytat na grupie ISO C++ Standard - Discussion (wytłuszczenie moje):

If you just want to flatten a multidimensional array, there's also taking the address of the first object, and using that as the basis for address arithmetic. But according to Clang, and my intuition of the standard, this also isn't allowed in constexpr since address arithmetic is only allowed in an immediate array, and subsequent arrays in the multidimensional object are past-the-end. This is a standard flaw in my humble opinion, but not a defect. Anyway GCC allows it without complaint, at least for this test: [...]

Źródło: https://groups.google.com/a/isocpp.org/forum/#!searchin/std-discussion/multidimensional/std-discussion/GxGgAaMoO40/nS26dro7ljkJ
Autorem jest David Krauss, którego nazwisko znalazłem przy propozycjach do standardu z paru ostatnich lat (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/).

Czyli kolejny głos na to, że to UB wg standardu, i jednocześnie na to, że raczej powinno się to konkretne UB zignorować, bo (jak to czasem bywa), standard swoje, a implementacje i programiści swoje ;) (i dobrze, inaczej by tak jakoś nudno było)


peace,
gynvael.coldwind//vx "Imagination is more important than knowledge..." Albert Einstein
Endrju
To, że to działa to kwestia bezdyskusyjna. Tak samo bezdyskusyjne jest to, że to jest UB. Te dwie rzeczy się nie wykluczają. Na to na prawdę nie potrzeba opinii kogokolwiek, bo to nie jest kwestia opinii. To jest wprost napisane w tekście standardu i wynika z 1.8, 8.3.4, 5.7. Jestem pod wrażeniem, że taka prosta rzecz jest dla kogokolwiek niezrozumiała albo w ogóle nowa i że jest się o co spierać. Nie ma. Z faktami nie można dyskutować. Możesz im pokazać dokładnie standard i cytować ekspertów - nic to nie da. Dlatego zakończyłem dyskusję po 2 poście. :-)
vpiotr
  • Rejestracja:ponad 13 lat
  • Ostatnio:prawie 3 lata
1

Z drugiej strony nawet K&R ponoć tak to rozumieli jak @Azarien.

http://stackoverflow.com/a/29734680

/me: hejtuje UB

Azarien
  • Rejestracja:ponad 21 lat
  • Ostatnio:2 dni
1
adrian17 napisał(a):

Jeśli sto reguł na coś "zezwala" i wszystkie kompilatory definiują zachowanie zgodnie z intuicją ale choćby jedna mówi że to to UB, to oficjalnie jest to UB.

Błąd logiczny. Jest dokładnie odwrotnie. Jeśli sto reguł mówi, że coś jest niezdefiniowane (czyli sto reguł nie definiuje zachowania), a choćby jedna je definiuje, to zachowanie jest zdefiniowane.
Oczywiście standard powinien być spisany na tyle precyzyjnie, żeby takich sytuacji nie było. Ale są.

edytowany 1x, ostatnio: Azarien
Zobacz pozostałe 3 komentarze
A1
Nie, bo tam dochodzi kwestia alignmentu między polami; tego problemu nie ma w tablicach.
kq
Jak masz te same typy w środku to masz taki sam alignment jak w tablicy.
A1
"Implementation alignment requirements might cause two adjacent members not to be allocated immediately after each other". Jedyną gwarancją jest stosunek mniejszy/większy między wskaźnikami.
kq
Ale te requirementy nie są różne dla tego samego typu.
A1
OK. Jest to bardziej złożona implikacja, ale fakt, też podobnie można by to argumentować.
Gynvael Coldwind
  • Rejestracja:ponad 21 lat
  • Ostatnio:11 dni
  • Lokalizacja:Zurich, Switzerland
  • Postów:457
3

No nic, czas iść spać :) Chyba możemy się zgodzić, że się nie zgadzamy, a przynajmniej, że standard się nie zgadza z rzeczywistością (mild shock). Dzięki za dyskusje Panowie :)


peace,
gynvael.coldwind//vx "Imagination is more important than knowledge..." Albert Einstein
Azarien
Ależ ja się zgadzam - jest to wada standardu i tym gorzej dla standardu ;-)
A1
  • Rejestracja:prawie 9 lat
  • Ostatnio:około 5 lat
  • Postów:3
0

...ja chyba właśnie zostałem przekonany. W ogólnym przypadku to jest UB, ale w przypadku tablic wielowymiarowych fakt, że standard jednoznacznie określa ich rozkład w pamięci, definiuje rezultat który otrzymamy.

W każdym razie niezależnie od tego jaka odpowiedź jest poprawna, zgadzam się z Davidem Kraussem że standard powinien to lepiej określić.

edytowany 1x, ostatnio: adrian17
somekind
Moderator
  • Rejestracja:około 17 lat
  • Ostatnio:dzień
  • Lokalizacja:Wrocław
0

Rozumiem, że ten wątek ma na celu udowodnienie, że istnieją jednak języki bardziej skopane niż JavaScript?

Gynvael Coldwind
Naprawdę chcesz wywołać kolejną dyskusję? ;DDD
vpiotr
tego nie trzeba udowadniać, teraz każdy przedszkolak o tym wie. http://ksiegarnia.pwn.pl/JavaScript-dla-dzieci,84924599,p.html
stasinek
  • Rejestracja:ponad 9 lat
  • Ostatnio:prawie 9 lat
  • Postów:17
0
Endrju napisał(a):
Azarien napisał(a):

Wskaźnik int* można indeksować tak jakby był tablicą, ale jednowymiarową.

Jeżeli masz tablicę

Kopiuj
int tab[2][2] = {{1,2},{3,4}};

to pod wskaźnikiem

Kopiuj
int *wsk = &tab[0][0];

możesz zobaczyć cztery inty dwuwymiarowej tablicy, które kolejno ułożone są w pamięci: wsk[0]==1, wsk[1]==2, wsk[2]==3, wsk[3]==4.

Ale nie zadziała to jak dwuwymiarowa tablica.

Absolutnie nie! To jest undefined behavior (5.7/5 w C++14, 6.5.6/8 w C11, przekroczenie rozmiaru tablicy przy dodawniu wskaźnik + offset).

Oboje macie racje w detalach wasze racje są sprzeczne. Racja Azariena jest niebezpieczna.. dogłębnie wyjaśnie to później bo teraz nie mam czasu. Poza tym wyjaśnie róznice co do wpływu na szybkość działania programu, adresowanie elementów tych tablic itd. Wklejam kod dla autora tematu żeby się zaznajomił ze wskaźnikami. Może sobie poekspetymentować, zamiast maloka wstawić wskaźnik do pierwszej i sprawdzi czy wyjdzie tak samo. Wskaźniki są jak zapałki w rękach dziecka. Prędzej czy póxniej wywołują pożar.

Kopiuj
 //---------------------------------------------------------------------------
// Example program
#include <string>
#include <assert.h>
#include <stdio.h >
#include <stdlib.h>
#pragma hdrstop

//---------------------------------------------------------------------------

#pragma argsused
int main(int argc, char* argv[])
{
char tab_azariena[3][5][7];
// zmienna pomocnicza do ustalenia zawartości kolejnych komórek tabeli
// Po koleji znakami ASCII od 1.2.3.4.5.. przez ABCD..abcd (między nimi kilka znaków międzynarodowych)
printf("Prodgram drukuje dwie tabele[3][5][7] jedna a[] to stosowy klasyk druga e[] utworzona z 3 list wskaznikow");
char num = '1';

for (int z = 0; z < 3; z++) {
	for (int y = 0; y < 5; y++) {
		for (int x = 0; x < 7; x++) {
        	tab_azariena[z][y][x] = num++;
            }
        }
    }
// dla pewności na ostatnim logicznie polu znak '\0'
tab_azariena[2][4][6] = '\t';
num = '1';

char ***tab;
tab = (char***)malloc(3*sizeof(char**));
assert(tab!= NULL);
for (int z = 0; z < 3; z++) {
    char **az = (char**)malloc(5*sizeof(char*));
    assert(az!= NULL);
	tab[z] = az;
	for (int y = 0; y < 5; y++) {
    	char *ay = (char*)malloc(7*sizeof(char));
        assert(ay!= NULL);
    	tab[z][y] = ay;
		// wypełnianie tablic liczb z memset jest kilkukrotnie szybsze niż naiwna pętla a to dzięki ASM x86: "REP SETB"
        ::memset((void*)(tab[z][y]),'0',5);
        //ale można i tak i pętlą
		for (int x = 0; x < 7; x++) {
        	tab[z][y][x] = num++;
        	}
        }
    }
// dla pewności na ostatnim logicznie polu znak '\0'
tab[2][4][6] = '\t';

for (int z = 0; z < 3; z++) {
        	printf("Warstwa tab[%d]:\n",z);
	for (int y = 0; y < 5; y++) {
		for (int x = 0; x < 7; x++) {
        	printf("a[%c] n[%c],",tab_azariena[z][y][x],tab[z][y][x]);
        	}
        	printf("\n");
        }
    }

printf("W kolejnej linijce wydrukuje tab_azariena jako liniowy wycinek pamieci:\n");
printf("%s",(char*)tab_azariena);
printf("\n");
printf("Koniec!\n");
system("pause");
}
//---------------------------------------------------------------------------

Można go przetestować u siebie albo na czymś w stylu(?):
http://cpp.sh/

Program dowodzi tezy że Azarien ma racje, w kolejnym poście dowiode tezy że nawet jeśli ma racje to nic dobrego z niej nie wynika. A w istocie taka świadomość może rodzić więcej problemów niż pożytku.

Nigdy nie wierz kobiecie, tablicy wielowymiarowej i wskaźnikom.

edytowany 2x, ostatnio: stasinek
Azarien
że pamięć w tablicy jest liniowa - ale akurat to nie podlegało wątpliwościom, bo to jest zapewnione. problemem jest cytowane wyżej nieszczęsne, źle sformułowane zdanie kończące się słowami “otherwise, the behavior is undefined”.
mwl4
// dla pewności na ostatnim logicznie polu znak '\0' tab_azariena[2][4][6] = '\t'; No.. w uj.
pingwindyktator
  • Rejestracja:ponad 12 lat
  • Ostatnio:18 dni
  • Lokalizacja:Kraków
  • Postów:1055
6

co


do not code, write prose
vpiotr
przeszliśmy płynnie od standardów C do rocka, dziwne? https://www.youtube.com/watch?v=awY1MRlMKMc
DR
  • Rejestracja:około 11 lat
  • Ostatnio:ponad 5 lat
  • Postów:31
1

ogólnie z tablicami wielowymiarowymi jest tak że jeśli mamy np tablice tab[x][y]; to obliczenie miejsca polega na tym (j*y+i)*dlugosc_jednej_zmiennej a więc musi być znana długość wszystkich indeksów oprócz tego najbardziej z lewej
ponadto ponieważ chyba [] ma większy priorytet operatorów więc deklaracja wskaźnika powinna wyglądać tak jak to napisałem w kodzie

Kopiuj
#include <iostream>
#include <stdlib.h>
 
using namespace std;
 
int main()
{
    int index = 3;
    int index1 = 1;
    int number = 0;
    int i,j;
    int tab[index][index1];
    int (*wsk)[index1]; //deklaracja wskaźnika
 
    wsk = tab;//nazwa tablicy jest adresem jej zerowego elementu
 
    for ( i = 0  ; i < index ; i++)
    {
        for ( j = 0 ; j < index1 ; j++ )
        {
            cout << "podaj liczbe: ";
            cin >> number;
 
            tab[i][j] = number;
            cout << "tab[" << i << "][" << j << "] = " << number << endl;
            system("clear");
        }
    }
    //cout << tab[2][0] << endl;
    cout << "podaj 'i'' i 'j'" << endl;
    cin >> i;
    cin >> j;
 
    cout << wsk[i][j] << endl;// wskaźnik możemy używać jak tablice
 
    return 0;
}
 
87
  • Rejestracja:prawie 9 lat
  • Ostatnio:ponad 2 lata
  • Postów:208
0
dreammaker napisał(a):

ogólnie z tablicami wielowymiarowymi jest tak że jeśli mamy np tablice tab[x][y]; to obliczenie miejsca polega na tym (j*y+i)*dlugosc_jednej_zmiennej a więc musi być znana długość wszystkich indeksów oprócz tego najbardziej z lewej
ponadto ponieważ chyba [] ma większy priorytet operatorów więc deklaracja wskaźnika powinna wyglądać tak jak to napisałem w kodzie

Kopiuj
#include <iostream>
#include <stdlib.h>
 
using namespace std;
 
int main()
{
    int index = 3;
    int index1 = 1;
    int number = 0;
    int i,j;
    int tab[index][index1];
    int (*wsk)[index1]; //deklaracja wskaźnika
 
    wsk = tab;//nazwa tablicy jest adresem jej zerowego elementu
 
    for ( i = 0  ; i < index ; i++)
    {
        for ( j = 0 ; j < index1 ; j++ )
        {
            cout << "podaj liczbe: ";
            cin >> number;
 
            tab[i][j] = number;
            cout << "tab[" << i << "][" << j << "] = " << number << endl;
            system("clear");
        }
    }
    //cout << tab[2][0] << endl;
    cout << "podaj 'i'' i 'j'" << endl;
    cin >> i;
    cin >> j;
 
    cout << wsk[i][j] << endl;// wskaźnik możemy używać jak tablice
 
    return 0;
}
 

Dzięki wielkie o to chodziło już wszystko śmiga. Jeszcze raz dzieki.

DR
  • Rejestracja:około 11 lat
  • Ostatnio:ponad 5 lat
  • Postów:31
0

Cieszę się że mogłem pomóc polecam bardzo książkę Symfonia C++ Jerzego Grębosza (najnowsza ma dopisek Standard lecz powstaje jeszcze nowsza) po lekturze tej książki powinieneś wszystko lub prawie wszystko zrozumieć o tym języku

SO
Kolejny troll czy niespełna rozumu?
vpiotr
Polecam przeczytać jeszcze coś oprócz Symfonii. Najlepiej kilka książek (o C++).
pingwindyktator
"powinieneś wszystko lub prawie wszystko zrozumieć o tym języku" ;DDDDDDD
kq
Moderator C/C++
  • Rejestracja:prawie 12 lat
  • Ostatnio:dzień
  • Lokalizacja:Szczecin
3

Takie posty są powodem, aby można było dawać negatywne oceny postom.

Symfonia C++ jest przestarzała, ale nawet w czasach gdy opisywała w miarę obecny standard C++, twierdzenie, że uczy czegokolwiek poza podstawami kompromituje mówcę.


DR
ale wiesz, że takie pisanie(mówię tutaj o polecaniu symfonii) nie wzięło się znikad? Na uczelniach, ośmiele się powiedzieć 90% wykładowców korzysta z materialow z symfonii przy tworzeniu ćwiczen i są jeszcze osoby ktore umieszczają tą ksiażke w syllabusie, wiec studenci mniej ogarnieci dopiero na rozmowie o prace dowiadują się, ze to czego się uczyli to nie to co napisali w cv
kq
Sure, ale w żaden sposób nie zmienia to faktu, że Symfonii nie powinno się polecać.
DR
ale to mów to wykładowcą z tytulami :P mnie nie mysisz przekonywać :)
stasinek
@degorio - wykładowcy C z tytułami i speca od laborek z programowania to przeważnie stare dziady mentalnie tkwiący w latach 80, którym w głowie wyłącznie emerytura. Twój wybór czy łykasz ich wiedze czy traktujesz jak powinieneś - wiedza bezużyteczna.
DR
@stasinek a kto mówi o C? W C++ są młodzi z tytułami, patrz AGH, czy UW, ale leniwi pod względem dydaktycznym, więc powielają cw które oni mieli jaak byli studentami :P
DR
  • Rejestracja:około 11 lat
  • Ostatnio:ponad 5 lat
  • Postów:31
0

Problem rozwiązałem? Rozwiązałem więc o co wam chodzi? Ja się C++ nauczyłem właśnie z Symfonii a potem jeszcze poprawiłem "liźnięciem" Asemblera żeby mieć lepsze pojęcie 700 osób przeglądało ten wątek i co i ja rozwiązałem problem(zawsze się znajdą malkontenci ) jeśli znacie jakieś lepsze książki to byłbym wdzięczny za polecenie mi jakichś jeśli rzeczywiście są lepsze to chętnie poczytam. I niech Kod będzie z Wami

Kliknij, aby dodać treść...

Pomoc 1.18.8

Typografia

Edytor obsługuje składnie Markdown, w której pojedynczy akcent *kursywa* oraz _kursywa_ to pochylenie. Z kolei podwójny akcent **pogrubienie** oraz __pogrubienie__ to pogrubienie. Dodanie znaczników ~~strike~~ to przekreślenie.

Możesz dodać formatowanie komendami , , oraz .

Ponieważ dekoracja podkreślenia jest przeznaczona na linki, markdown nie zawiera specjalnej składni dla podkreślenia. Dlatego by dodać podkreślenie, użyj <u>underline</u>.

Komendy formatujące reagują na skróty klawiszowe: Ctrl+B, Ctrl+I, Ctrl+U oraz Ctrl+S.

Linki

By dodać link w edytorze użyj komendy lub użyj składni [title](link). URL umieszczony w linku lub nawet URL umieszczony bezpośrednio w tekście będzie aktywny i klikalny.

Jeżeli chcesz, możesz samodzielnie dodać link: <a href="link">title</a>.

Wewnętrzne odnośniki

Możesz umieścić odnośnik do wewnętrznej podstrony, używając następującej składni: [[Delphi/Kompendium]] lub [[Delphi/Kompendium|kliknij, aby przejść do kompendium]]. Odnośniki mogą prowadzić do Forum 4programmers.net lub np. do Kompendium.

Wspomnienia użytkowników

By wspomnieć użytkownika forum, wpisz w formularzu znak @. Zobaczysz okienko samouzupełniające nazwy użytkowników. Samouzupełnienie dobierze odpowiedni format wspomnienia, zależnie od tego czy w nazwie użytkownika znajduje się spacja.

Znaczniki HTML

Dozwolone jest używanie niektórych znaczników HTML: <a>, <b>, <i>, <kbd>, <del>, <strong>, <dfn>, <pre>, <blockquote>, <hr/>, <sub>, <sup> oraz <img/>.

Skróty klawiszowe

Dodaj kombinację klawiszy komendą notacji klawiszy lub skrótem klawiszowym Alt+K.

Reprezentuj kombinacje klawiszowe używając taga <kbd>. Oddziel od siebie klawisze znakiem plus, np <kbd>Alt+Tab</kbd>.

Indeks górny oraz dolny

Przykład: wpisując H<sub>2</sub>O i m<sup>2</sup> otrzymasz: H2O i m2.

Składnia Tex

By precyzyjnie wyrazić działanie matematyczne, użyj składni Tex.

<tex>arcctg(x) = argtan(\frac{1}{x}) = arcsin(\frac{1}{\sqrt{1+x^2}})</tex>

Kod źródłowy

Krótkie fragmenty kodu

Wszelkie jednolinijkowe instrukcje języka programowania powinny być zawarte pomiędzy obróconymi apostrofami: `kod instrukcji` lub ``console.log(`string`);``.

Kod wielolinijkowy

Dodaj fragment kodu komendą . Fragmenty kodu zajmujące całą lub więcej linijek powinny być umieszczone w wielolinijkowym fragmencie kodu. Znaczniki ``` lub ~~~ umożliwiają kolorowanie różnych języków programowania. Możemy nadać nazwę języka programowania używając auto-uzupełnienia, kod został pokolorowany używając konkretnych ustawień kolorowania składni:

```javascript
document.write('Hello World');
```

Możesz zaznaczyć również już wklejony kod w edytorze, i użyć komendy  by zamienić go w kod. Użyj kombinacji Ctrl+`, by dodać fragment kodu bez oznaczników języka.

Tabelki

Dodaj przykładową tabelkę używając komendy . Przykładowa tabelka składa się z dwóch kolumn, nagłówka i jednego wiersza.

Wygeneruj tabelkę na podstawie szablonu. Oddziel komórki separatorem ; lub |, a następnie zaznacz szablonu.

nazwisko;dziedzina;odkrycie
Pitagoras;mathematics;Pythagorean Theorem
Albert Einstein;physics;General Relativity
Marie Curie, Pierre Curie;chemistry;Radium, Polonium

Użyj komendy by zamienić zaznaczony szablon na tabelkę Markdown.

Lista uporządkowana i nieuporządkowana

Możliwe jest tworzenie listy numerowanych oraz wypunktowanych. Wystarczy, że pierwszym znakiem linii będzie * lub - dla listy nieuporządkowanej oraz 1. dla listy uporządkowanej.

Użyj komendy by dodać listę uporządkowaną.

1. Lista numerowana
2. Lista numerowana

Użyj komendy by dodać listę nieuporządkowaną.

* Lista wypunktowana
* Lista wypunktowana
** Lista wypunktowana (drugi poziom)

Składnia Markdown

Edytor obsługuje składnię Markdown, która składa się ze znaków specjalnych. Dostępne komendy, jak formatowanie , dodanie tabelki lub fragmentu kodu są w pewnym sensie świadome otaczającej jej składni, i postarają się unikać uszkodzenia jej.

Dla przykładu, używając tylko dostępnych komend, nie możemy dodać formatowania pogrubienia do kodu wielolinijkowego, albo dodać listy do tabelki - mogłoby to doprowadzić do uszkodzenia składni.

W pewnych odosobnionych przypadkach brak nowej linii przed elementami markdown również mógłby uszkodzić składnie, dlatego edytor dodaje brakujące nowe linie. Dla przykładu, dodanie formatowania pochylenia zaraz po tabelce, mogłoby zostać błędne zinterpretowane, więc edytor doda oddzielającą nową linię pomiędzy tabelką, a pochyleniem.

Skróty klawiszowe

Skróty formatujące, kiedy w edytorze znajduje się pojedynczy kursor, wstawiają sformatowany tekst przykładowy. Jeśli w edytorze znajduje się zaznaczenie (słowo, linijka, paragraf), wtedy zaznaczenie zostaje sformatowane.

  • Ctrl+B - dodaj pogrubienie lub pogrub zaznaczenie
  • Ctrl+I - dodaj pochylenie lub pochyl zaznaczenie
  • Ctrl+U - dodaj podkreślenie lub podkreśl zaznaczenie
  • Ctrl+S - dodaj przekreślenie lub przekreśl zaznaczenie

Notacja Klawiszy

  • Alt+K - dodaj notację klawiszy

Fragment kodu bez oznacznika

  • Alt+C - dodaj pusty fragment kodu

Skróty operujące na kodzie i linijkach:

  • Alt+L - zaznaczenie całej linii
  • Alt+, Alt+ - przeniesienie linijki w której znajduje się kursor w górę/dół.
  • Tab/⌘+] - dodaj wcięcie (wcięcie w prawo)
  • Shit+Tab/⌘+[ - usunięcie wcięcia (wycięcie w lewo)

Dodawanie postów:

  • Ctrl+Enter - dodaj post
  • ⌘+Enter - dodaj post (MacOS)