hmm ale czemu musi to byc w kanapce? przeciez duzo latwiej zrobic to w deathmsg
Witamy w Nieoficjalnym polskim support'cie AMX Mod X
Witamy w Nieoficjalnym polskim support'cie AMX Mod X, jak w większości społeczności internetowych musisz się zarejestrować aby móc odpowiadać lub zakładać nowe tematy, ale nie bój się to jest prosty proces w którym wymagamy minimalnych informacji.
|
ogieR8
Rejestracja: 25.02.2011Aktualnie: Nieaktywny
Poza forum Ostatnio: 29.03.2017 21:42





Statystyki
- Grupa: Power User
- Całość postów: 641
- Odwiedzin: 11 534
- Tytuł: Wszechmogący
- Wiek: 30 lat
- Urodziny: Maj 15, 1995
-
Imię
Mariusz
-
Płeć
Mężczyzna
-
Lokalizacja
Ruda Śląska
Kontakt
Narzędzia użytkownika
Ostatnio byli
#737894 [HamSandwich] Ham_Killed a zabicie z granata
Napisane przez rzeznik9871
w 23.03.2017 20:38
#737881 [HamSandwich] Ham_Killed a zabicie z granata
Napisane przez _McHappy
w 23.03.2017 11:09
Z fakemetą (ze wspomnianego postu z allied'ów):
#include <amxmodx> #include <fakemeta> #include <hamsandwich> #include <fun> #define m_bitsDamageType 76 #define DMG_GRENADE (1<<24) #define DMG_BULLET (1<<1) public plugin_init() { register_plugin("HamKilled Test","1.0","..."); RegisterHam(Ham_Killed, "player", "HamKilled"); } public HamKilled(iVictim, iAttacker, shouldgib) { give_item(iAttacker, "weapon_hegrenade"); //W celu szybkich testow if ( get_pdata_int( iVictim , m_bitsDamageType ) & DMG_BULLET ) return HAM_IGNORED; if ( get_pdata_int( iVictim , m_bitsDamageType ) & DMG_GRENADE ) client_print(0, 3, "Zabicie z granata..."); return HAM_IGNORED; }
Jak wspomniał Bugsy, metoda powyżej działa, ale jak też wspomniał ( ) Connor, bity typów obrażeń się sumują.
Podczas testów, zauważyłem, iż sumują się tylko w sytuacji gdy następuje zabicie z broni palnej (gracz ma ustawione bity obrażeń DMG_GRENADE | DMG_BULLET).
W sytuacji śmierci od obrażeń granata, wszystko jest ustawione prawidłowo.
Nie wiem do końca, jak jest z innymi bitami obrażeń, musiałbyś sprawdzić, żeby uniknąć niespodzianek.
Potestuj powyższy kod i zobacz, czy o to Ci chodziło.
#684889 Stałe kompilatora pawn
Napisane przez DarkGL
w 26.01.2015 17:02
Lista stałych kompilatora pawn które możemy używać w naszym kodzie
- __DATE__ - aktualna data
- __TIME__ - aktualny czas
- cellbits - "wielkość" pojedynczej komórki pamięci
- cellmax - maksymalna wartość którą może przechowywać komórka pamięci
- cellmin - minimalna wartość którą może przechowywać komórka pamięci
- __Pawn - wersja interpretatora pawn
- debug - poziom debugowania
- __LINE__ - aktualna linia pliku
- __BINARY_PATH__ - ścieżka do wykonywanego pliku
- __BINARY_NAME__ - nazwa wykonywanego pliku
server_print("__DATE__: %s", __DATE__); server_print("__TIME__: %s", __TIME__); server_print("cellbits: %d", cellbits); server_print("cellmax: %d", cellmax); server_print("cellmin: %d", cellmin); server_print("__Pawn: %d", __Pawn); server_print("debug: %d", debug);

#684433 SHM 1.2, rejestracja bohatera w kilku językach
Napisane przez Rivit
w 23.01.2015 19:06
W silniku zmienione na:
public _sh_set_hero_info() { new heroIndex = get_param(1) if ( heroIndex < 0 || heroIndex >= gSuperHeroCount ) return new pPower[50], pHelp[128] for(new i = 1; i <= 32; i++) { if(gJezykGracza[i]) { get_string(4, pPower, charsmax(pPower)) get_string(5, pHelp, charsmax(pHelp)) } else { get_string(2, pPower, charsmax(pPower)) get_string(3, pHelp, charsmax(pHelp)) } } debugMsg(0, 3, "Stworzenie bohatera -> ID: %d, NAME: %s, HELP: %s", heroIndex, pPower, pHelp) copy(gSuperHeros[heroIndex][superpower], 49, pPower) copy(gSuperHeros[heroIndex][help], 127, pHelp) }W klasie:
sh_set_hero_info(gHeroID, "Master of Magnetism", "Strip and Get a players guns when they shoot you", "Mistrz magnetyzmu", "Przeciwnik traci bron, a Ty ja dostaniesz.. kiedy w Ciebie strzeli")Niestety, obydwa pliki zaaktualizowane na serwerze jednak nie działa. Zawsze 'nazwa' jest po ang, a opis się chyba czasem nie pojawia jak dobrze widzę, jeśli język = EN - działa zawsze. Nie bawiłem się tak nigdy, pewnie zrobiłem znając życie głupie błędy, jakaś rada ?
Nie rób pętli tam, przecież gdy wykonuje sie plugin_init (czyli rejestracja natywu) to nikt nie ma jeszcze polskiego....
Tu zrób tylko pobieranie angielskiego i polskiego (czyli będzie 5 parametrów)
Potem gdzieś w kodzie (tam gdzie wyświetlasz np. opis), dodajesz warunek: jeśli polski to wyświetl mu tekst z tablicy z napisami polskimi, jeśli nie to angielskie
Rozumiesz?
#684416 SHM 1.2, rejestracja bohatera w kilku językach
Napisane przez Rivit
w 23.01.2015 16:31
#683281 [ROZWIĄZANE] Przekierowanie gracza na inny serwer za pomoca "stock cmdExe...
Napisane przez Droso
w 17.01.2015 12:08
#677781 [ROZWIĄZANE] Usuniecie celownika,message_begin - HideWeapon
Napisane przez grankee
w 26.12.2014 15:36
#680698 [ROZWIĄZANE] brak możliwości usuwania rekordów w MYSQL
Napisane przez GwynBleidD
w 04.01.2015 16:04
#680696 Struktury
Napisane przez GwynBleidD
w 04.01.2015 16:02
#680547 [ROZWIĄZANE] brak możliwości usuwania rekordów w MYSQL
Napisane przez GwynBleidD
w 04.01.2015 07:19
#680133 Event (Ham_TakeDamage), warunek nie spełnia się na 'botach ?'
Napisane przez Rivit
w 02.01.2015 21:11
#677080 Odrzucenie ciała po strzale, w tym od lotu kuli
Napisane przez grankee
w 24.12.2014 03:06
Postaram się coś naskrobać tożto tylko matematyka.. Nie mam jak sprawdzić więc odpisz jakby co.
//Edit
Ok po kilku chwilach rozkminy obczaiłem, że twierdzenie pitagorasa działa w dowolnej ilości wymiarów:D Pewnie mówili w szkole, ale byłem w kafejce i grałem w cs'a i teraz sam muszę takie wnioski wyciągać
odrzut(attacker,victim,Float:value) { new Float:vOrigin[3],Float:aOrigin[3]; new Float:Vec[3]; pev(attacker,pev_origin,aOrigin); pev(victim,pev_origin,vOrigin); for(new i=0;i<3;i++) Vec[i]=(vOrigin[i]-aOrigin[i]) new Float:odleglosc odleglosc=floatsqroot(Vec[0]*Vec[0]+Vec[1]*Vec[1]+Vec[2]*Vec[2]) new Float:przelicznik=value/odleglosc for(new i=0;i<3;i++) Vec[i]*=przelicznik set_pev(victim,pev_velocity,Vec); }
Spróbuj różne wartości i zobacz czy będzie za każdym razem odrzucało z tą samą siłą przy jednej wartości, ale zmieniaj odległość od atakowanego gracza. W ogóle porób testy. Najpierw skup się czy dobrze działa na ziemi, a dopiero dalej testuj w powietrzu.
#677028 Odrzucenie ciała po strzale, w tym od lotu kuli
Napisane przez grankee
w 23.12.2014 23:17
new flags=pev(id, pev_flags) if(flags&FL_ONGROUND || flags&FL_PARTIALGROUND)//ew. FL_PARTIALONGROUND jak tego kompilator nie przepusci-nie pamiętam jak jest prawidłowo. { //kod gdy stoi na ziemi }
tak sprawdzisz czy jest na ziemi, velocity sprawdzanie nie jest do końca dobre, bo moze podskoczyc na schodku itp i nie jest w stanie lotu, a wykryjesz ze jest, ponadto w momencie zmiany velocity czyli w najwyzszym punkcie przez moment jest 0.00
Ja bym tu się czepił innej rzeczy niż tego w jakim stanie jest victim, to może i jest problem, ale żeby go rozwiązać najpierw trzeba rozwiązać inną sprawę.
Załóżmy, że odległość atakera od ofiary to 10 we wszystkich plaszczyznach, wtedy nadamy mu predkosc 10*15=150 w kierunku przeciwnym do atakera, ok, ale jesli victim stoi w odleglosci 200 od atakera to nadamy mu velocity 200*15 czyli 3000, spora rozbieżność co nie?
Moim zdaniem trzeba tu raczej zastosować proporcje. Trzeba tylko pomysłu jak przelozyc 3 wymiarowe velocity na predkosc, potem ustalic z jaka predkoscia ma odrzucac i nadac proporcjonalnie odpowiednie velocity w trzech kierunkach. Chory jestem i nie mam pomyslu jak to ugryzc.
#676965 Odrzucenie ciała po strzale, w tym od lotu kuli
Napisane przez GwynBleidD
w 23.12.2014 18:56
1. W zależności od tego, w jakim stanie jest victim, zwiększaj lub zmniejszaj mnożnik. Np gdy jest w powietrzu, myślę że mnożnik ok 2-3 będzie w sam raz. Gdy kuca również możesz zmniejszyć mnożnik, będzie bardziej realnie
2. Jeśli victim znajdzie się w powietrzu tuż po odrzuceniu go, wyhamuj go lekko. Do tego chyba będziesz musiał określić mniej więcej jak długo trwa odrzut i w thinku sprawdzać, czy minęło X czasu od odrzucenia i gracz nie zmienił swojego stanu.
#675787 PAWN Pre-Processor Część 1
Napisane przez DarkGL
w 19.12.2014 12:11
Jest to pierwsza część cyklu tutoriali na temat preprocesora autorstwa Y_Less przetłumaczona na język polski
Źródło http://forum.sa-mp.c...5175#post785175
http://darkgl.amxx.p...cessor-czesc-1/
Sam tutorial dotyczy preprocesora obecnego w wersji pawn'a dla sa:mp jednak wiele rzeczy jest wspólnych , niektóre niestety działają tylko w sa:mp ale postanowiłem zostawić ich opis jaką ciekawostkę. Wszelkie uwagi co do tlumaczenia mile widziane. Podczas tłumaczenia dodawałem / zmieniałem rzeczy od siebie.
Za wszystkie błędy lub nieścisłości przepraszam czasami ciężko było przenieść znaczenie zdań z języka angielskiego na polski.
Zawartość
Część 1 - Obejmuje wprowadzenie do preprocesora oraz kilka ważnych rzeczy przydatnych podczas pisania makr.
Część 2 - Wyjaśnienie dokładnie czego szuka kompilator oraz typowych zastosowań makr.
Część 3 - Opis innych dostępnych dyrektyw (oprócz"#define") oraz spojrzenie na definicje bez wartości podmiany.
Część 4 - Używanie stringów w preprocesorze.
Część 5 - Alternatywy dla preprocesora, wiele symboli i rekurencja.
Część 6 - Problemy z makrami oraz spacje.
Podstawowa podmiana
Na początek proste makra i jeszcze prostsze definicje. Idealny początek do wprowadzenia jak działa preprocesor.
Definicje
Definicja poniżej składa się tylko z tekstu do podmiany i tekstu na który podmieniasz.
#define MAX_PLAYERS 500
Klasyka - definicja określa ilośc graczy na Twoim serwerze.Idealny przykład jak działają definicje. Preprocesor jak sama nazwa wskazuje wykonuje się przed głownym procesem ( kompilatorem ). Kompilator bierze napisany kod i konwertuje go do pliku AMXX, preprocesor generuje napisany kod. Przykład:
printf("%d", MAX_PLAYERS);
Preprocesor podczas wykonywania przekonwertuje ten kod do:
printf("%d", 500);
Jest to kod który zostanie przekazany głownemu kompilatorowi i to ten kod zostanie przekonwertowany do pliku wynikowego ( AMXX ). Jest to po prostu podmiana tekstu. Wszystkie makra są w tej samej formie:
#define <szukany text><spacja/spacje><text podmiany>
Warto zauważyć że spacja jest ważna - pierwsza spacja oznacza koniec stringu który będzie szukany, wszystko za spacją jest traktowane jako string na który preprocesor będzie podmieniał znalezione stringi! W przykładzie wyżej szukany string to "MAX_PLAYERS" a string podmiany to "500".
Makra
Makro to coś w rodzaju funkcji - posiada parametry. Nazwy parametrów zaczynają się od "%0" do "%9" ( "%0" , "%1" , "%2" itp. itd. ) - nie można im nadać własnych nazw. Funkcja zwracająca maksymalną ilośc graczy pomnożoną przez liczbę wyglądała by tak:
MaxPlayersTimesNumber(number) { return MAX_PLAYERS * number; // Pamiętaj że "MAX_PLAYERS" jest definicją więc kompilator skompiluje te wyrażenie jako: // return (500) * number; }
Makro robiące to samo wyglądało by tak:
#define MAX_PLAYERS_TIMES_NUMBER(%0) MAX_PLAYERS * %0
Tym razem szukanym stringiem jest "MAX_PLAYERS_TIMES_NUMBER(%0)" a stringiem podmiany jest "MAX_PLAYERS * %0". "%0" to specjalne wyrażenie - nie oznacza szukaj "%0", oznacza szukaj czegokolwiek pomiędzy dwoma nawiasami . "%0" otrzymują tą samą wartość która była pomiędzy nawiasami w stringu który był podmieniany.
Przykład:
#define MAX_PLAYERS 500 #define MAX_PLAYERS_TIMES_NUMBER(%0) MAX_PLAYERS * %0 printf("%d", MAX_PLAYERS_TIMES_NUMBER(7));
Po wykonaniu preprocesora ( podamiana "%0" na "7" ) otrzymujemy:
#define MAX_PLAYERS 500 printf("%d", MAX_PLAYERS * 7);
"MAX_PLAYERS" jest dodatkowo makrem więc otrzymujemy:
printf("%d", 500 * 7);
Warto zauwayżyć że kompilator jest "inteligentny" - jeśli widzi takie wyrażenie jak to tutaj gdzie nie mamy żadnych zmiennych , wyliczy sobie wartość , więc kod który finalnie dostajemy do kompilacji wygląda tak ( kompilator nie umie formatować stringów ):
printf("%d", 3500);
Można by to też zrobić tak:
#define MAX_PLAYERS 500 #define MAX_PLAYERS_TIMES_NUMBER(%0) MAX_PLAYERS * %0 new value = 7; printf("%d", MAX_PLAYERS_TIMES_NUMBER(value));
Po wykonaniu preprocesora ( podamiana "%0" na "value" ) otrzymujemy:
#define MAX_PLAYERS 500 new value = 7; printf("%d", MAX_PLAYERS * value);
"MAX_PLAYERS" jest makrem więc otrzymujemy:
new value = 7; printf("%d", 500 * value);
Ponieważ te wyrażenie używa zmiennej kompilator nie umie go wyliczyć. Więc jest to finalny kod który zostaje skompilowany.
Dlaczego ?
Więc dlaczego używać makr zamiast funkcji ( lub dlaczego używać funkcji zamiast makr )? Makra podmieniają tekst - więc wszedzie gdzie umieścisz makro tam zostanie dodany twój tekst. Jeśli masz makro w kodzie użyte 100 razy , kod zostanie wygenerowany 100 razy. Z drugiej strony jeśli masz 100 wywołań funkcji w swoim kodzie , kod zostanie dodany tylko raz mimo 100 wywołań. Funkcje są prawdopodbnie bardziej użyteczne jeśli masz dużo kodu - duże bloki kodu występujące 100 razy utworzą bardzo duży plik AMXX ! Makra są raczej używane przy małej ilości kodu - wywołanie funkcji zajmuje pamieć i czas procesora więc jeśli masz mały blok kodu nie opłaca się wywoływać funkcji , ale to nie jest zasadą ! Jeśli użyłbyś funkcji zamiast makra powyżej , skompilowany kod wygląał by tak:
MaxPlayersTimesNumber(number) { return (500) * number; }
Przykład 1:
printf("%d", MaxPlayersTimesNumber(7));
Przykład 2:
new value = 7; printf("%d", MaxPlayersTimesNumber(value));
W obu przypadkach kompilator nie wie jak zoptymalizować kod.
Konwencja
Jedną z rzeczy które mogłeś zauważyć czytając ten poradnik jest nazewnictwo ,funkcja została nazwana "MaxPlayersTimesNumber" to samo makro zostało nazwane "MAX_PLAYERS_TIMES_NUMBER".
To tylko konwencja - funkcje w tym poradnik będą miały nazwy pisane małymi literami oprócz pierwszych znaków wyrazów , makra za to będą miały nazwy pisane wielkimi literami z wyrazami oddzielonymi "_", jest to po to,aby łatwo można było zorientować się czego teraz używamy bez sprawdzania definicji.
Kolejna konwencja to ustawianie stringu podmiany na pozycji 40 ( kiedy to możliwe ) - jest to po to, aby ułatwić czytanie dużej ilości makr np.
#define DEFINITION_1 1 #define MY_DEF 2 #define SOME_OTHER_LONG_NAME_DEFINITION 3 #define A_MACRO(%0) 3 * %0
Zamiast:
#define DEFINITION_1 1 #define MY_DEF 2 #define SOME_OTHER_LONG_NAME_DEFINITION 3 #define A_MACRO(%0) 3 * %0
Żadna z tych konwencji nie jest zasadą, więc masz wolną ręke przy używaniu ich, jeśli chcesz możesz je zignorować. Ale zachęcał bym Cie to posiadania naprawdę dobrych powodów zanim je zignorujesz.
Składnia / Semantyka
Szybkie przypomnienie. "Składnia" jest to wygląd kodu, "Semantyka" oznacza to co ten kod robi . Składnia pętli for to: "for (; ; ) {}", "semantyka" pętli for to: wykonaj się ileś razy na podstawie przekazanych parametrów. Wążna sprawą w następnej sekcji jest składnia i semantyka funkcji "printf". Składnia to: "printf(string[], ...);" - czyli string a następnie dowolna ilość parametrów, zawartość stringu nie wpływa na składnie - "printf("%d", 6, 7);" spełnia zasady składnie, ale 7 nie zostanie wyświetlona ponieważ string określa semantykę funkcji ( co ona naprawdę robi ).Kod się skompiluje ale nie będzie działał poprawnie , i jest to bardzo ważna różnica.
Parametry
Makro może posiadać kilka parametrów:
#define MULTIPLY_TWO_NUMBERS(%0,%1) %0 * %1
W rzeczywistości makro może mieć nawet do 10 parametrów:
#define MULTIPLY_NUMBERS(%0,%1,%2,%3,%4,%5,%6,%7,%8,%9) %0 * %1 * %2 * %3 * %4 * %5 * %6 * %7 * %8 * %9
Niektórzy lubią stawiać spacje po przecinku w liście parametrów np.:
#define MULTIPLY_TWO_NUMBERS(%0, %1) %0 * %1
Czegoś takiego nie można robić w makrach - tak jak było wcześniej powiedziane spacja oznacza koniec stringu do podmiany , więc preprocesor będzie szukał "MULTIPLY_TWO_NUMBERS(%0,", a nie "MULTIPLY_TWO_NUMBERS(%0, %1)" i podmieni to na "%1) %0 * %1".
Teraz kiedy wiesz już czym jest makro i czym są jego parametry możemy skupić się na różnicach parametrów makr i parametrów funkcji.
Po pierwsze - parametry makra i funkcji nie są tym samym i nie powinny być traktowane w ten sam sposób. Parametry funkcji są oddzielane przecinkami, parametry makr są oddzielone czymkolwiek chcesz.
Ten kod nie jest poprawny, podczas wywołania funkcji jest przekazywane za dużo parametrów:
MyFunc(a) { return a; } main() { printf("%d", MyFunc(1, 2)); }
Ten kod jest poprawny:
#define MY_FUNC(%0) %0 main() { printf("%d", MY_FUNC(1, 2)); }
W przykładzie wyżej makro "MY_FUNC" szuka czegoś pomiędzy dwoma nawiasami poprzedzone "MY_FUNC". W tym przykładzie zawartością pomiędzy nawiasami jest "1, 2". Wyrażenie zawiera przecinek ale dla makra nie robi to różnicy. Kod po wykonynaniu preprocesora dla tego makra będzie wyglądał tak:
main() { printf("%d", 1, 2); }
Wygenerowany kod jest w pełni poprawny( oczywiście 2 nie zostanie wyświetlone ).
Jeśli parametry nie są odzielane przecinkami , jak móc używać więcej niż jednego ? Parametry są odzielane czymkolwiek chcesz żeby były odzielane np.:
#define MULTIPLY_TWO_NUMBERS(%0,%1) %0 * %1
Kod wyżej będzie szukał "MULTIPLY_TWO_NUMBERS(" następnie wszystkiego do przecinka , przecinka , wszystkiego do zamykającego nawiasu.
printf("%d", MULTIPLY_TWO_NUMBERS(6, 7));
Kod wyżej zostanie podmieniony przez makro ( spacja tutaj jest dopuszczalna , nie jest dopuszczalna w deklaracji ) i otrzymamy taki kod:
printf("%d", 6 * 7);
Jednak przecinek nie jest zamykajacym nawiasem więc to też jest prawidłowe:
printf("%d", MULTIPLY_TWO_NUMBERS(6, 7, 8));
W tym przypadku parametr "%0" przyjmuje wartość 6 a parametr "%1" przyjmuje wartość "7,8" więc po wygenerowaniu kodu otrzymamy:
printf("%d", 6 * 7, 8);
Nawiasy
Skoro parametry są tak elastyczne jak możemy kontrolować to co generuje nam preprocesor ? Wszystkie makra wyżej były bardzo złe , nie używały nawiasów.
Przykład:
// Without brackets (first). #define MULTIPLY_TWO_A(%0,%1) %0 * %1 // With brackets (second). #define MULTIPLY_TWO_B(%0,%1) ((%0) * (%1)) main() { // Two with first. printf("%d", MULTIPLY_TWO_A(6, 7)); // Two with second. printf("%d", MULTIPLY_TWO_B(6, 7)); // Three with first. printf("%d", MULTIPLY_TWO_A(6, 7, 8)); // Three with second. printf("%d", MULTIPLY_TWO_B(6, 7, 8)); }
Po wygenerowaniu otrzymamy taki kod:
main() { // VALID printf("%d", 6 * 7); // VALID printf("%d", ((6) * (7))); // VALID printf("%d", 6 * 7, 8)); // INVALID! printf("%d", ((6) * (7, 8))); }
Finalny kod pokazuje ważna różnice , po dodaniu nawiasów wygenerowany kod jest błędny ( składnia jest błędna ). Próbujemy mnożyć "6" przez "7,8" - co jest błędne więc użytkownik dostanie błąd przy kompilacji.
Inne użycie nawiasów to ustalanie priorytetów operatorów. Dzięki nawiasom możemy ustalać kolejność wykonywania operatorów np. "4 + 5 * 6" otrzymujemy "34", nie "54". Ponieważ * ma wyższy priorytet niż + więc "4 + 5 * 6" zostaje wykonane do "4 + 30" a potem "34". Jeśli parametry były by wykonywane po kolei "4 + 5 * 6" staje się "9 * 6" a następnie "54".
Przeanalizujmy taki kod
#define ADD_TWO(%0,%1) %0 + %1 main() { printf("%d", ADD_TWO(3, 3) * 7); }
3 + 3 to 6 , 6 * 7 to 42 prawda? Nie! Zobaczmy wygenerowany kod.
main() { printf("%d", 3 + 3 * 7); }
Wiemy co się stanie, mnożenie zostanie wykonane przed dodawaniem więc otrzymamy 24. Całość możemy naprawić dodając nawiasy:
#define ADD_TWO(%0,%1) (%0 + %1)
Kolejny przykład
#define MUL_TWO(%0,%1) (%0 * %1) main() { printf("%d", MUL_TWO(3 + 3, 7)); }
Dodaliśmy nawiasy więc wszystko powinno być ok ? Błąd ! Zobaczmy co wygenerował preprocesor:
main() { printf("%d", (3 + 3 * 7)); }
Obliczenia są w nawiasach, ale znowu mnożenie zostanie wykonane przed dodawaniem. Powinniśmy dodać jeszcze jeden poziom nawiasów dzięki czemu wszystkie operacje będa wykonywane poprawnie:
#define MUL_TWO(%0,%1) ((%0) * (%1))
PAMIĘTAJ: Owijaj makro i parametry makra w nawiasy. Istnieją sytuację kiedy nie trzeba tego robić ale o nich opowiem później.
Makra kilku linijkowe
Makro może mieć kilka linii dzięki użyciu "\". Zasada jest prosta jeśli na końcu linii znajduje się znak \ makro jest kontynuowane w kolejnej linii. Makro nie może być kontynuowane w parametrach i nazwie z tych samych powodów z których nie możemy używać spacji. Uwaga: W tym poradniku znak kontynuacji jest umieszczany na pozycji 80:
#define MUL_TWO(%0,%1) \ ((%0) * (%1))
#define MUL_TWO(%0,%1) \ ( \ (%0) \ * \ (%1) \ )
#define MUL_TWO(%0,%1) \ ( \ ( \ %0 \ ) \ * \ ( \ %1 \ ) \ )
Ostatnia linii makra nie posiada operatora konytnuacji.
Pułapka
Jest jeden bardzo ważny problem podczas używania makr zamiast funkcji:
Wersja funkcyjna:
PrintSquare(var) { printf("%d", var * var); } main() { new var = 2; PrintSquare(var++); printf("%d", var); }
Wynik:
4
3
Wersja z makrami:
#define PRINT_SQUARE(%0) printf("%d", (%0) * (%0)) main() { new var = 2; PRINT_SQUARE(var++); printf("%d", var); }
Możemy otrzymać:
4
4
Lub:
6
4
Ponieważ parametry przekazane do makra są inkrementowane , więc inkrementacja jest dodawana przy generowaniu kodu:
main() { new var = 2; printf("%d", (var++) * (var++)); printf("%d", var); }
W takim przypadku w drugim printf zmienna var będzie zinkrementowana dwa razy - co jest błędne i nie wydarzy sie podczas użycia funkcji.
Kolejność wykonania dla operatora inkrementowania może zostać wykonana na dwa sposoby:
temp1 = var; temp2 = var; var = var + 1; var = var + 1; printf("%d", temp1 * temp2);
Lub:
temp1 = var; var = var + 1; temp2 = var; var = var + 1; printf("%d", temp1 * temp2);
Oba są technicznie prawidłowe - w obu przypadkach inkrementowanie jest wykonane po użyciu zmiennej , problemem jest tylko który sposób wybierze kompilator. Dlatego wynik może być "4" lub "6".
Bądź bardzo uważny podczas używania makr z parametrami które modyfikują zmienne - dlatego nazwy makr są pisane bardzo często z dużych liter aby użytkownik wiedział że jest to makro i był bardzo uważny podczas jego używania.
- AMXX.pl: Support AMX Mod X i SourceMod
- → Przeglądanie profilu: Reputacja: ogieR8
- Regulamin