Wskaźnik ze zmianą typu zmiennej

0

Witam.

mam jakiś dziwny problem ze wskaźnikiem. Próbuje na wszystkie sposoby lecz niestety mi nie wychodzi. Musze użyć funkcji, która jako argument przyjmuje wskaźnik na zmienna double. Niestety wskaźnik który mam do dyspozycji wskazuje na zmienna typu uint16_t. Potrzebuje wiec przekonwertować typ zmiennej i przekazać ja do funkcji. Napisałem przykładowy kod. Niestety zwraca 0.

uint16_t punkt1;
uint16_t *wskaznik;

double oblicz(double *liczba){
    return *liczba;
}

int main()
{
    wskaznik = &punkt1;
    punkt1 = 123;
    double wynik = oblicz((double*) wskaznik);
    printf("> %f", wynik);

    return 0;
}

Podpowiedzcie proszę gdzie robię błąd.

Od razu dodam, ze ani funkcji ani zmiennej nie mogę zmienić.

3

Z tego co mi wiadomo to konwersja wskaźnika nie konwertuje wartości na którą wskazuje ten wskaźnik.

3

To mi wygląda na problem XY.
Nie pisz jak coś próbujesz osiągnąć, ale napisz co chcesz osiągnąć. Co ten kod ma zrobić? Co użytkownik ma zobaczyć?

0

@MarekR22: sprobuje wyjasnic. Najpierw najwazniejsze. To program na mikroprocesor.
Mam zadeklarowana strukture ktorej uzywaja rozne urzadzenia. Potrzebuje pobrac z tej struktury zmienna uint16_t (poprzez wskaznik) i przekazac go jako zmienna double do funkcji regulatora PID. Musi sie to odbyc w jak najmniejszej ilosci krokow assemblerowych.

2

Już lepiej, ale nie rozumiem czemu chcesz angażować w obliczania wskaźnik. Standardowa konwersja int -> double powinna być ok (przynajmniej według obecnego opisu). Jednak bardziej stawiam na to, że nadal pominąłeś ważne szczegóły.

uint16_t punkt1;
uint16_t *wskaznik;

double oblicz(double liczba){
    // jakieś obliczania .....
    return liczba;
}


int main()
{
    wskaznik = &punkt1;
    punkt1 = 123;
    double wynik = oblicz(*wskaznik);
    printf("> %lf", wynik); // naprawiona literówka w napisie formatujacym

    return 0;
}
4

Jeśli naprawdę potrzebujesz wskaźnika to to działa, ale możesz spróbować uprościć:

int punkt1;
int *wskaznik;

double oblicz(double *liczba){
    return *liczba;
}

double wrapped(int * pointer) {
    double value = (double)(*pointer);
    return oblicz(&value);
}


int main()
{
    wskaznik = &punkt1;
    punkt1 = 123;
    
    double wynik = wrapped(wskaznik);
    printf("> %f", wynik);

    return 0;
}

zamieniłem uint16_t na int żeby odpalało się w kompilatorze online

0

@MarekR22: Ta funkcja oblicz ktora tu napisalem odwzorowuje funkcje inicjalizujaca obliczanie regulatora PID. To zewnetrzna biblioteka ktora jako argument przyjmuje wskaznik, nie zmienna. Po inicjalizacji tego regulatora nastepuje cykliczna regulacja i ona korzysta z aktualnej zmiennej podanej tym wskaznikiem. Struktury rowniez nie moge zmienic poniewaz obliczanie liczb zmiennoprzecinkowych trwa zbyt dlugo, a ja nie mam na to czasu.

@KamilAdam: sprawdze to ale chyba zajmie to sporo czasu. Nie chcialem tworzyc dodatkowych zmiennych. Czy nie powinno dac sie przekazac tego w ten sposob?

int * pointer

oblicz((double)(*pointer));
0
Marcin Kosela napisał(a):

@MarekR22: Ta funkcja oblicz ktora tu napisalem odwzorowuje funkcje inicjalizujaca obliczanie regulatora PID. To zewnetrzna biblioteka ktora jako argument przyjmuje wskaznik, nie zmienna. Po inicjalizacji tego regulatora nastepuje cykliczna regulacja i ona korzysta z aktualnej zmiennej podanej tym wskaznikiem. Struktury rowniez nie moge zmienic poniewaz obliczanie liczb zmiennoprzecinkowych trwa zbyt dlugo, a ja nie mam na to czasu.

No to pominąłeś bardzo dużo istotnych szczegółów. Coś mi się wydaje, że bez przeczytania dokumentacji tej biblioteki i twojego urządzenia trudno będzie ci pomóc.

Pamiętaj, że nie siedzimy w twojej głowie nie znamy twojego projektu. przykład PID otwiera mi w głowie Process ID co w tym przypadku jest bezsensu.

0

@MarekR22: nie trzeba wcale siedziec w mojej glowie. Zagadnienie nie jest chyba tak skomplikowane zebym musial przeprowadzac Was przez meandry calej biblioteki i calego projektu. Na poczatek podalem zalozenia i problem do rozwiazania. Wydaje mi sie, ze jest wszystko co potrzebne do znalezienia rozywiazania. Nie ma tu duzego pola do popisu. Wystarczy jedno proste rozwiazanie.
To ktore podal @KamilAdam zadzala ale chcialem pomianac tworzenie kolejnej zmiennej ze wzgledu na duza czestotliwosc jej aktualizacji. To bedzie kosztowalo sporo czasu.

0

Ja rowniez nie rozumiem o co Ci chodzi, jezeli chcesz po prostu wyjac wartosc, na ktora wskazuje int* i scastowac na double w jednej linijce to mozesz to zrobic tak:

int main()
{
    int val = 10;
    int* pointer = &val;
    double temp = (double)(*pointer);
    printf("Hello World %f", temp);

    return 0;
}
0

@Seken: W pierwszym poscie napisalem "musze uzyc funkcji" i wystarczy jak uzyjemy funkcji oblicz ktora umiescilem w kodzie (tej funkcji nie mozna zmienic). W tym przykladzie utworztyles kolejna zmienna, tak jak kolega @KamilAdam, a to chcialbym pominac.

1

No, ale to sam nie umiesz skleic 2 malych fragmentow kodu? Ja Ci bardzo chetnie pomoge, ale musisz sie wykazac jakakolwiek inicjatywa i powiedziec czego nie rozumiesz, a nie oczekiwac gotowca.

0

@Seken: jakiej inicjatywy ode mnie oczekujesz? Zapytalem o to jak przekazac wskaznik ze zmiana typu zmiennej. Napisalem caly kod reprezentujacy to zagadnienie. Co mialbym sam wiecej tutaj zrobic? Co masz na mysli, w tym wypadku, piszac gotowiec? 2 linie kodu? Jesli tak to wlasnie tego oczekuje. Ewentualnie informacji gdzie znajde odpowiedz na moje pytanie.
Mysle ze to wcale nie jest to gotowiec. Gotowiec to bardziej cala funkcja czy czesc programu. O gotowcu moznaby mowic gdybym wrzucil tu caly projekt i zapytal jak to zrobic.

0

Mozliwe, ze my sie po prostu nie rozumiemy, jezeli tak to przepraszam.
Chodzi Ci o to, ze @KamilAdam robi tam po drodze double value = (double)(*pointer); ? Nie da sie tego obejsc, poniewaz jak chcesz zwrocic adres pamieci rvalue? Innymi slowy nie mozesz miec wskaznika na wartosc np. 11, tylko na zmienna ktora przechowuje wartosc 11.
Teoretycznie gdyby to byl C++ to daloby sie to zalatwic za sprawa rvalue reference, ale w C nie slyszalem o niczym takim.

2
Marcin Kosela napisał(a):

Czy nie powinno dac sie przekazac tego w ten sposob?

int * pointer

oblicz((double)(*pointer));

@Marcin Kosela: da się, nawet ten cast będzie zbędny, bo nastąpi automatyczna konwersja. Ale zakładając, że nie masz kompilatora z 1980, to co robisz jest kompletnie bez sensu. Dowolny optymalizujący kompilator z tego tysiąclecia wyeliminuje taką zmienną, a z tego co wiem większość kompilatorów na mikroprocki bazuje na gcc 4 lub nowszym, co pod to podchodzi.

Porównaj sobie wygenerowany kod funkcji i znajdź różnice:
https://godbolt.org/z/b5vhsKcPn

void sink(double);

void func1(int* ptr)
{
    sink(*ptr);
}

void func2(int* ptr)
{
    double var = *ptr;
    sink(var);
}
func1(int*):
        pxor    xmm0, xmm0
        cvtsi2sd        xmm0, DWORD PTR [rdi]
        jmp     sink(double)
func2(int*):
        pxor    xmm0, xmm0
        cvtsi2sd        xmm0, DWORD PTR [rdi]
        jmp     sink(double)
0

@Seken: Nie ma problemu.
Tak. Wlasnie o to mi chodzi. W tym wypadku odswiezenie tej dodatkowej zmiennej 10000 razy na sekunde robi roznice.

@kq: przyjrze sie temu, skompiluje i przemysle to jeszcze raz na spokojnie, bo juz mi sie wszystko pomieszalo. Wydaje mi sie, ze u mnie to nie dzalalalo

1
Marcin Kosela napisał(a):

To ktore podal @KamilAdam zadzala ale chcialem pomianac tworzenie kolejnej zmiennej ze wzgledu na duza czestotliwosc jej aktualizacji. To bedzie kosztowalo sporo czasu.

Ciężko komercyjne prace robić ze znacznymi lukami w znajomości języka.

TYM BARDZIEJ się dziwię, że optymalizujesz w assemblerze.
Nie wyobrażam sobie inaczej dziubać w assemblerze, niż mieć "o trzeciej w nocy" perfekyjne rozumienie czym jest wskaźnik, CZYM NIE JEST itd.
Powtórzę, dedukując imiennie do Ciebie: rozumieć, czym wskaźnik NIE JEST.

Twój temat, tak jak go stawiasz, jest mrzonką. NIE MA takiego rzutowania(whatwver) wskaźnika na niekompatybilnych typach, które by nie polegało na rzutowaniu (wskazywanej) wartości. Natomiast sam fakt istnienia zmiennej w C kosztuje bardzo mało / nic.

BTW jak mamy 10tys aktualizacji na sekundę, jak piszesz, to ja wątpię, czy te dane są konsumowane w tym tempie, czy są przepisywane "na wiwat" bo się nie chciało komuś przyjrzeć procesowi

0

@ZrobieDobrze: Człowieku... Skąd tyle jadu?! Dziwne jest to, że musze się tutaj opowiadać o sobie i udowadniać co sobą reprezentuję. Jeśli kolega jest taki wnikliwy to proponuję zajrzeć choćby na mój profil facebookowy, bo przedstawiam się tu imieniem i nazwiskiem. Jak już kolega dojdzie do tego, że ani nie jestem zawodowym programistą, ani nie tworzę komercyjnych projektów to może się kolega zastanowi nad podejściem do ludzi na forum. Ja nikogo do niczego nie zmuszam. Jeśli ktoś nie chce, nie musi ani czytać ani odpowiadać na moje posty.
Uważam, że włożyłem pracę i trud w znalezienie rozwiązania i po tym podjąłem próbę znalezienia pomocy tutaj na forum. Nie pierwszy raz spotykam osobę, która tworzy piękne opowieści i udowadnia innym, że g**no się znają ale sama rozwiązania nie potrafi znaleźć, więc nie robi mi różnicy co tu napisałeś. Twój post nic tu nie wnosi. Zaśmiecasz forum.

Odpowiem na zarzuty ze względu na szacunek do innych z tego forum:

  • nie "dziubie" w assemblerze, bo go po prostu nie znam
  • rozumiem czym jest wskaźnik, wiem też czym mógłby nie być albo być. Czy to jakieś dywagacje egzystencjalne?
  • tematu nie stawiam. Szukam rozwiazania
  • fakt istnienia zmiennej DOUBLE kosztuje 4 bajty danych
  • to powątpiewanie w 10k aktualizacji czym jest spowodowane? Dla obliczenia mocy dla 3xPWM dla silnika krecącego sie z predkością 100 obr na minutę może być, na przykład, spowodowane...
2

fakt istnienia zmiennej kosztuje 4 bajty danych

No właśnie niekoniecznie, jak widać na przykładzie, który zamieściłem wyżej.

0

@kq: poprawiłem

1

Przecież w moim przykładzie mam zmienną double, która znika w asemblerze, bo kompilator ją optymalizuje.

0

@kq: ja chyba czegoś nie rozumiem. U Ciebie zmienna jest tworzona okresowo w funkcji dlatego kompilator pomija ją, przepisując od razu do wyniku ale u mnie to tak nie działa.

Ja mam strukturę:

struct BLDCMotorSt{
	uint32_t pwmU;
	uint32_t pwmV;
	uint32_t pwmW;
	int16_t fieldPosition;  //ustawiona pozycja pola magnetycznego dla kompletnego obrotu - 4095 pozycji
	uint16_t expectedPosition; // oczekiwana pozycja silnika
	uint8_t direction; // kierunek 0-L 1-R
	double distance; // dystans od punktu oczekiwanego do aktualnego
	float powerAcceleration;
	int16_t expectedPower;
	int16_t actualPower;
};

Ta struktura przechowuje aktualny stan urządzenia.

Teraz do sterowania actualPower chcę użyć tej biblioteki: link

Więc wychodzi mi coś w tym stylu:

main{
/...
PID(&TPID, &bldcMotor.distance, &bldcMotor.actualPower, &TempSetpoint, 2, 5, 1, _PID_P_ON_E, _PID_CD_DIRECT);

PID_SetMode(&TPID, _PID_MODE_AUTOMATIC);
PID_SetSampleTime(&TPID, 500);
PID_SetOutputLimits(&TPID, 1, 30);
.../
}

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
	if (htim->Instance == TIM2) {
		if (initPhase == 0) {
			bldcHaptickMode();
		}
	}

	if (htim->Instance == TIM3) {
		bldcEncoder.status = as5600_ReadPosition(&hi2c2, &bldcEncoder.angle);
		bldcEncoder.magnetStatus = as5600_StatusMagnet(&hi2c2);

		if (initPhase == 0) {
			bldcMotor.fieldPosition = bldcEncoder.calculatedAngle;
			bldcCalc();
			PID_Compute(&TPID);  //tutaj nastepuje okresowa aktualizacja PID
		}
	}
}

problem jest z inicjalizacją PID ponieważ w bibliotece zmienne są typu double. Nie chcę przerabiać biblioteki, bo nie jest to moja praca i szanuje to co ktoś zobił, ale również dlatego, że to trochę pracy. Myślałem, że będzie to proste i ogarnę w miarę szybko.
Widzę, że chyba jednak tak nie będzie. Może spróbuję dostać się bezpośrednio do pamięci

2

No to masz następujące opcje:

  1. Modyfikacja po swojej stronie, tak abyś używał double tam gdzie biblioteka chce double
  2. Modyfikacja biblioteki
  3. Konwersja (nie rzutowanie!) na double tam, gdzie ma być double. int->double dzieje się automatycznie, więc de facto przekazujesz wartość całkowitą i się nie przejmujesz. Jeśli biblioteka wymaga wskaźnika na double to nie obędzie się bez utworzenia zmiennej (może być lokalna), na którą będziesz wskazywał. Albo opcja 1. lub 2.
0

@kq: dziękuję za wyczerpującą odpowiedź i zaangażowanie. Spróbuję ustalić co będzię szybsze i taką opcję wybiorę

0

Nie rozumiem tutaj jeszcze jednej kwestii - jak masz zmienna typu int w pamięci, to zakładając że u ciebie jest to tak samo reprezentowane jak na x86_64, nie da się przekazać wskaźnika na double na nic co by miało odpowiednią wartość, ze względu na to, że reprezentacja w pamięci liczb całkowitych i zmiennoprzecinkowych jest zupełnie inna.

0

@enedil: właśnie tu leży problem. Oczywiście reprezentacja tych typów w pamięci jest odmienna. Chodziło o przekonwertowanie tej wartości. Myślałem, że możliwe jest obejście tej konwersji zmuszając kompilator do "poradzenia" sobie z tym

4
Marcin Kosela napisał(a):

@enedil: właśnie tu leży problem. Oczywiście reprezentacja tych typów w pamięci jest odmienna. Chodziło o przekonwertowanie tej wartości. Myślałem, że możliwe jest obejście tej konwersji zmuszając kompilator do "poradzenia" sobie z tym

Nawet jeśli wyobrazimy sobie język o wysokim pozmie abstrakcji, gdzie by to było możliwe i syntax języka to potrafi zapisać - sądzisz że na poziomie kodu maszynowego to jest za darmo? Przecież "samo" się nie skonwertuje. Typy nie są żadną miarą kompatybilne. Powstanie zmienna robocza o takim samym koszcie jak zaprogramowana ręcznie itd ...

0

Strict aliasing.

Rób unie. Za coś takiego jest ula w łeb.

  • to się chyba nie zmieści ale złamanie strict aliasing jest gorsze.

  • No raczej na pewno się nie zmieści ;-)

Szczęśliwie odczyt to segfault.

Pzdr! doczytaj o o pierwszym błędzie bo tego kompilator nigdy nie zawarnuje. Szukanie takiego błędu to katorga

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