PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : nochmal Arraypointerreferenztohuwabohu



HaWe
09.12.2018, 12:20
hallo,
ich schleudere wieder bei Übergabe von arrays an Funktionen:

ich habe einen array of cstrings

char mlist0[6][11] = {"Titel","ESC ","Ja","Nein","foo","bas"};

und die folgende Funktion (in einer Klasse als Methode)


class tMenu {
private:
int MENULEN;
public:
char ** list;

void importlist ( char ** extlist ) {
int16_t N = MENULEN;
for(int line=0; line<N; line++) {
strncpy( list[line], extlist[line], strlen(extlist[line]) );
}
}

tMenu menu0(6,11);

//


ich will jetzt die Objekt-Methode aufrufen zum Initialisieren:


menu0.importlist(mlist0); // exit status 1 no matching function for call to 'tMenu::importlist(char [6][11])'

menu0.importlist(*mlist0); // exit status 1 no matching function for call to 'tMenu::importlist(char [11])'

menu0.importlist(**mlist0); // exit status 1 invalid conversion from 'char' to 'char**' [-fpermissive]

menu0.importlist(&mlist0); // exit status 1 no matching function for call to 'tMenu::importlist(char (*)[6][11])'

menu0.importlist((char**)mlist0); // => runtime error:
Exception ( 28 ):
epc1=0x40206204 epc2=0x00000000 epc3=0x00000000 excvaddr=0x00000030 depc=0x00000000


ich weiß nicht mehr, was ich jetzt sonst noch ausprobieren könnte... :confused: :confused: :confused:

Moppi
09.12.2018, 12:35
Hier kann zumindest die Funktion zum Aufruf zugeordnet werden:
menu0.importlist(**mlist0); // exit status 1 invalid conversion from 'char' to 'char**' [-fpermissive]

Bloß die Parameterübergabe ist noch nicht i.O.: invalid conversion

HaWe
09.12.2018, 12:39
ja, genau, trotzdem läuft dann was schief zur Laufzeit wenn ich explizit caste

menu0.importlist( (char**)mlist0 );

Moppi
09.12.2018, 12:46
Stört vielleicht, dass von "(**mlist0)" nach "void importlist ( char ** extlist )" konvertiert werden soll?
Leider werde ich hier nicht weiter helfen können, weil ich mich damit zu wenig auskenne. Werde ich auch mal abstellen müssen...

HaWe
09.12.2018, 13:00
vermutlich hat gestört, dass von der Form
char list1[a][b]
in die Form von
char **list2
übertragen werden sollte, daher mein Versuch des expliziten casts
(char**) list1 // list1[a][b] ->-> **list1
dann --> als Parameter für Funktion importlist (char ** list2)

führt dann aber zu Laufzeitfehlern, wenn die array-Inhalte kopiert werden sollen

Ich weiß nur absolut nicht, wie es stattdessen richtig geht :confused:

Moppi
09.12.2018, 13:45
Hier definierst Du die Liste:


char mlist0[6][11]

mit ** mlist0 erzeugst Du einen Zeiger. (mit dem ersten * auf mlist0[x] und mit dem zweiten * auf mlist[x][x])

Was ist, wenn Du so den Aufruf machst: menu0.importlist(*mlist0);

Und Deine Funktion so gestaltest: void importlist ( char * extlist )

Weil, beim Kopieren verwendest Du ja nur:extlist[line] nicht: extlist[x][line]

Aber wie gesagt, ich glaube, ich stifte nur mehr Verwirrung!

Ceos
09.12.2018, 13:48
deine Deklaration macht mir irgendwie Kopfschmerzen ... aber für die symmetrie ist es schon nicht ganz verkehrt

(nicht böse gemeint, da schrillen nur ein paar alarmglocken)

deklariere doch ein

char* mlist[]

und fülle es mit

char* entry0 = "bla"
char* entry1 = "foo"
mlist[0] = entry0;
mlist[1] = entry1;

ist zwar etwas umständlich aber explizit

und in deiner import funktion kannst du auch so etwas schreiben
void importlist ( char * extlist[] )
damit sollte es besser funktionieren, probiert haeb ich es aber nciht ... das Problem ist nämlich dass die strings dabei unterschiedlich lang sind was weider andere phänomene hervorrufen kann

Moppi
09.12.2018, 14:10
Ich habs jetzt mal so:

void importlist (char ** extlist )

und so:

tMenu menu0(char ** mlist0);


Nur so wird es für ein ESP-12E ohne Warnung und Fehler compiliert.

HaWe
09.12.2018, 15:26
man kann eine
char ** mlist;
ja nicht initialisieren als
{"Titel","ESC","Ja","Nein","foo","bas"}; // error !!

Das ist aber wesentlich, sonst wird es bei der Initialisierung aller Menüpunkte aller Menüs und Untermenüs zu kompliziert.

also brauche ich für den Original-Array
char mlist[6][11] = {"Titel","ESC","Ja","Nein","foo","bas"};

und den will ich jetzt an die Funktion
tMenu::import() // (fraglich ob hier als Argument (char ** extlist) richtig ist ?!? )
übergeben,

denn man kann als Funktions-Argument kein
import(char extlist[MENULEN][LINELEN])
definieren, zumal MENULEN und LINELEN grundsätzlich in der Class variabel sind und keine Konstanten (ihre Größe wird erst bei Instanziierung des Objekts bekannt gegeben, wenn die interne list erzeugt wird)


public:
char ** list;

tMenu (int16_t menulen, int16_t linelen, tMenu* pMenu) { // constructor
MENULEN = menulen;
LINELEN = linelen;
list = new char*[MENULEN];
for(int i = 0; i < MENULEN; i++)
{
list[i] = new char[LINELEN+1];
}


habe ich es erst mal übergeben, kann ich die Liste von strings in der Objekt-Methode weiterverwenden:

for(int line=0; line<MENULEN; line++) {
strncpy( list[line], extlist[line], strlen( list[line] ) );
...

Offenbar ist das für die Übergabe mit den ** falsch, aber wie geht es richtig?

Wie übergibt man einen 2-dim array (bzw. eine Aufzählung von cstrings) per Pointer oder als Kopie an einen zweiten 2-dim array?

Moppi
09.12.2018, 16:29
char mlist0[6][11] = {"menu0","ESC >","Ja","Nein","foo","bas"};

class tMenu{
private:
int MENULEN;
public:
char ** list; //char * list[];


void importlist (char ** extlist ) {
int16_t N = MENULEN;
for(int line=0; line<N; line++) {
strncpy( list[line], extlist[line], strlen(extlist[line]) );
}
}
};


void setup(){
}


void loop(){
tMenu menu0(char ** mlist0);
}

HaWe
09.12.2018, 16:39
ist das nicht mein eigener Code?
Der war zwar zu kompilieren, aber ergab ja einen Runtime Error.

Oder hast du daran etwas verändert?

Moppi
09.12.2018, 17:05
Der ist geändert. Produziert bei mir keinen Error.

HaWe
09.12.2018, 17:58
ach so, du übergibst auch schon die Liste an den Constructor...
mal sehen...

- - - Aktualisiert - - -


tMenu menu0(char ** mlist0);
gehört aber global instanziiert, nicht jedes Mal neu in der loop,
und warum übergibst du die Liste mlist0 als Doppelpointer mitsamt Variablentyp char** ?
Parameter führen doch keine Typen beim Aufruf...?
Das gibt bei mir zumindest auch einen compile error.

Instanziiere ich indes global per

tMenu menu0(6,11, (char**)mlist0 );
lässt es sich kompilieren, erzeugt aber ebenfalls wieder einen Laufzeitfehler, wie schon zuvor

rst cause:4, boot mode: (1,7)
wdt reset



class tMenu {
protected:
int16_t MENULEN, LINELEN, VISLNUM, FONTHI;

public:
char ** list;
tMenu * preMenu;
int16_t act;

tMenu (int16_t menulen, int16_t linelen, char ** extlist) // constructor
{
MENULEN = menulen; // number of available menu options
LINELEN = linelen; // line length of menu options

list = new char*[MENULEN];
for(int i = 0; i < MENULEN; i++)
{
list[i] = new char[LINELEN+1];
strncpy( list[i], extlist[i], LINELEN);
}

}
};
//

Moppi
09.12.2018, 19:18
gehört aber global instanziiert, nicht jedes Mal neu in der loop

Ja, weiß ich, war nur zum Test.


warum übergibst du die Liste mlist0 als Doppelpointer mitsamt Variablentyp char** ?


Weil alles andere zu Fehlermeldungen führte. Ich habe mir die Meldungen durchgelesen und das gegeben, was der Compiler haben wollte. Meine Arduino-IDE ist so damit zufrieden.

Und wie gesagt, bin ich eher hier an der Stelle eine schlechte Hilfe. Weiß noch nicht mal, ob Dich das so in irgendeiner Weise weiter bringt. Weil ich zu wenig damit programmiert habe, gerade in C/C++. Ich muss mich unbedingt mehr damit beschäftigen, allerdings ist die Sprache teils sehr eigenartig, das gefällt mir nicht so gut. Da gibt es andere mit denen es sich einfacher umgehen lässt und man wegen so einem Mini-Problem nicht tagelang suchen muss. Aber so ist es nun mal mit C.

Ich hole die Tage mal mein dickes C-Buch aus dem Schrank, mal schauen, was dort zur Parameterübergabe drin steht, ob man das hier in der Arduino-IDE überhaupt brauchen kann. Dann übe ich mal ein wenig.


MfG

HaWe
09.12.2018, 19:36
ja, vielen Dank für dein Interesse!

Falls mal jemand den gesamten (komplizierteren) Code richtig komplett testen möchte (Button-Steuerung auskommentiert), hier ist er:





// i2c
#include <Wire.h> // Incl I2C comm, but needed for not getting compile error


//----------------------------------------------------------------------------
// display driver
//----------------------------------------------------------------------------
#include <Adafruit_GFX.h> //

// Adafruit Arduino or ESP OLED driver
//#include <Adafruit_SSD1306.h>
#include <ESP_SSD1306.h> // Modification of Adafruit_SSD1306 for ESP8266 compatibility

#include <Fonts/FreeSans12pt7b.h> //
#include <Fonts/FreeSansBold12pt7b.h> //
#include <Fonts/FreeSans9pt7b.h> //
#include <Fonts/FreeMono12pt7b.h> //
#include <Fonts/FreeMono9pt7b.h>
// Pin definitions

#define OLED_RESET 10 // GPIO10=D12 Pin RESET signal (virtual)

//Adafruit_SSD1306 display(OLED_RESET);
//Adafruit_SSD1306 display(128, 64, &Wire, OLED_RESET);
ESP_SSD1306 display(OLED_RESET);


//----------------------------------------------------------------------------
// ButtonClass buttons
//----------------------------------------------------------------------------

/*
#include <ButtonClass.h>
// 3 buttons for menu control
tButton btn0;
tButton btn1;
tButton btnEnter;
*/


//----------------------------------------------------------------------------
// OLED menu
//----------------------------------------------------------------------------

class tMenu {

private:
void parselist() {
bool issub;
issub=false;
int16_t N = MENULEN;
for(int line=0; line<N; line++) {
for(int k=strlen(list[line]-1); k<LINELEN-1; k++) {
if(list[line][k]=='\0') list[line][k]=' ';
if(list[line][k]=='>') issub=true;
}
if(issub) list[line][LINELEN-2] = '>';
else list[line][LINELEN-2] = '.';
list[line][LINELEN-1] = '\0';
issub=false;
}
}

protected:
int16_t MENULEN, LINELEN, VISLNUM, FONTHI;
int16_t firstvln, lastvln, displn;
char buf[20];

public:
char ** list;
tMenu * preMenu;
int16_t act;

tMenu (int16_t menulen, int16_t linelen, char ** extlist, tMenu* pMenu) : // constructor
MENULEN(5), LINELEN(11), VISLNUM(5), FONTHI(13), act(0)
{
firstvln=0; lastvln=0; displn=0;
act=0;
MENULEN = menulen; // number of available menu options
LINELEN = linelen; // line length of menu options
preMenu = pMenu; // predesessor menu

list = new char*[MENULEN];
for(int i = 0; i < MENULEN; i++)
{
list[i] = new char[LINELEN+1];
strncpy( list[i], extlist[i], LINELEN); // <<<< Hier funktioniert es nicht !!
}
}

void initscr(int16_t vislnum, uint8_t fonthi ) { // ()=defaults=(5,13)
VISLNUM = vislnum; // number of visible menu options
FONTHI = fonthi;
}


void mdisplay() {
if(act>VISLNUM-1) firstvln=_min(act-1, MENULEN-VISLNUM);
else firstvln=0;
lastvln=firstvln+VISLNUM-1;
display.clearDisplay();
for(int i=firstvln; i<=lastvln; i++) {
displn=(FONTHI-3) + (i-firstvln)*FONTHI;
if(i==act) {
display.setCursor(0, displn); display.print('>');
Serial.print('>');
}
else Serial.print(' ');
Serial.println(list[i]);
display.setCursor(11, displn); display.print(list[i]);
}
display.display();
Serial.println();
}

int32_t checkbtn(int8_t btop, int8_t bbtm, int8_t bfunc) {
if(btop==1){ // dec
if(act>0) act--;
//Serial.print ("^"); Serial.println(act);
mdisplay();
return 11 ;
}
if(btop==3){ // min
act=0;
//Serial.print ("^"); Serial.println(act);
mdisplay();
return 13 ;
}
if(bbtm==1){ // inc
if(act<MENULEN-1) act++;
//Serial.print ("v"); Serial.println(act);
mdisplay();
return 21;
}
if(bbtm==3){ // max
act=MENULEN-1;
//Serial.print ("v"); Serial.println(act);
mdisplay();
return 23;
}
if(bfunc==1){
sprintf(buf,"MenuLn %d ToDo=%d", act,bfunc);
Serial.println(buf);
return 1;
}
if(bfunc==2){
sprintf(buf,"MenuLn %d ToDo=%d", act,bfunc);
Serial.println(buf);
}
if(bfunc==3){
sprintf(buf,"MenuLn %d ToDo=%d", act,bfunc);
Serial.println(buf);
return 3;
}
return 0;
}

~tMenu() { // destructor
// Dynamically delete the array
delete[] list ;
}
};


char mlist0[6][11] = {"Titel 0","Zeile1","zu menu02>","Zeile3","Zeile4","Zeile5"};
tMenu menu0(6,11, (char**)mlist0, &menu0); // numEntries, lineLength, preMenu (N/A);

char mlist02[5][11] = {"Titel 02","ESC>","Zeile2","Zeile3","zu menu024"};
tMenu menu02(5,11, (char**)mlist02, &menu0); // numEntries, lineLength, preMenu=menu0;

char mlist024[6][11] = {"Titel 024","ESC >","Ja","Nein","foo","bas"};
tMenu menu024(6,11, (char**)mlist024, &menu02); // numEntries, lineLength, preMenu=menu02;


tMenu * actMenu = &menu0;





//----------------------------------------------------------------------------
// OLED dashboard
//----------------------------------------------------------------------------

void dashboard(int8_t mode) {

display.clearDisplay();
display.setFont();

if (mode == 0) {
display.setFont(); // h=8.0 pt
display.setCursor( 0, 0); display.print(" 0 Hello World 1");
}
display.display();
display.setFont();
}



void setup(void)
{
// Start Serial
Serial.begin(115200);
delay(3000); // wait for Serial()

Serial.println("Serial started");


/*
btn0.init(D6, INPUT_PULLUP, 50);
btn1.init(D3, INPUT_PULLUP, 50);
btnEnter.init(D4, INPUT_PULLUP, 50);
*/


// Start Wire (SDA, SCL)
//Wire.begin(ESPSDA,ESPSCL); // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
Wire.begin();

// SSD1306 Init
display.begin(SSD1306_SWITCHCAPVCC); // Switch OLED
//display.begin(SSD1306_SWITCHCAPVCC, 0x3D, true, false);
display.setRotation(2);
display.clearDisplay(); // Clear the buffer.

// text display tests
display.setTextSize(1);
display.setTextColor(WHITE);
display.setCursor(0,0);
display.println("Hello, world!");
display.display();

//--------------------------------------
// test + debug


display.setFont(&FreeMono9pt7b); // h=13.10pt
display.clearDisplay();
display.display();

Serial.println("menu init:");

strcpy(menu0.list[0], " menu0"); // debug: menu name at top line
strcpy(menu02.list[0], " menu02"); // debug: menu name at top line
strcpy(menu024.list[0], " menu024"); // debug: menu name at top line

Serial.println("actMenu=menu0");

actMenu = &menu0; //
actMenu->mdisplay();

actMenu = &menu02; //
actMenu->mdisplay();

actMenu = &menu024; //
actMenu->mdisplay();

delay(1);
}


void loop() {
int32_t result;
/*
result = actMenu->checkbtn(btn0.click(), btn1.click(), btnEnter.click() );

if(result==3) { // long press btnEnter
if( (actMenu==&menu0) && actMenu->act==2) {
actMenu = &menu02;
actMenu->mdisplay();
}
else
if( (actMenu==&menu02) && actMenu->act==4) {
actMenu = &menu024;
actMenu->mdisplay();
}
else
if( (actMenu==&menu02) && actMenu->act==1) {
actMenu = actMenu->preMenu;
actMenu->mdisplay();
}
else
if( (actMenu==&menu024) && actMenu->act==1) {
actMenu = actMenu->preMenu;
actMenu->mdisplay();
}
}

*/

}



(Ausgabe über Serial + über OLED, aber OLED lässt sich auch auskommentieren)

HaWe
10.12.2018, 16:44
habe es hingekriegt, bin selber überrascht:


tMenu (int16_t menulen, int16_t linelen, char ** extlist, tMenu* pMenu) : // constructor
MENULEN(5), LINELEN(11), VISLNUM(5), FONTHI(13), act(0)
{
firstvln=0; lastvln=0; displn=0;
act=0;
MENULEN = menulen; // number of available menu options
LINELEN = linelen; // line length of menu options
preMenu = pMenu; // predesessor menu

list = new char*[MENULEN];
for(int i = 0; i < MENULEN; i++)
{
list[i] = new char[LINELEN+1];
strncpy( list[i], extlist[i], strlen(extlist[i]));
}
}


char * mlist0[11] = {"Titel 0","Zeile1","zu menu02>","Zeile3","Zeile4","Zeile5"};
tMenu menu0(6,11, (char**)mlist0, &menu0); // numEntries, lineLength, preMenu (N/A);

char * mlist02[11] = {"Titel 02","ESC>","Zeile2","Zeile3","zu menu024"};
tMenu menu02(5,11, (char**)mlist02, &menu0); // numEntries, lineLength, preMenu=menu0;

char * mlist024[11] = {"Titel 024","ESC >","Ja","Nein","foo","bas"};
tMenu menu024(6,11, (char**)mlist024, &menu02); // numEntries, lineLength, preMenu=menu02;

Ausgabe der Menüs, völlig korrekt (Ausgabe hier testweise nur immer max. 5 Einträge):


>Titel 0
Zeile1
zu menu02>
Zeile3
Zeile4

>Titel 02
ESC>
Zeile2
Zeile3
zu menu024

>Titel 024
ESC >
Ja
Nein
foo