Wypełnianie tablicy liczbami od 1 do 5

0

Chcę wypełnić tablicę 5 elementową kolejnymi liczbami począwszy od 1.

Czyli powinienem uzyskać tablicę:
indeks: 0 1 2 3 4
wartość: 1 2 3 4 5

Mój kod:

int t[5];  //tablica

for (int a=0; a<5 ; )
{
	 t[a]=++a; 
}

A tablica po jego wykonaniu ma taką zawartość (są to kolejne elementy tablicy):

1118312
1
2
3
4

Dlaczego element o indeksie 0 nie jest wypełniony?

Przecież ta pętla powinna działać tak:

  1. Inicjalizacja zmiennej a i przypisanie jej wartości 0 (a=0).
  2. Sprawdza warunek ( 0<5), jest spełniony. (a=0)
  3. Wykonuje ciało pętli: t[0]=1; Na początku a=0, więc w indeksie wpisuje 0. Następnie
    zwiększa o 1 zmienną a (czyli 0+1=1) i przypisuje wartość zmiennej a, czyli a=1 i teraz ta
    nowa (zwiększona) wartość zostaje pobrana przez operator przypisana i co za tym idzie
    wpisana do zmiennej tablicy o indeksie 0, czyli t[0] = 1;
  4. Sprawdzenie warunku 1<5 - spełniony.
  5. Ciało pętli: t[1]=2 i dalej znowu będzie sprawdzał warunek, itd.

Nawet jak wpisałem taki kod (dodałem wyświetlanie zawartości zmiennej a przez operacji na tablicy i po owej operacji:

int t[5];

for (int a=0; a<5 ; )
{
	cout<<a<<"\t"; t[a]=++a; cout<<a<<endl;
}

To program wypisał (liczba z lewej to wartość przed wykonaniem operacji na tablicy t, zaś ten po prawej stronie to wartość po wykonaniu wspomnianej operacji:

0 1
1 2
2 3
3 4
4 5

A zawartość tablicy po wykonaniu tej pętli
wyświetlona kodem:

 cout<<endl<<endl;
cout<<t[0]<<endl;
cout<<t[1]<<endl;
cout<<t[2]<<endl;
cout<<t[3]<<endl;
cout<<t[4]<<endl;

jest taka:

48799848
1
2
3
4

1

Skąd wiesz, w którym momencie zostanie zwiększone a?

0

Dlaczego element o indeksie 0 nie jest wypełniony?

Ponieważ kompilator doszedł do wniosku, że efektywniej będzie najpierw skompilować prawą stronę (++a), a potem wykonać przypisanie.
To co robisz to jest undefined behaviour, więc równie dobrze mógłby sformatować komputer :P

Dlaczego nie zrobisz tego po ludzku:

for (size_t a=0; a<5; a++)
{
 t[a] = a+1; 
}
0

Zaktualizowałem post pierwszy.

0

Po pierwsze co ma oznaczać " Ponieważ kompilator doszedł do wniosku, że efektywniej będzie najpierw skompilować prawą stronę (++a), a potem wykonać przypisanie.", bo nie rozumiem.
Przecież nawet w książkach jest napisane, że najpierw zwiększa zmienną, następnie przypisuje tą zwiększoną wartość tej zmiennej i następnie dopiero tą wartość pobiera do np. przypisania czy jakiejś funkcji (w zależność od kodu). Wobec czego nie rozmiem o co tu chodzi?

A skąd wiem kiedy a jest zwiększane napisałem poniżej postu pierwszego. Przecież nawet książkach do c++ jest napisane jak działa inkrementacja i dekrementacja.

Co jest błędnego w moim kodzie?

0

Wersja C:

for (unsigned i=0, a=1; i<5; i++, a++)
   t[i] = a; 

Wersja C++

struct IncGenerator {
    int current_;
    IncGenerator (int start) : current_(start) {}
    int operator() () { return current_++; }
};

std::generate_n( t, 5, IncGenerator(1) );

Wersja C++ 11 (lambda)

static int nextValue = 1;
generate_n(t, 5, [] { return nextValue++; }); 

Wersja C++ 11 (iota, od VS 2012)

std::iota(t, t+5, 1);
0

No ale jak wpisałem taki kod (dodałem wyświetlanie zawartości zmiennej a przez operacji na tablicy i po owej operacji:

int t[5];

for (int a=0; a<5 ; )
{
	cout<<a<<"\t"; t[a]=++a; cout<<a<<endl;
}

To program wypisał (liczba z lewej to wartość przed wykonaniem operacji na tablicy t, zaś ten po prawej stronie to wartość po wykonaniu wspomnianej operacji:

0 1
1 2
2 3
3 4
4 5

To by wychodziło,że wykonywał zgodnie z moim opisem, czyli:

  1. Inicjalizacja zmiennej a i przypisanie jej wartości 0 (a=0).
  2. Sprawdza warunek ( 0<5), jest spełniony. (a=0)
  3. Wykonuje ciało pętli: t[0]=1; Na początku a=0, więc w indeksie wpisuje 0. Następnie
    zwiększa o 1 zmienną a (czyli 0+1=1) i przypisuje wartość zmiennej a, czyli a=1 i teraz ta
    nowa (zwiększona) wartość zostaje pobrana przez operator przypisana i co za tym idzie
    wpisana do zmiennej tablicy o indeksie 0, czyli t[0] = 1;
  4. Sprawdzenie warunku 1<5 - spełniony.
  5. Ciało pętli: t[1]=2 i dalej znowu będzie sprawdzał warunek, itd.

A zawartość tablicy po wykonaniu tej pętli
wyświetlona kodem:

 cout<<endl<<endl;
cout<<t[0]<<endl;
cout<<t[1]<<endl;
cout<<t[2]<<endl;
cout<<t[3]<<endl;
cout<<t[4]<<endl;

jest taka:

48799848
1
2
3
4

Dlaczego element o indeksie 0 zostało pominięty przez pętlę (i zamiast mieć wartość 1, to ma 48799848)? Co jest sprzeczne z tym z tym co program wypisał wcześniej.
Przecież przy wyświetlaniu wartości zmiennej a widać, że na początku ma ona wartość 0 (iteracja pierwsza) i taka powinna być przypisana do indeksu, a później jako wartość tablicy powinna zostać przypisana zwiększona wartość zmiennej a o 1. Później ta zwiększona wartość powinna być sprawdzona w warunku, itd. To czemu program wyświetlił co innego, a wykonał co innego?

0

Twoja linijka:

cout<<a<<"\t"; t[a]=++a; cout<<a<<endl;

wykonuje się tak:

cout << a;
cout << "\t"; 
++a;
t[a] = a; 
cout << a;
cout << endl;

Nie łącz kilku instrukcji w jedną linijkę bo zaciemniasz sobie sam kod.

0

To ja mam jedno pytanie czy kompilator w c++ wykonuje instrukcje w kolejności w jakiej zostały zapisane, czy w jakiś inny sposób?
Bo dotąd sądziłem, że kompilator w c++ wykonuje instrukcje w kolejności w jakiej mu je zapisałem. Przecież w c++ mamy wolny styl zapisu kodu.

To w jaki sposób, w jakiej kolejności instrukcje są wykonywane przez kompilator w linii? Bo myślałem,że pokolei za wyjątkiem priorytetów operatorów.

2
qws napisał(a):

To ja mam jedno pytanie czy kompilator w c++ wykonuje instrukcje w kolejności w jakiej zostały zapisane, czy w jakiś inny sposób?
Bo dotąd sądziłem, że kompilator w c++ wykonuje instrukcje w kolejności w jakiej mu je zapisałem. Przecież w c++ mamy wolny styl zapisu kodu.

Póki wynik jest taki sam kompilator może pomieszać co tylko chce w kodzie. Oczywiście musi to być też zgodne ze standardem języka. Są teź optymalizacje, które zrywają z kompatybilnością ze standardami i nawet nie muszą dawać takich samych wyników (np. ffast-math).

0

To w jaki sposób, w jakiej kolejności instrukcje są wykonywane przez kompilator c++ zapisane w jednej linii? Bo myślałem,że po kolei za wyjątkiem priorytetów operatorów.

0

@kws: sprawdz czym różni się
++i
od
i++
w wyrażeniach (w takim jak napisałeś lub innych).

2

To w jaki sposób, w jakiej kolejności instrukcje są wykonywane przez kompilator c++ zapisane w jednej linii?

Nie ma żadnego znaczenia, czy coś jest w jednej linii czy nie.

0
qws napisał(a):

Chcę wypełnić tablicę 5 elementową kolejnymi liczbami począwszy od 1.

	 t[a]=++a; 

Wpierw jest obliczana wartość po prawej stronie(tak jak zawsze), a następnie jest przypisywana do lewej strony w taki sposób:

a = a + 1;
t[a] = a;

W taki sposób pierwszemu elementowi tablicy nigdy nie zostanie nadana wartość. Nie ma w tym nic dziwnego.

0

Można tak, nie znam żadnego kompilatora który zrobi to jakoś inaczej, ale jednak to UB.

#include <iostream>
using namespace std;

int main()
  {
   int t[5];
   for(int i=0;i<5;t[i++]=i) {}
   for(int i=0;i<5;++i) cout<<i<<": "<<t[i]<<endl;
   return 0;
  }
0

Przecież różnice pomiędzy preinkrementacją a postinkrementacją widać dopiero wtedy, kiedy chce się uzyskać dostęp do wyniku w tym samym momencie co przypisać go czyli:

Jak zrobię

cout << "Coś: " << x++ << endl;

To zwróci mi się x przed postinkrementacją, ale jak (w Twoim przypadku) zrobię tak:

int x = 5;
x++
cout << "Coś: " << x << endl;

To zwróci mi x po preikrementacji, ponieważ nie dokonuje jej w tym samym momencie co wypisania wyniku.. tak?

PS: tak, pomyliło mi się (użycie inkrementacji i dekrementacji zamiast przedrostków) :D

0

http://stackoverflow.com/questions/4176328/undefined-behavior-and-sequence-points/4183735#4183735

czytajcie tylko tę odpowiedź, która się pokaże

edit: wytłumaczę;

mając wyrażenie t[x] = ++x;

t[x] i ++x posiadają sekwencję dla operatora =

ale nie wiemy, które z tych wyrażeń zostanie policzone / wzięte pod uwagę jako pierwsze; czy weźmiemy najpierw adres t[x], czy inkrementujemy x -> nie posiadając tej wiedzy możemy łatwo stwierdzić, że mamy do czynienia z ub

#include <iostream>

int main()
{
	size_t const s = 5;
	int t[s];
	int x = 0;
	while(x != s) {
		int& r = t[x];
		r = ++x;
	}

	for(auto e : t)
		std::cout << e;

	return 0;
}
0

(korekta)

_13th_Dragon napisał(a):

Można tak, nie znam żadnego kompilatora który zrobi to jakoś inaczej, ale jednak to UB.

Z ciekawości sprawdziłem jak to wygląda pod ASM:

Dla 2 różnych postaci pętli wynik jest ten sam:

    // wersja 1
  for (unsigned i=0, a=1; i<5; i++, a++)
      t[i] = a; 
   // wersja 2
   for (int i=0; i<5 ; i++)
      t[i] = i + 1;
  

Daje w wersji ASM:

		
	mov	DWORD PTR [eax], 1
	mov	DWORD PTR [eax+4], 2
	mov	DWORD PTR [eax+8], 3
	mov	DWORD PTR [eax+12], 4
	mov	DWORD PTR [eax+16], 5

Dla wersji z UB jest inaczej:

    // wersja UB
   for(int i=0;i<5;t[i++]=i) {}

i kod ASM:

	
	mov	DWORD PTR [eax], 0
	mov	DWORD PTR [eax+4], 1
	mov	DWORD PTR [eax+8], 2
	mov	DWORD PTR [eax+12], 3
	mov	DWORD PTR [eax+16], 4
  • dla wersji "t[i]=i++" jest tak samo.
  • a dla "t[i]=++i" pierwszy element jest wypełniony "czymś"

(VS 2010)

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