funkcja przyjmujaca parametr: wskaznik do tablicy

0

Witam. Mam problem z zadaniem którego treść brzmi:
Napisz funkcję zwracającą wskaźnik do tablicy, która przyjmuje parametry :wskaźnik do tablicy oraz wskaźnik do rozmiaru. Wczytuj wielkość do tablicy dopóki będzie ona z przedziału: 1-20. Liczby wczytywane do tablicy muszą być większe od 0 i podzielne przez 3 lub większe od 0 i podzielne przez 5.

I tu problem. zrobiłem program który przyjmuje jako parametr wskaźnik do wielkości tablicy, natomiast jak upchać do tego wskaźnik do tablicy to nie mam pojęcia Proszę o rady.

#include <stdio.h>
#include <stdlib.h>

int *wczytaj(int *n)
{

   int i,u;
   int *tab;


   do
   scanf("%d",n);
   while((*n<1)||(*n>20));

   tab=(int *)malloc(*n*sizeof(int));

   for(i=0;i<*n;++i)
   {
      scanf("%d",&u);
      if ((u>0)&&(u%3==0)||((u>0)&&(u%5==0)))
         *(tab+i)=u;
      else
         *(tab+i)=0;
   }

   return tab;
}

void main(void)
{

   int *tab,n;
   tab=wczytaj(&n);
   


   system("pause");

}
0

zrobic funkcje ktora przyjmie 2 parametry?

0

To zależy co z tym wskaźnikiem będziesz chciał w funkcji robić. Jeśli będziesz chciał zmienić jego wartość (np. przypisać do niego adres innej tablicy), tak żeby zmiana była widoczna poza funkcją, to musisz przekazać wskaźnik do wskaźnika. Przykład:

int  foo(int  **blah) { *blah = (int*)malloc(10); }

Jeśli to jest wskaźnik "tylko do odczytu" w funkcji, to przekazujesz... wskaźnik:

int  foo(int  *blah) { blah[0] = bar }

Nadal możesz zmienić wartość zmiennej blah, ale ponieważ jest ona lokalna, zmiana będzie widoczna tylko w funkcji.
W pierwszym przypadku funkcję wywołujesz np. w ten sposób:

bar = foo(&tab);

W drugim:

bar = foo(tab);
0

Dziękuję za podpowiedzi i sugestie.
Źle się troszeczkę wyraziłem formułując treść posta.
Chodziło bardziej o to czy da się wsadzić

int *tab; 

do tej funkcji jako argument?
Tak zrozumiałem treść mojego zadania ale nie wiem czy to możliwe bo przecież tablica (tab) musi być wcześniej zainicjalizowana, czyż nie?

Wybaczcie jeśli piszę coś niezgodnie lub niezrozumiale, jestem początkującym "programistą" i Was również proszę o traktowanie mnie jako właśnie raczkującego w tej dziedzinie. Będzie mi o wiele łatwiej zrozumieć co chcieliście mi przekazać:)

1

Kumashiro wyłożył Ci na tacy rozwiązanie(jeżeli dobrze zrozumiałem):

  1. do funkcji podajesz wskaznik tego typu co ma być tablica i wskaźnik na wielkość tablicy.
 

...
typtablicy magicznafunkcja(typtablicy** wsk, int* wielkosc)
{};
...

typtablicy* wsk;
int wielkosc=20;

magicznafunkcja(wsk, &wielkosc);
  1. W magicznej funkcji alokujesz pamięć dla wskaźnika, losujesz liczby i zwracasz wsk. Tak to zrozumiałem ;p
0

Zrobiłem coś takiego:

#include <stdio.h>
#include <stdlib.h>

int wczytaj(int **tab, int *n)
{

   int i,u;



   do
   scanf("%d",n);
   while((*n<1)||(*n>20));

   tab=(int *)malloc(*n*sizeof(int));

   for(i=0;i<*n;++i)
   {
      scanf("%d",&u);
      if ((u>0)&&(u%3==0)||((u>0)&&(u%5==0)))
         *(tab+i)=u;
      else
         *(tab+i)=0;
   }

   return tab;
}

void main(void)
{

   int *tab,n=10;
   int *wsk;

   tab=wczytaj(wsk, &n);
   

   system("pause");

}

teraz wywala że

 wsk 

został uzyty bez inicjalizacji. jeśli ktoś może niech poprawi ten mój program, byłbym wdzięczny, może wtedy uda mi się zrozumieć.

1

Nie chciałem się odwoływać do treści zadania, gdyż nie wynika z niego dla mnie po co ma być użyty wskaźnik do tablicy w argumencie funkcji. Czy ma w nim być zapisany adres tablicy zainicjowanej w funkcji, czy też ten adres ma być zwrócony, czy jeszcze coś innego.
W każdym razie, jeśli do funkcji przekazujesz wskaźnik do tablicy, to oczywiście musi ona istnieć, a sam wskaźnik musi wskazywać na prawidłowe miejsce w pamięci. Taką tablicę tworzysz i inicjujesz poza funkcją. W przypadku, gdy do funkcji przekazujesz wskaźnik na wskaźnik, tablica nie musi istnieć w momencie wywołania funkcji. Możesz ją utworzyć w ciele funkcji. Wskaźnik w takim wypadku może mieć dowolną wartość.
W uproszczeniu wskaźnik jest zmienną, która zajmuje 4 lub 8 bajtów. Nie przechowuje on całej tablicy, a jedynie "namiary" w pamięci gdzie ta tablica się znajduje. Jeśli tablicy jeszcze nie utworzyłeś, możesz zmienną wskaźnikową przekazać (a właściwie wskaźnik do tego wskaźnika) jako miejsce, w którym funkcja zapisze namiary po jej zaalokowaniu (lub z innego źródła).
Ponieważ trochę namieszałem, to warto zapamiętać, że najczęściej jeśli masz tablicę i chcesz ją przekazać do funkcji, podajesz w argumentach tę tablicę. Jeśli funkcja w jakiś sposób tworzy tablicę, a Ty chcesz w argumencie przekazać wskaźnik pod którym ma być zapisany wynik (adres do tablicy), przekazujesz wskaźnik na wskaźnik i nie musisz mieć do niego przypisanej żadnej gotowej tablicy. Oczywiście nie wyczerpuje to wszystkich możliwych zastosowań, ale powinno ułatwić zapamiętanie co jest co.

1
filozof_ napisał(a)

Zrobiłem coś takiego:
[...]
teraz wywala że

 wsk 

został uzyty bez inicjalizacji. jeśli ktoś może niech poprawi ten mój program, byłbym wdzięczny, może wtedy uda mi się zrozumieć.

Bo przekazujesz "goły" wskaźnik. Przeczytaj jeszcze raz co napisałem wyżej i przekaż wskaźnik do wskaźnika:

tab=wczytaj(&wsk, &n);
0

Dziękuję za wyjaśnienie, teraz już jasne jak słońce :) a cały dzień nad tym siedziałem. widocznie koleś źle zadanie sformułował a ja się męczę jak baran. Wielkie ale to wielkie dzięki za pomoc Drodzy Forumowicze :)

0

No to jeszcze pamiętaj o tym, że wywołanie funkcji malloc() może się nie udać. W takim przypadku zwraca ona NULL i jakiekolwiek odwołania do tego wyniku w sposób taki, jaki Ty to robisz będą Ci wykładały program na łopatki z niewiele mówiącym komunikatem błędu. Zawsze należy sprawdzać czy wywołanie jakieś funkcji się powiodło czy nie i odpowiednio na to reagować.

0

a teraz żeby stworzyć funkcję która wypisze mi te elementy z tablicy z funkcji wczytaj jak mogę to zrobić?

0

Przekazujesz już sam wskaźnik, nie wskaźnik na wskaźnik. Do tego musisz też przekazać rozmiar, gdyż wskaźnik nie przenosi takich informacji. W funkcji iterujesz po elementach i je wypisujesz.

0

teraz mam coś takiego:

int wypisz(int *tab, int *n)
{ 
	int i=0;
	for(i=0;i<=*n;i++)
	{
		printf("%d\n", (tab+i));
	}
}
void main(void)
{

   int *tab,n=10;
   int *wsk;

   tab=wczytaj(&tab, &n);

   wypisz(&tab, &n);

   system("pause");

}

dlaczego nie pokazuje mi poprawnych wartości które wklepałem z klawiatury a takie oto dziwactwa?:

1245024
1245028
1245032
1245036

gdy w printfie dam

printf("%d\n", *(tab+i));

wyrzuca jeszcze inne jakieś dziwne liczby...

W mainie wypisując elementy w ten sposób wszystko działa a w funkcji wypisz nie:

   printf("%d\n", *(tab));
0

Program wypisuje to co sobie zażyczyłeś, adresy w pamięci.

0

No i właśnie, w czym tkwi mój błąd, jak to poprawić?

0
#include <iostream>

using namespace std;

int* wczytaj(int *wsk, int *size)
{
    int elem;
    do
    {
        cin >> *size;
    }
    while(*size>20 or *size<1);
    wsk = new int[*size];
    for(int i=0;i<*size;i++)
    {
        cin >> elem;
        if((elem>0 and elem%3==0) or (elem>0 and elem%5==0)) wsk[i] = elem;
        else wsk[i] = 0;
    }
    return wsk;
}

void wypisz(int *tab, int size)
{
    for(int i=0;i<size;i++)
    {
        cout << tab[i] << " ";
    }
}

int main()
{
    int *tab=0,rozmiar;
    tab = wczytaj(tab,&rozmiar);
    wypisz(tab,rozmiar);
    cout << endl;
    delete [] tab;
    return 0;
}

Moze tak?

0

Przecież c++ to inna bajka :(
Proszę jeśli ktoś wie jak poprawić mój kod niech to zrobi, będę wdzięczny.

0

LOL jaka inna bajka operator new == malloc a operator cin/cout to printf/scanf wskazniki przypisania wszystko dziala tak samo.

No i delete to chyba free w c

Spojrz na moj kod on dziala. c a c++ to mala roznica naprawde.

0

Jeśli chcesz iterować po wskaźnikach, to musisz użyć wyłuskania aby pobrać wartość znajdującą się pod adresem wskazywanym przez wskaźnik:

printf("%d\n", *(tab + i));

Czytelniej jednak będzie odwołać się jak do tablicy, po indeksach:

printf("%d\n", tab[i]);

BTW, tablicę zaalokowaną przez malloc() wypadałoby zwolnić funkcją free(), kiedy nie będzie już potrzebna. W Twoim przypadku system zwolni pamięć po zakończeniu programu, ale warto sobie już wyrabiać właściwe nawyki.

0
#include <stdio.h>
#include <stdlib.h>

int wczytaj(int *wsk, int *size)
{

   int elem;
   int i;
do
   scanf("%d", *size);                 //
   while(*size>20 || *size<1);

    wsk=(int*)malloc(*size*sizeof(int)); 

   for(i=0;i<*size;i++)
   {
      scanf("%d",&elem);
      if ((elem>0)&&(elem%3==0)||((elem>0)&&(elem%5==0)))
         wsk[i]=elem;
      else
         wsk[i]=0;
   }

   return wsk;
}

int wypisz(int *tab, int size)
{ 
	int i=0;
	for(i=0;i<size;i++)
	{
		printf("%d\n", tab[i]);
	}
}
int main()
{

   int *tab=0,rozmiar;
   tab=wczytaj(tab, rozmiar);

   wypisz(tab, rozmiar);

   system("pause");

}

proszę. i teraz błąd że rozmiar został użyty zanim został zainicjalizowany ! :/

Kumashiro zrobiłem przecież tak jak pisałeś i dalej nie pokazuje wartości

0

ehh jesli w deklaracji funkcji masz int *size to przekazuj wskaznij a nie w wywolaniu dajesz bezposrednio liczbe!

Utworzyles
int rozmiar; to jest int nie wskaznik na int
przekazales inta func(...,rozmiar) a miales wskaznik czyli trzeba uzyc operatora wyluskania tego adresu & tak jak ja to zrobilem u siebie. Patrz uwazniej w kod. Nie uzywalem tych "znaczkow" od tak sobie.

0

Krycho ma rację. Zapis:

int *tab=0,rozmiar;

jest równoznaczny z

int  *tab = 0;
int  rozmiar;

Jeśli chciałbyś zadeklarować dwa wskaźniki, musiałbyś to zrobić w taki sposób:

int  *tab = 0, *rozmiar;

Ale uwaga! W Twoim przypadku musisz mieć zaalokowane miejsce pod rozmiar, więc deklaracja jest prawidłowa (rozmiar ma być intem, a nie wskaźnikiem na int), lecz wywołanie funkcji powinno wyglądać tak:

tab=wczytaj(tab, &rozmiar);

Do funkcji przekazujesz w ten sposób wskaźnik do zaalokowanego miejsca w pamięci, żeby funkcja mogła sobie po nim (miejscu, nie wskaźniku) bazgrać.
Dodatkowo masz też błąd w wywołaniu scanf(). Ta funkcja oczekuje wskaźnika, a Ty przekazujesz jej inta (użyłeś wyłuskania). Prawidłowe wywołanie powinno wyglądać tak:

scanf("%d", size);

Poczytaj o wskaźnikach.

EDIT: jeśli już tak bardzo chcesz nadać wskaźnikowi wartość początkową (w tym przypadku nie musisz tego robić), proponuję użyć NULL zamiast 0. Widać wtedy, że jest to wskaźnik, a nie zmienna liczbowa.

0

ok już wszystko jasne, no prawie. co oznacza w mainie takie coś:

 int *tab=0 

ja to rozumiem jako: wskaźnikowi typu int przypisz wartość 0, ale pewnie tak nie jest. Wytłumaczcie tak na chłopski rozum.

Dziękuję bardzo za dotychczasową pomoc, dzięki Wam wiele mogłem się nauczyć w szybszym tempie niż miałbym to robić sam :)

EDIT: nie zauważyłem edycji z posta poprzedniego :)

0

Ogolnie nie musialem tego przypisania robic, ale kompilator warninga rzucal ze tworze wskaznik i go nie inicjalizuje. Przypisujac mu 0 tak naprawde przypisuje NULL, czyli wskaznik nie wskazuje na nic. Null pointer. Dopiero potem mu pokazuje na co ma wskazywac.

EDIT: jeśli już tak bardzo chcesz nadać wskaźnikowi wartość początkową (w tym przypadku nie musisz tego robić), proponuję użyć NULL zamiast 0. Widać wtedy, że jest to wskaźnik, a nie zmienna liczbowa

Akurat 0 i null sa rownowazne w typ przypadku. Ja wiem po co to zrobilem, a nie chce mi sie nigdy pisac NULL ;p Jak sprawdzam czy jakas funkcja zwrocila null bo jakis blad czy cos to tez pisze 0.

0

To dokładnie to oznacza. Wskaźnik jest numerycznym adresem, więc możesz mu nadać dowolną wartość liczbową z zakresu, który potrafi on przechować (na architekturze 32bit są to 4 bajty, na 64bit - 8 bajtów). Odwołanie się do takiej dowolnej wartości może jednak źle się skończyć, gdyż program będzie usiłował coś zrobić z wartością zapisaną pod tym adresem (trochę dramatyzuję tutaj).
Nadanie wskaźnikowi wartości 0 (lub NULL, są zwolennicy obu wartości; ja preferuję NULL gdyż to makro nie zwraca 0) jest specjalnym sygnałem oznaczającym, że wskaźnik nie wskazuje na nic. Jeśli spróbujesz użyć go do zapisania jakiejś wartości, dostaniesz błąd i program pójdzie w krzaki.
Ponieważ wskaźnik jest numeryczny, możesz wykonywać na nim operacje numeryczne, np. dodawanie, odejmowanie, mnożenie. Taka arytmetyka na wskaźnikach jest przydatna, ale podatna na błędy jeśli nie jest się uważnym. Można w ten sposób sobie "skakać" po elementach tablicy, a nawet po całej przydzielonej pamięci. Użyłeś tego pierwotnie przy wypisywaniu elementów tablicy. Rezultatem takiej operacji jest zawsze wskaźnik (dlatego Ci napisałem, że musiałbyś użyć wyłuskania). Nie polecam zabawy tym mechanizmem dopóki nie będziesz się czuł pewnie we wskaźnikach.

Na koniec: zrozumienie wskaźników w C jest trudne. Notacja jest zagmatwana, nie wiadomo po co, jak i kiedy się używa czego itp. To normalne. Wszyscy moi znajomi potrafiący coś sklecić w C (łącznie ze mną) mieli te same przejścia ze wskaźnikami: na początku mętlik i chaos, a nagle coś "zaskakuje" i ten cały bałagan staje się zrozumiały i banalnie prosty. Dobra książka (z graficznym przedstawieniem co i jak) pomoże w tym bardzo. Polecam "K&R", swego rodzaju "Biblię Programistów C", w której wszystkie inkantacje są ładnie opisane i zademonstrowane.

0
Krycho napisał(a)

Ogolnie nie musialem tego przypisania robic, ale kompilator warninga rzucal ze tworze wskaznik i go nie inicjalizuje. Przypisujac mu 0 tak naprawde przypisuje NULL, czyli wskaznik nie wskazuje na nic. Null pointer. Dopiero potem mu pokazuje na co ma wskazywac.

GCC z -Wall i -pedantic żadnych warningów na ten temat nie wysypuje. Ostrzeżenie o niezainicjowanym wskaźniku pojawia się tylko wtedy, gdy spróbujesz z takiego wskaźnika wyciągnąć jakąś wartość. Wtedy ostrzeżenie ma sens.

0

No niestety u mnie wywala ten warning jeśli nie dam NULLA. Nie wnikam, niech będzie tak jak jest.

Ostatnie moje pytanie:

Skoro przekazując wskaźnik na tablicę do funkcji , musze mieć jej rozmiar(tak jak wcześniej pisałeś) to dlaczego nie muszę mieć zainicjalizowanego parametru *size ?

0
filozof_ napisał(a)

No niestety u mnie wywala ten warning jeśli nie dam NULLA. Nie wnikam, niech będzie tak jak jest.

Bo źle przekazujesz "tablicę" przez argument. Już o tym pisaliśmy...

int *wczytaj(int **wsk, int *size)

/* ... */
tab = wczytaj(&tab, &rozmiar);

Tak, jak Ty to robisz zadziała źle i niezgodnie z treścią zadania. Do zmiennej wsk w funkcji zostanie przypisany wskaźnik do tablicy, ale po wyjściu z funkcji ta zmienna jest niszczona. Program robi "prawie" to co powinien tylko dlatego, że do zmiennej tab w main przypisujesz ten wskaźnik, ale kod jest błędny. Dlatego właśnie uważam, że demonstrowanie przekazywania wartości przez argument i wartość zwracaną w jednej funkcji wprowadza tylko zamieszanie.
Zrób eksperyment zanim poprawisz swój kod. Tak jak na początku, użyj dwóch zmiennych wskaźnikowych, np. tab i tab2. Nadaj im wartości NULL (do celów demonstracji) i wywołaj funkcję w ten sposób:

tab = wczytaj(tab2, &rozmiar);

Zgodnie z zadaniem, zmienne tab i tab2 powinny mieć po wywołaniu funkcji te same wartości. W rzeczywistości w tab będzie jakiś adres, natomiast w tab2 nadal pozostanie NULL, ponieważ zmiany widoczne były tylko w ciele funkcji.

filozof_ napisał(a)

Skoro przekazując wskaźnik na tablicę do funkcji , musze mieć jej rozmiar(tak jak wcześniej pisałeś) to dlaczego nie muszę mieć zainicjalizowanego parametru *size ?

Jeśli piszesz o funkcji wczytaj(), to wartość zostanie nadana przez funkcję. Nie musisz zatem inicjować wskaźnika przed jej wywołaniem. Nie sugeruj się tym, że kompilator na Ciebie pluł warningiem w przypadku *tab, gdyż wynika on z błędu w kodzie.

EDIT: OK, zrobię coś, czego nie powinienem robić i wkleję Ci poprawiony kod. Większość zrobiłeś sam, więc dużo Ci to nie pomoże. Poprawiłem sporo błędów, dodałem kilka rzeczy i uprościłem coś. Porównaj to ze swoim kodem.

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>


int  *wczytaj(int  **wsk, int  *size) {
    int     elem, i;

    do
        scanf("%d", size);
    while (*size > 20 || *size < 1);

    if ( (*wsk = (int*)malloc(*size * sizeof(int))) == NULL )
        return NULL;

    for (i = 0; i < *size; i++) {
        scanf("%d", &elem);
        if ( (elem > 0) && ((elem % 3 == 0) || (elem % 5 == 0)) )
            (*wsk)[i] = elem;
        else
            (*wsk)[i] = 0;
    };

    return *wsk;
}


void  wypisz(int  *tab, int  size) {
    int     i;

    for (i = 0; i < size; i++) {
        printf("%d\n", tab[i]);
    };
}


int  main(void) {
    int     *tab, *tab_ret, rozmiar;

    tab_ret = wczytaj(&tab, &rozmiar);
    assert(tab == tab_ret);
    wypisz(tab, rozmiar);

    free(tab);
    return 0;
}

EDIT2: Żeby jeszcze bardziej zamieszać - z treści zadania wnoszę, że w tablicy mają się znaleźć tylko liczby większe od zera i podzielne przez 3 oraz większe od zera i podzielne przez 5. W Twoim kodzie do tablicy lądują liczby podzielne przez 3 i 5 (większe od zera), zaś pozostałe są zastępowane wartością 0. Zastanów się nad tym.

0

Ja uzywam kompilatora MSVC i on mi rzuca warningi jesli utworze wskaznik a go nie zainicjalizuje.

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