Skocz do zawartości
FlyingDutch

Programowanie w języku C - pytanie kontrolne

Pomocna odpowiedź

Dnia 18.08.2019 o 15:15, FlyingDutch napisał:

To może teraz kolega zada jakieś pytanie - zobaczymy, czy uda się odpowiedzieć poprawnie 😉

Długo myślałem, ale nic sensownego nie wymyśliłem 🙂

Potem przeglądając kody znalazłem taka konstrukcję:

int funkcja(a,b)
	int a; int b;

{	int c;
	return c = a*b;
}

Czy to poprawne? Z którą wersją języka C "to przyszło", jeśli poprawne? Po co tak pisać??

  • Lubię! 1

Udostępnij ten post


Link to post
Share on other sites
9 godzin temu, Zealota napisał:

Długo myślałem, ale nic sensownego nie wymyśliłem 🙂

Potem przeglądając kody znalazłem taka konstrukcję:


int funkcja(a,b)
	int a; int b;

{	int c;
	return c = a*b;
}

Czy to poprawne? Z którą wersją języka C "to przyszło", jeśli poprawne? Po co tak pisać??

Cześć,

to jest sposób definiowania funkcji sprzed ANSI C, czyli krótko mówiąc prehistoria.

Cytat

Stary sposób definiowania funkcji

Zanim powstał standard ANSI C, w liście parametrów nie podawało się typów argumentów, a jedynie ich nazwy. Również z tamtych czasów wywodzi się oznaczenie, iż puste nawiasy (w prototypie funkcji, nie w definicji) oznaczają, że funkcja przyjmuje nieokreśloną liczbę argumentów. Tego archaicznego sposobu definiowania funkcji nie należy już stosować, ale ponieważ w swojej przygodzie z językiem C Czytelnik może się na nią natknąć, a co więcej standard nadal (z powodu zgodności z wcześniejszymi wersjami) dopuszcza taką deklarację to należy tutaj o niej wspomnieć.

Patrz ten link:

https://pl.wikibooks.org/wiki/C/Funkcje

Pozdrawiam

  • Lubię! 2

Udostępnij ten post


Link to post
Share on other sites

Cześć,

szkoda, że nikt więcej nie ma pomysłu na pytanie z języka C (czy C++). To może ja podam jeszcze jedno pytanie:

Załóżmy, że mamy następujące definicje tablicy double i wskaźnika na  double:

double tabD[5] = {21.99, 178.22, 100.12, 56.77, 0.,034};

const double * const wskD = tabD;

Powiedz, która z dwóch powyższych operacji jest prawidłowa, a która nie:

wskD = &tabD[3];

*wskD = 98.79;

Odpowiedź uzasadnij 😉

Pozdrawiam

Udostępnij ten post


Link to post
Share on other sites

Obie są błędne. Pierwsza to próba przypisania adresu do stałego wskaźnika, druga próbuje nadpisać dane przez wskaźnik na stałe dane co jest oczywiście błędem

Odnośnie powyższego też mam pytanie. Czy taki sposób odniesienia się do pierwszego elementu powyższej tablicy jest prawidłowy?

double *wsk = (double*)wskD;

*(++wsk-1) = 0b01100000110000001010000000000000;

  • Pomogłeś! 1

Udostępnij ten post


Link to post
Share on other sites

Cześć,

według mnie jest prawidłowy, bo zadeklarowaliśmy nowy wskaźnik - wsk, który nie ma tych narzuconych ograniczeń co wskaźnik wskD. Czy to się zgadza? Co do twojej odpowiedzi, to jest prawidłowa, uzasadnienie jest jak najbardziej trafne 🙂

Pozdrawiam

Udostępnij ten post


Link to post
Share on other sites
(edytowany)

Oczywiście się zgadza. A czy te konstrukcje są prawidłowa? Uzasadnij odp. i wyjaśnij dlaczego definicje kontenerów różnią się.


typedef union{
    int b;
    char a;

}union_u;

typedef struct{
    int x;
    int y;

}structa_t;

typedef struct structb_t{
    union_u un;
    int x;
    struct structb_t *this;
  
}structb_t;

 

i co się stanie po usunięciu słowa kluczowego przed wskaźnikiem *this i dlaczego. :)

Edytowano przez atMegaTona
literowki
  • Lubię! 1

Udostępnij ten post


Link to post
Share on other sites

... i żeby nudno nie było.

1 Czy ten kod się skompiluje? Spróbuj odpowiedzieć nie sprawdzając, później sprawdź.

2 Co się stanie z resztą nadmiarowych argumentów funkcji?

3 Co się stanie gdy argumentów będzie za mało?

#include <stdio.h>

typedef struct structb_t{
    int x;
    int y;
    int (*foo)();
    struct structb_t *this;
  
}structb_t;

int f1(structb_t *s, int x, int y){
    s->x = x;
    s->y = y;
    return 0;
}

structb_t st = {0,0,f1,&st};

int main()
{
    st.foo(&st,1,2,123,11201, st.this);
    printf("x = %u; y = %u;\n", st.x, st.y);

    return 0;
}

 

  • Lubię! 1

Udostępnij ten post


Link to post
Share on other sites

Cześć @atMegaTona,

dzięki za pytania, muszę przyznać, że będę się musiał dłużej zastanowić - ale o to właśnie chodziło 🙂 Wczoraj już byłem zmęczony, ale dzisiaj można do tego podejść z nowymi siłami.

Pozdrawiam

Udostępnij ten post


Link to post
Share on other sites
16 godzin temu, atMegaTona napisał:

Oczywiście się zgadza. A czy te konstrukcje są prawidłowa? Uzasadnij odp. i wyjaśnij dlaczego definicje kontenerów różnią się.



typedef union{
    int b;
    char a;

}union_u;

typedef struct{
    int x;
    int y;

}structa_t;

typedef struct structb_t{
    union_u un;
    int x;
    struct structb_t *this;
  
}structb_t;

 

i co się stanie po usunięciu słowa kluczowego przed wskaźnikiem *this i dlaczego. 🙂

Cześć,

według mnie te konstrukcje są poprawne - deklarujemy tu 3 typy: union_u, structa_t, structb_t. Pierwszy z nich jest unią a dwa pozostałe strukturami. Rozumiem, ze chodzi Ci o różnicę między kontenerami: structa_t, structb_t - tak różnią się. Dlaczego ponieważ pierwszy typ ma dwie składowe:

 int x;
 int y;

a drugi trzy, z których pierwsza to unia: un (może przechowywać zarówno zmienną int jak i char), druga składowa to int a trzecia to wskaźnik na samą siebie.

Gdybyśmy w definicji typu structb_t usunęli słowo kluczowe struct w trzeciej linii:

typedef struct structb_t{
    union_u un;
    int x;
    structb_t *this;
  
}structb_t;

To będzie błąd mówiący o tym, że typ structb_t nie został zdefiniowany, bo tak naprawdę jego definicja nie została jeszcze zakończona. Natomiast nic nie stoi na przeszkodzie, żeby użyć takiej definicji poz zakończeniu definicji typu structb_t:

typedef struct{
    structb_t a;
}struct1;

Te wszystkie odpowiedzi można podsumować takim kodem programu:

#include <stdio.h>

typedef union{
    int b;
    char a;

}union_u;

typedef struct{
    int x;
    int y;

}structa_t;

typedef struct structb_t{
    union_u un;
    int x;
    struct structb_t *this;
  
}structb_t;

typedef struct{
    structb_t a;
}struct1;


int main()
{
    structa_t aa;
    structb_t bb;
    
    aa.x =3;
    aa.y = 4;
    
    bb.un.a = 'z';
    bb.x = 6;
   
	printf("Start\n\n");
	return 0;

}

 

Udostępnij ten post


Link to post
Share on other sites
(edytowany)

Częściowo masz rację, jednak to co najważniejsze w tym zestawieniu jednak uszło uwadze.

typedef struct structb_t{
    int x;
    int y;
    int (*foo)();
    struct structb_t *this;
  
}structb_t;

int f1(structb_t *s, int x, int y){
    s->x = x;
    s->y = y;
    return 0;
}

structb_t st = {0,0,f1,&st};

Wskaźnika w polu struktury do własnego typu nie da się inaczej zadeklarować niż ten powyższy, nawet jeśli wcześniej zadeklarowalibyśmy nowy typ danych. Jeśli z kolei definicja struktury nie będzie jednocześnie deklaracją ( structb_t na początku i końcu) nie będzie można do nie wskaźnika zdefiniować tak więc to chyba jedyny sposób w jaki można to prawidłowo zrobić. Druga rzecz to wskaźnik na funkcję. Zwrócić uwagę należy na fakt, że wskaźnik jest deklarowany bez żadnych argumentów i jest to jedyny sposób aby móc uzyskać możliwość wywołania funkcji z dowolną ilością argumentów bez kontroli kompilatora. Argumenty nadmiarowe po prostu ulegną zniszczeniu podczas wywoływania funkcji i nic poważnego się nie stanie poza wygenerowaniem kilku linijek assemblera więcej. Sprawa się komplikuje kiedy argumentów będzie zbyt mało ponieważ każdemu niepodanemu argumentowi zostanie przypisana przypadkowa wartość: zawartość kolejnej komórki pamięci po ostatnim prawidłowym argumencie dlatego należy unikać tego typu wypadków ponieważ kompilator nie kontroluje ani ilości ani typów podawanych argumentów przy wywołaniu funkcji przez tak zadeklarowany wskaźnik. Jednak korzyści płynące z używania tego wyjątku języka C rekompensują z nadmiarem niedogodności. Wystarczy zadbać o to aby kontekst programu określał prawidłowe jego użycie.

 Wskaźniki na własny typ to przydatna funkcjonalność, można np. dzięki nim zbudować listę połączoną co z kolei może stanowić zalążek do budowy RTOS w zestawieniu ze wskaźnikami na funkcje nawet na najmniejszych uC. Niestety w avr dynamiczna alokacja pamięci jest nieco powolna a samej pamięci jest niewiele ale przy kilku wątkach działających jednocześnie można ten sposób stosować statycznie włączając i wyłączając, przez przypisywanie i zerowanie wskaźników, poszczególne człony listy zamiast alokować i zwalniać pamięć. Zdecydowanie lepiej do takiej zabawy nadają się ARMy.

Edytowano przez atMegaTona
  • Lubię! 1

Udostępnij ten post


Link to post
Share on other sites

Cześć @atMegaTona

ale ja jeszcze nie odpowiedziałem na tą drugą część pytania(związaną z funkcjami), chciałem to zrobić dzisiaj 😉 Dzięki za dokładne wytłumaczenie - przyda się na pewno wielu osobom z forum.

Pozdrawiam

Udostępnij ten post


Link to post
Share on other sites

No, w sumie proste, ale sam się na to kiedyś naciąłem  (pisałem taki prosty parser i chciałem go zamknąć w jednej funkcji)...

Jak poprawić ten kod?

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

int main(int argc, char *argv[])
{
    extern int b(int);
    int a(int x)
    {
        return (x<5)?1:b(x/2);
    }
    int b(int x)
    {
        return (x<10)?7:a(x-1);
    }
    printf("%d\n", a(atoi(argv[1])));
}

 

Udostępnij ten post


Link to post
Share on other sites

Dołącz do dyskusji, napisz odpowiedź!

Jeśli masz już konto to zaloguj się teraz, aby opublikować wiadomość jako Ty. Możesz też napisać teraz i zarejestrować się później.
Uwaga: wgrywanie zdjęć i załączników dostępne jest po zalogowaniu!

Gość
Dołącz do dyskusji! Kliknij, aby zacząć pisać...

×   Wklejony jako tekst z formatowaniem.   Przywróć formatowanie

  Dozwolonych jest tylko 75 emoji.

×   Twój link będzie automatycznie osadzony.   Wyświetlać jako link

×   Twoja poprzednia zawartość została przywrócona.   Wyczyść edytor

×   Nie możesz wkleić zdjęć bezpośrednio. Prześlij lub wstaw obrazy z adresu URL.


×
×
  • Utwórz nowe...