Extern használata

Processing/Wiring (illetve C) nyelvű programozási fogások, tippek. (AVR-Duino, Arduino, EthDuino, Diecimila, Severino, Nano, LilyPad)
Avatar
SzLacus
Tranzisztorgyógyász
Hozzászólások: 175
Csatlakozott: 2012. május 20. vasárnap, 6:00

Extern használata

Hozzászólás Szerző: SzLacus »

Több topikban is található utalás a változók láthatóságát befolyásoló előtagokra, de célzottan egyiknek sem ez a témája. Itt foglalkozzunk ezzel.
Eljön az a pillanat, amikor a programot már nem győzi görgetni az írója, és szívesen szét darabolná értelmesen egybe tartozó darabokra. Ekkor biztosan bele fog ütközni a változók láthatóságának problémájába, amit a hatókör módosítókkal kéne tudni megoldani. Amikor a dolog nem úgy működik, mint azt várnám, akkor egyszerűsítés felé fordulva led a villogtatón próbálom a fájlokra darabolást kipróbálni. A példa kedvéért legyen a villogtatónk villogTikkTakk.ino fájlban.
Első lépésben a setup() előtt leírtakat kitereltem egy definiciok.h fájlba, és a .ino fájlban helyette megjelenik az include. Ezzel a globális érvényű definiciók, változók, makrók a továbbiakban a definiciok.h fájlba kell kerüljenek. Megfordítva, amit a definiciok.h tartalmaz az mind globális, bárki láthatja, és használhatja.
A refiniciok.h így néz ki. Ami most éppen ki van kommentezve, az a változó helyének próbálgatása során ki kerülhet a kommnet alól.

Kód: Egész kijelölése

/*
 * definiciok.h
 *
 * Created: 2017.06.09. 14:49:10
 *  Author: Laci
 */ 


#ifndef DEFINICIOK_H_
#define DEFINICIOK_H_

/*
	D E K L A R Á C I Ó K
*/
#define LEDPIN 13
#define ALAPIDO 1000
#define DEBUG


/*
	G L O B Á L I S    V Á L T O Z Ó K
*/

unsigned long most = millis();
// char* szoveg;
// String szoveg;



#endif /* DEFINICIOK_H_ */
Kell egy külső eljárás amiben játszani tudunk a változó helyével, ezért kiegészítettem a led villogtatót egy sorosporti kiírással, másodpercenkét Tiik-Takk jöjjön a soros portra. Készítettem egy metodusok.h fájlt, amibe a TikkTakk() eljárás került. Ebben is szerepel kikommentezett sor, amit a változó helyének változtatása során könnyen használhatunk.
A metodusok.h ai inkludolódik a villogTikkTakk.ino fájlba, még ez is a setup előttre. A metodusok.h az alábbi tartalommal bír.

Kód: Egész kijelölése

/*
 * metodusok.h
 *
 * Created: 2017.06.09. 15:27:54
 *  Author: Laci
 */ 


#ifndef METODUSOK_H_
#define METODUSOK_H_

void tikkTakk(){
// 	extern char* szoveg;
	extern String szoveg;
	szoveg = digitalRead(LEDPIN) ? "Tikk" : "Takk";
	Serial.println(szoveg);
}




#endif /* METODUSOK_H_ */
És akkor most eljutottunk odáig, hogy az ino fájt is részletezzem. Hogy rögtön feltételes fordítás lehetőségeinek alapjait is kipróbáljam, van benne néhány utasítás erre is.

Kód: Egész kijelölése

#include "definiciok.h"
#include "metodusok.h"

void setup()
{
	pinMode(LEDPIN,OUTPUT);
	digitalWrite(LEDPIN,LOW);
#ifdef DEBUG
	Serial.begin(57600);
	while(!Serial);
#endif
}

void loop()
{
// 	char* szoveg;
	String szoveg;
	if (most < (millis() - ALAPIDO))
	{
		most = millis();
		digitalWrite(LEDPIN, !digitalRead(LEDPIN));
#ifdef DEBUG
	tikkTakk();
#endif
	}
}

És amiért ezt a topikot nyitottam az a következő.
Ha a "szoveg" változómat a define-be, vagy a metodusok-ba rakom, akkor értelem szerűen nem kell elé az extern, mert vagy globál, vagy lokál ott ahol használo. Ekkor kommentezve szerepel a loop()-ban a "String szoveg;" sor. Ekkor a dolog le is fordul, és gondolom működne is, nem töltöttem ki.
De ha a loopban szeretném definiálni a "szoveg" változómat, akkor a metodusok között az extern módosítóval fel kell tüntessem a változót, mert itt használni szeretném, de máshol hoztam létre. Elképzelésem szerint azt közlöm a fordítóbal, pontosabban a preprocesszel, hogy amikor kelleni fog, akkor majd lesz "szoveg" változó, higgye el. Viszont fordításnál definiálatlannak találja a szoveg változót, és hibát ad rá.

Kód: Egész kijelölése

Compiling 'VillogTikkTakk' for 'Arduino Nano w/ ATmega328'
 
ccZo29ed.ltrans0.ltrans.o*: In function main
ccZo29ed.ltrans0.o*: (.text.startup+0x268): undefined reference to szoveg
ccZo29ed.ltrans0.o*: (.text.startup+0x26a): undefined reference to szoveg
ccZo29ed.ltrans0.o*: (.text.startup+0x272): undefined reference to szoveg
ccZo29ed.ltrans0.o*: (.text.startup+0x276): undefined reference to szoveg
ccZo29ed.ltrans0.o*: (.text.startup+0x27a): undefined reference to szoveg
ccZo29ed.ltrans0.ltrans.o*: ccZo29ed.ltrans0.o:(.text.startup+0x27e): more undefined references to szoveg follow
 
collect2.exe*: error: ld returned 1 exit status

Error compiling for board Arduino Nano w/ ATmega328

Persze ha a DEBUG változót kikommnetezem a def fájlban, akkor lefordul, hiszen senkinek nem lesz szüksége a szoveg változóra. Valamit nem jól értelmezek, vagy az Arduino nem enged külső változót, csak globálisat, vagy mit nem veszek észre?
Avatar
kapu48
Elektronbűvölő
Hozzászólások: 3375
Csatlakozott: 2008. augusztus 29. péntek, 6:00

Re: Extern használata

Hozzászólás Szerző: kapu48 »

Nem jól csinálod!

// A string az objektum
String stringOne = String(digitalRead(LEDPIN) ? "Tikk" : "Takk");
char Mychar[10]; //char az egy tömbb.
Mychar[0] = digitalRead(LEDPIN) ? "Tikk" : "Takk";

És létrehozól: metodusok.c fájlt, abban dolgozodki a fügvényeket.
A : metodusok.h-ban csak deklarálod ha globálissá akarod tenni.
Avatar
SzLacus
Tranzisztorgyógyász
Hozzászólások: 175
Csatlakozott: 2012. május 20. vasárnap, 6:00

Re: Extern használata

Hozzászólás Szerző: SzLacus »

Köszönöm, mindjárt értelmezem, és átalakítom.
Akkor a definiciok.h az bármilyen hosszan sorolhat #define, vagy változótípus kezdetű sorokat, ahhoz nem kell .c-t írni.
A metodusoknál .c fájlba áttolom a tikkTakk() függvényemet, a .h-ban pedig csak a prototípusát szerepeltetem. Ha jól gondolom, akkor majd itt a prototípusnál lesz lehetőségem arra, hogy ha a későbbiekben paraméterekkel hívott függvényt default paraméterrel akarok ellátni, akkor azt ott kell majd megadnom.
A String értékadását nem úgy kell ahogy én csináltam? Megyek gyorsan a reference oldalra, felfrissítem amit még meg sem jegyeztem. :-)
Avatar
SzLacus
Tranzisztorgyógyász
Hozzászólások: 175
Csatlakozott: 2012. május 20. vasárnap, 6:00

Re: Extern használata

Hozzászólás Szerző: SzLacus »

Még segítségre szorulok, mert az értékadás átalakítása nem segített.
Továbbra is a már fentebb bemutatott hibaüzenetet adja a fordítás mindaddig, míg a "szoveg" változóm az eljáráson belülre, vagy a globális helyre nem kerül. Igaz, még nincs .c fájlba tolva, csak az eredeti "szuszterolással" a .h-ban átalakítva erre a formára.

Kód: Egész kijelölése

	extern String szoveg;
	szoveg = String(digitalRead(LEDPIN) ? "Tikk" : "Takk");
Vagy nagyon nem értettem amit segítségnek kaptam.
Jut eszembe az Arduino az cpp, nem .cpp fájlt kéne használnom? Nem mintha abban mást írnék mint a .c-be.
Avatar
kapu48
Elektronbűvölő
Hozzászólások: 3375
Csatlakozott: 2008. augusztus 29. péntek, 6:00

Re: Extern használata

Hozzászólás Szerző: kapu48 »

Headerban csak const-nak lehet értéket adni!
extern String szoveg; // biztositja, hogy máshol is látható legyen

A változót már a *.c-be kel rakni:
// most hozod létre
String szoveg = String(digitalRead(LEDPIN) ? "Tikk" : "Takk");
Avatar
csabeszq
Bitfaragó
Hozzászólások: 678
Csatlakozott: 2012. szeptember 5. szerda, 6:00

Re: Extern használata

Hozzászólás Szerző: csabeszq »

Azért tegyük hozzá, hogy az extern semmire nem megoldás. Ideális esetben kb. 0 extern-t kellene használni. Én is használom, de nem azért, mert jó, hanem mert trehány vagyok.

Az objektum orientáltság lényege, hogy egy adott változót egy modul használ, a többiek nem. Minden programot pontosan 0 externnel szépen meg lehet írni, nem vesztesz vele semmit.

'a' változót A modul bizgeti. A változóhoz írsz getA(), setA() metódusokat, amivel olvasni és írni lehet őket. B/C/D modulnak tök felesleges tudni, hogy 'a' változó mi a csoda.

Kód: Egész kijelölése

int16_t getA()
{
  return a;
}
Azért írtam a hozzászólást, mert úgy éreztem, hogy a topik nyitó az extern-t jó megoldásnak találta. Szerintem sokat nem vesztesz azzal, ha meg sem tanulod, hogy micsoda.
Avatar
kapu48
Elektronbűvölő
Hozzászólások: 3375
Csatlakozott: 2008. augusztus 29. péntek, 6:00

Re: Extern használata

Hozzászólás Szerző: kapu48 »

Viszont igy müködik.

Kód: Egész kijelölése

/*
 * definiciok.h
 *
 * Created: 2017.06.09. 14:49:10
 *  Author: Laci
 */ 


#ifndef DEFINICIOK_H_
#define DEFINICIOK_H_

/*
   D E K L A R A C I O K
*/
#define LEDPIN 13
#define ALAPIDO 1000
#define DEBUG   1


/*
   G L O B A L I S    V A L T O Z O K
*/

extern unsigned long most;
extern char szoveg[10], *pszoveg;


#endif /* DEFINICIOK_H_ */

Kód: Egész kijelölése

/*
 * metodusok.h
 *
 * Created: 2017.06.09. 15:27:54
 *  Author: Laci
 */ 


#ifndef METODUSOK_H_
#define METODUSOK_H_

#include <arduino.h>  // <-Ez kel a String kezeléshez

extern String string; // Itt láthatóvá teszed  

#ifdef DEBUG
void tikkTakk2(void);
#endif



#endif /* METODUSOK_H_ */

Kód: Egész kijelölése

//	metodusok.c

#include "definiciok.h"
#include "metodusok.h"

char szoveg[10];
String string = "String"; // <- Itt létrehozod

#ifdef DEBUG
void tikkTakk2(){
  
//  pszoveg = &szoveg;
   //String szoveg;
//   *pszoveg = digitalRead(LEDPIN) ? "Tikk" : "Takk";
   
}
#endif

Kód: Egész kijelölése

#include "definiciok.h"
#include "metodusok.h"

unsigned long most; 

void setup() {
  pinMode(LEDPIN,OUTPUT);
   digitalWrite(LEDPIN,LOW);
#ifdef DEBUG
   Serial.begin(57600);
   while(!Serial);
#endif
  //pszoveg = szoveg[0];
  most = millis();
  Serial.println(string);      // <-Itt használhatod
}

void loop() {
}
Avatar
SzLacus
Tranzisztorgyógyász
Hozzászólások: 175
Csatlakozott: 2012. május 20. vasárnap, 6:00

Re: Extern használata

Hozzászólás Szerző: SzLacus »

Lesem nézem, forgatom. Teljesen fordított gondolkodással közelítettem a dolgot.
Azt gondoltam, hogy a definiciok.h-ban a globális változók vannak. Agyon olvastam a könyvet, meg a wikit meg amit még magyarul találtam, és azt modja mind, hogy oda kell az extern, ahol használni akarok egy olyan változót, amit máshol definiáltam. Azt is írja, hogy ami a használati hely előtt definiálódik, (és a globálisok ilyenek) annak használati helyén egy függvényben nem kell az extern. De valójában a globálisra nem kell később semmi, hiszen globális, mindenki tudja róla hogy mi az.
Az olvasmányaim szerint akkor kell az extern, ha egy függvényben, (tegyük fel a setup()-ban) definiált változót, a setupon belül hívott függvény is használná. Akkor abban a függvényben, amit a setupban hívok, a változót "be kell mutatnom" deklarálnomkell, hogy az már létezik, ezért elé kell írnom az extern-t. Amíg ezt nem teszem meg, addig a fordítás azt a hibát adja, hogy a változó nem létezik az adott szkópban.
Amint megmondom, hogy extern, abban a pillanatban túllendül a dolgon, mert elhiszi hogy van olyan. De az .o fájlhoz érve mégsem azonosítja össze az extern-ként megjelölt változót a setupban létrehozottal.
Talán amit leírtam abból látszik, hogy én milyen gondolkodás móddal közelítem a dolgot.
Az, hogy a define.h-ban a definiált változó elé externt írok, számomra a gondolatmenetem, és az olvasottak fordítottját jelenti, azt, hogy a definíciónál "kiajánlom" az extern-el a változómat, hogy más is elérhesse. De az mindenkinek látható, nincs szüksége ajánlásra.
De most neki megyek megint, hogy kapu48 megoldását megértsem.
Csabeszku-nak meg köszönöm az intemet, még nem tudom, hogy kell-e ez az extern nekem, csak azt tudom, hogy szeretném megtanulni hogyan kell több fájlra bontani a programomat. Az olvasgatásból pedig úgy tűnt, hogy az extern ehhez elengedhetetlen lesz. De lehet, hogy nem így van, hanem amit többen is hasztnálnak azt globálra kell venni, amit meg a függvény maga, azt meg ott a függvényben kell definiálni, és kész.
Azt nem tudom, hogy hogyan fognak felszabadulni azok a helyek, amiket mondjuk csak a setupban használok, a loopban meg nincs rájuk szükség. Lehet, hogy ezt megoldja helyetetm a környezet, de ha a loop-on belül lesz két eljárásom, amiből vagy az egyik, vagy a másik játszik, akkor a nem használt részhez tartozó változók helyét szabaddá lehet-e tenni.
Avatar
csabeszq
Bitfaragó
Hozzászólások: 678
Csatlakozott: 2012. szeptember 5. szerda, 6:00

Re: Extern használata

Hozzászólás Szerző: csabeszq »

A fájlokra bontás nagyon fontos dolog. Érdemes megtanulni.

Előfordul, hogy extern-t is használni fogsz, de nélküle is szépen meg lehet csinálni.

extern jelentése: külső

Kód: Egész kijelölése

extern int a;
azt jelenti, hogy a változót már létrehozta valaki, azt szeretnéd használni.

A példák, amiket írtatok nem annyira rosszak, mert a header-be raktátok az extern-t, tehát a modularitás elvét nem sértette.

modul.cpp - létrehozza a változót
modul.h - extern-nel mások számára is elérhetővé teszi

Nem a legszebb megoldás, de annyira nem is csúnya.

Ez az ami nagyon ronda:

Kód: Egész kijelölése

extern int gps_allapot;

float ntc_homerseklet()
{
  if( gps_allapot == 2 )
  {
    ...  
  }
}
Magyarul a modulok össze-vissza keresztbe-kasul hívják egymást. Az NTC hőmérő extern-nel behozza a GPS vevő állapotát és hasonlóak. A hőmérő modul hőmérsékletet mér, a GPS modul lekérdezi a koordinátákat. Egymás adataiban ne túrkáljanak. Fájlokra bontásnál a logikai egységet meg kell őrizni.
vargham
Chipgyilok
Hozzászólások: 308
Csatlakozott: 2014. január 8. szerda, 8:32

Re: Extern használata

Hozzászólás Szerző: vargham »

> Azt nem tudom, hogy hogyan fognak felszabadulni azok a helyek, amiket mondjuk csak a setupban használok, a loopban meg nincs rájuk szükség.
Ha nincsen benne direkt memórafoglalás (new, malloc), akkor a stack-en foglalja le a helyet, és automatikusan felszabadítja, amint az adott függvény visszatér (kilép).
Ha azt akarod, hogy egy lokális változót létrehozol egy függvényben, és egy másik függvényben szertnél vele dolgozni, akkor paraméterként add át a címét (pointer), és dolgozz azzal.
vargham
Chipgyilok
Hozzászólások: 308
Csatlakozott: 2014. január 8. szerda, 8:32

Re: Extern használata

Hozzászólás Szerző: vargham »

Avatar
kapu48
Elektronbűvölő
Hozzászólások: 3375
Csatlakozott: 2008. augusztus 29. péntek, 6:00

Re: Extern használata

Hozzászólás Szerző: kapu48 »

Szerintem az extern, pont fordítva van, mint ahogyan te gondolod!:
Az extern deklaráció használható a függvények belsejében és azt jelzi, hogy a deklarált objektumhoz máshol rendeljük hozzá a tárterületet.

Tehát azért hiányolja az objectben a fordító, mert nem találja a létrehozás helyét.
Avatar
kapu48
Elektronbűvölő
Hozzászólások: 3375
Csatlakozott: 2008. augusztus 29. péntek, 6:00

Re: Extern használata

Hozzászólás Szerző: kapu48 »

Ez legalább magyarúl van: http://lidi.uw.hu/krc/files/a.html#A4.1.
A8.1. Tároláslosztály-specifikátorok

Ha a hívó függvényben, létrehozol változót. Akkor a belőle meghívottban externel láthatóvá teheted.
És addig él amíg az eredeti függvényből ki nem lépsz.
vargham
Chipgyilok
Hozzászólások: 308
Csatlakozott: 2014. január 8. szerda, 8:32

Re: Extern használata

Hozzászólás Szerző: vargham »

> Ha a hívó függvényben, létrehozol változót. Akkor a belőle meghívottban externel láthatóvá teheted.
> És addig él amíg az eredeti függvényből ki nem lépsz.
És így alkotsz katyvaszt. Minden egység csak a maga adataival dolgozzon. Átláthatóbb a program.
Ha csak nincsen valami NAGYON nyomós okod, akkor paramétert adsz át, és felhasználod a visszatérési értéket.
Amúgy tud valaki a fentire nagyon nyomós okot? Nekem hirtelen nem jut eszembe.
Avatar
kapu48
Elektronbűvölő
Hozzászólások: 3375
Csatlakozott: 2008. augusztus 29. péntek, 6:00

Re: Extern használata

Hozzászólás Szerző: kapu48 »

Ez volt a kérdés!
És nem az Ok-ra, hanem a miért kap hibaüzenetetre volt a felelet! Mivel még csak ott tart, hogy megértse az Extern működését.

Az a gond, hogy ti nagygépes több személyes fejlesztésben gondolkoztok. De a kezdőnek még korai moduláris programozást emlegetni.
Válasz küldése