Der Programmier-Thread

crack-king

Administrator
Team-Mitglied
systems, systems, systems, systems, systems, systems
Bin jetzt Google-Mitarbeiter ~lol~



Ne, scherz ;) War heute bei einem Android-Workshop von Google und Medion, der an unserer Uni veranstaltet wurde. Wohl auch nur, weil beide ehemalige Studenten sind und nun hohe Positionen in beiden Unternehmen haben.

War aber echt verdammt interessant das Ganze, auch wenn es sich hauptsächlich um die Basics gedreht hat bzw. man eigentlich mit 8 Stunden gar nicht zu den spannenden Sachen kommen konnte. Aber trotzdem hat man gemerkt, dass Google sich verdammt viel bei der Entwicklung davon gedacht hat (Einer der beiden hat selber bei der Entwicklung von Android mitgearbeitet 0o ). Es gibt einfach zig Möglichkeiten, die einzelnen Apps miteinander kommunizieren zu lassen. Selbst das Speichern von diversen Daten ist viel einfacher als gedacht, denn jede App hat zwei Verzeichnisse. Einmal für persistente und einmal für temporäre Dateien, die auch mal verloren gehen dürfen. So kann man mega leicht Sachen speichern. Interessant fand ich auch die Entwicklung über die einzelnen Android-Versionen und was man mittlerweile für Möglichkeiten hat. Manche Sachen sind kinderleicht und seit dem letzten Jahr hat die Google-Dokumentation auch deutlich zugelegt. Also ich bin gerade wieder richtig begeistert von der Plattform und würde direkt die Onpsx-App weiterprogrammieren, wobei ich vllt teilweise neu anfangen würde, da manche Sachen einfach grauenhaft implementiert waren :ugly:
 
PSN-Name: DZDomi
ich bräuchte ein bisschen hilfe bei einem c programm:

und zwar will ich minesweeper nachprogrammieren und anscheinend schreibt das programm in einen speicherbereich in den es nicht hineinschreiben darf und ich kann das problem aber nicht genau feststellen:

Es wird zuerst Bombenanzahl, spalten sowie zeilenanzahl vom Benutzer eingegeben hier entsteht kein problem

Danach wird die Funktion aufgerufen und bekommt die adresse des ersten Wertes im Array

Es generiert einen zufaelligen Spalten und Zeilenwert um die Bomben zu platzieren, der die spalten sowie zeilen anzahl nicht ueberschreitet

Das Problem entsteht bei den verschiedensten werten: wie man hier an den screenshots sehen kann:

Einmal funktionert es:



Ein andermal ensteht eben dieses Problem: (dadurch funktioniert die Ausgabe natuerlich auch nicht mehr richtig)



Wie ich schon bemerkt habe schreibt er bei einer hohen bombenanzahl in einen bereich in den er nicht hineinschreiben darf, was daran zu erkennen ist das spalten, zeilen sowie diverse andere variablen auf -1 gesetzt werden in der main funktion:



Code:
#define ZEILEN 26
#define SPALTEN 26

int main(){
    
   //Einlesen von Zeilen und Spaltenbreite, sowie Minenanzahl

   feldFuellen(bombenfeld, zeilen, spalten, bomben); //Aufruf der Funktion

}

void feldFuellen(int feld[ZEILEN][SPALTEN], size_t zeilen, size_t spalten, int bomben){
    
    srand(time(NULL));
    
    int i, bombenzeile, bombenspalte;
    
    for (i = 0; i < bomben;) {
        
        bombenzeile = rand() % zeilen;
        bombenspalte = rand() % spalten;
        
        if(feld[bombenzeile][bombenspalte] == -1){
            
            continue;
            
        }
        else{
            
            feld[bombenzeile][bombenspalte] = -1; //Das Problem entsteht irgendwo hier
            i++;
            
        }
        
    }
    
}
Ich hoffe irgendjemand hat zeit sich den code mal ein bisschen anzuschauen ; )
Seid nicht so streng mit meinen Programmier skills in c hab erst dieses jahr mit c angefangen

Falls jemanden den ganzen source code haben will:

http://pastebin.com/cYSMDnq7

Es funktioniert natuerlich noch nicht alles ; ) (das was hald auskommentiert ist)

Ich glaube ich mache irgendwas bei der Uebergabe falsch aber ich finde aktuell leider keine lösung fuer mein problem :( ?(
 
PSN-Name: chrizeliq
Der Fehler ist ganz schoen tricky, und ich hab auch etwas gebraucht um es zu verstehen.

Mehrdimensionale Arrays sind nicht ganz so einfach im Umgang in C oder C++, desshalb verwende ich sie nie. C ist nicht Java, und mehrdimensionale Arrays sind ein Punkt, der nicht so einfach ist, vor allem wenn du jetzt eine dynamische Groesse von deinem Feld im Speicher abbilden willst.

Erst mal muss man feststellen wie mehrdimensionale Arrays in C im Speicher ueberhaupt ausschauen. Zum Testen gebe ich mir also die Adresse von bombenfeld selber aus (bombenfeld ist ein Pointer auf die Adresse des ersten Elements in der ersten Zeile) -- desweiteren gebe ich mir die Adresse des ersten Feldes aus der zweiten Zeile aus, und schaue mir dessen Speicheradresse an:

Code:
printf("%p / %p\n", bombenfeld, &bombenfeld[1][0]);
Bei mir gibt das folgendes aus:
0x7fff681e1100 / 0x7fff681e115c

in Dezimal ist das die Speicheradresse 3777755007410400 und 3777755007410534 d.h. eine Differenz von 206 Adressen. Das ist merkwuerdig, weil eine Zeile in meinem Beispiel ja nur 23 Spalten hat.

Letztendlich ist mein Verstaendnis jetzt, dass C mehrdimensionale Arrays nicht in zusammenhaengender Form im Speicher hinterlegt d.h. auf die Zellen der ersten Zeile muessen nicht sofort die Zellen der zweiten Zeile folgen.

Das ganze waere kein Problem, wenn du jetzt dein mehrdimensionales Array mit dynamischer Groesse nicht falsch an die einzelnen Funktionen uebergeben wuerdest, was letztendlich dein Fehler ist:

Code:
#define ZEILEN 26
#define SPALTEN 26
void feldFuellen([b]int feld[ZEILEN][SPALTEN][/b], size_t zeilen, size_t spalten, int bomben);
Du definierst Funktionen die ein mehrdimensionales Array mit fixer Groesse erwarten, naemlich 26 Zeilen und 26 Spalten. Wenn du diesen Funktionen jetzt einen Pointer auf ein mehrdimensionales Array uebergibst, das kleiner ist als 26 Zeilen und 26 Spalten, dann fuehrt das dazu, dass in diesen Funktionen auf das mehrdimensionale Array falsch zugegriffen wird, weil der Compiler davon ausgeht, dass dein Array 26 Zeilen und 26 Spalten hat. Das liegt eben daran, dass mehrdimensionale Arrays im Speicher nicht fortlaufend hinterlegt sind, und der Compiler abhaengig von der Array-Groesse unterschiedlich auf die Arrays zugreifen muss.

Wie greift C auf mehrdimensionale Arrays zu? Das koennen wir uns anschauen, indem wir uns die Assembler-Ausgabe z.B. von folgendem Programm anschauen:

Code:
void foo(int array[3][3]) {
  array[2][2] = 42;
}

void bar(int array[5][5]) {
  array[2][2] = 42;
}
Kompiliert mit gcc -O0 -S foo.c bekommen wir folgenden Assembler-Code bei meinem 64 bit Intel System:

Code:
_foo:
Leh_func_begin1:
	pushq	%rbp
Ltmp0:
	movq	%rsp, %rbp
Ltmp1:
	movq	%rdi, -8(%rbp)
	movq	-8(%rbp), %rax
	[b]movl	$42, 32(%rax)[/b]
	popq	%rbp
	ret

_bar:
Leh_func_begin2:
	pushq	%rbp
Ltmp2:
	movq	%rsp, %rbp
Ltmp3:
	movq	%rdi, -8(%rbp)
	movq	-8(%rbp), %rax
	[b]movl	$42, 48(%rax)[/b]
	popq	%rbp
	ret
Merkst du etwas? foo schreibt auf das Array mit movl $42, 32(%rax) und bar mit movl $42, 48(%rax) d.h. obwohl auf C-Seite der Code mit array[2][2] = 42; genau der gleiche ist, unterscheidet sich auf Assembler-Seite der Zugriff auf das Array im Speicher, weil mit einem anderen Index auf die Arrays zugegriffen wird.

Das ist das Problem mit mehrdimensionalen Arrays. Ich sehe zwei moegliche Loesungsansaetze fuer dein Problem:

Loesung 1
Du hast die Groesse deines Feldes sowieso auf 26 Zeilen und Spalten beschraenkt. D.h. anstatt das bombenfeld mit dynamischer Groesse zu erzeugen:
Code:
int bombenfeld[zeilen][spalten];
Wuerde ich das Bombenfeld-Array einfach mit fixer Groesse im Speicher erstellen:
Code:
int bombenfeld[ZEILEN][SPALTEN];
Das ist die einfachste Loesung die du machen kannst. Dein Code funktioniert nun ohne Fehler, weil du den einzelnen Funktionen immer ein Array mit der korrekten Zeilen- und Spaltenanzahl uebergibst, die sie auch erwarten. Einziger Nachteil ist halt, dass ungenutzter Speicher verbraucht wird, den du bei kleineren Feldern gar nicht nutzt.

Loesung 2
Wenn du wirklich ein Feld dynamischer Groesse erstellen willst, dann muesstest du ein paar mehr Stellen im Code aendern, und typischerweise das Feld dynamisch mit malloc auf dem Heap erstellen. Ich weiss nicht ob das schon deinem Wissensstand von 1-Jahr C-Kurs gerecht wird.

Ich habe frueher Computerspiele gebastelt mit 2D-Feldern, in denen ich die Daten in einem 1-Dimensionalen Array abgelegt habe, das mit malloc erzeugt wurde. Das ist meiner Meinung nach einfacher zu handeln als sich mit mehrdimensionalen Arrays in C rumzuaergern, eben weil selbige nicht fortlaufend im Speicher liegen.

Code:
int *bombenfeld;
bombenfeld = malloc(sizeof(int) * zeilen * spalten);
Hiermit erzeugen wir dynamisch auf dem Heap Speicher (den wir bei Programmende mit free wieder freigeben sollten) -- es ist einfach ein grosser zusammenhaengender Speicherbereich dynamischer Groesse, in dem wir zeilen * spalten Integer-Werte ablegen koennen. Ok, in deinem Beispiel koenntest du wahrscheinlich sogar auf malloc verzichten, indem du das 1D-Array dynamisch auf dem Stack anlegst, aehnlich wie du es bereits mit deinem 2D-Array machst.

Wie greifst du auf die einzelnen Elemente in diesem Speicherbereich zu?

Code:
bombenfeld[spalten * j + i]
Denk drueber nach. Ist eigentlich recht simpel. D.h. statt ein mehrdimensionales Array hast du ein ganz normales einfaches Array, und die jeweilige Zelle berechnest du dir einfach mit spalten * j + i.

Nur wenn du diesen Loesungsansatz waehlst, musst du wie gesagt alle Stellen im Code umschreiben wo auf dieses Array zugegriffen wird, weil dein Ansatz mit mehrdimensionalen Arrays kann damit nicht funktionieren. Alternative 3 waere, du machst eine Pointer auf Pointer Struktur ala int **bombenfeld, aehnlich wie argv -- dann koenntest du so auf das Array zugreifen wie bisher -- aber das ist von der Speicherverwaltung her etwas schwieriger als mein Loesungsansatz mit dem 1D-Array...

---------- Beitrag um 10:15 Uhr hinzugefügt ---------- Vorheriger Beitrag um 09:51 Uhr ----------

Selbst das Speichern von diversen Daten ist viel einfacher als gedacht, denn jede App hat zwei Verzeichnisse. Einmal für persistente und einmal für temporäre Dateien, die auch mal verloren gehen dürfen. So kann man mega leicht Sachen speichern.
Btw. iOS hat das gleiche Prinzip, jede App hat ein Cache-Verzeichnis das vom Betriebssystem geloescht werden kann, wenn der Speicher eng wird, und es gibt ein Verzeichnis wo man Daten speichert die wichtig sind und auch im Backup landen.
 
Zuletzt editiert:
PSN-Name: DZDomi
Vielen vielen vielen Dank chrizel! :okay:
Das hat mir echt super weiter geholfen.

Besonders die Variante mit malloc find ich sehr interessant da wir malloc in der Schule noch nicht durchgenommen haben und sie fuer mich irgendwie logischer klingt da der Adressbereich nacheinander angereiht wird. Ich werde mal versuchen das ganze mit malloc umzuschreiben :) Wenn sich das Programm beendet muss ich diesen Speicherbereich mit

Code:
free(bombenfeld)
einfach wieder freigeben hab ich das richtig verstanden? bzw bei der funktionsuebergabe bekommt die Funktion nur den anfangspointer sowie zeilen und spaltenazahl? Also so:

Code:
void feldFuellen(int feld[], int zeilen, int spalten, int bomben);
und ich kann in der Funktion dann in das Feld schreiben in dem ich einfach:

Code:
 bombenfeld[spalten * j + i]
verwende?

Aber nochmals Vielen Vielen Dank fuer die Hilfe!
 
Zuletzt editiert:
PSN-Name: DZDomi
es funktioniert auch mit int feld[] ;) aber ja ich weiss was du meinst ; ) und wieso muss ich das free(); eigentlich machen? gibt der computer den speicher nicht eh wieder frei wenn sich das Programm beendet?
Jetzt muss ich nur noch eine Moeglichkeit finden das wenn man ein 0 feld erwischt das dann alle felder rundherum aufgedeckt werden und falls es sich wieder um ein 0 feld handelt das da auch wieder alle aufgedeckt werden >.>
 
PSN-Name: chrizeliq
es funktioniert auch mit int feld[] ;) aber ja ich weiss was du meinst ; ) und wieso muss ich das free(); eigentlich machen? gibt der computer den speicher nicht eh wieder frei wenn sich das Programm beendet?
Klar, in deinem speziellen Programm macht es keinen Unterschied. Sobald dein Programm beendet wird, wird der komplette Prozess und sein gesamter Speicher freigegeben. Aber es ist ein besserer Stil, und wenn du dir jetzt angewoehnst dir schon Gedanken ueber das Freigeben und die Verwaltung von Speicher zu machen (etwas, was vielen Java-Only-Bastlern oft schwer faellt), dann hilft dir das spaeter weiter, wenn du mal groessere und komplexere Programme schreiben solltest, wo der richtige Umgang mit malloc und free sehr wohl eine sehr wichtige Rolle spielt.

Zum Thema Arrays als Pointer moechte ich anmerken, dass die Schreibweise array[3] einfach nur syntaktischer Zucker fuer die Schreibweise *(array + 3) ist (falls du schon den Umgang mit Pointern und Pointer-Arithmetik gelernt hast) -- aus dieser Tatsache heraus, kann man sogar 3[array] schreiben, weil der Compiler daraus einfach *(3 + array) macht. Aber das ist natuerlich kein ueblicher Stil und wuerde ich im Code nie verwenden, nur nice to know.
 
PSN-Name: DZDomi
ja ich wusste das feld[] gleichbedeutend mit *feld ist, hald ein pointer aufs erste element, aber das der kompiler auch 3[array] zulaest ist mir auch neu, interessant zu wissen ;)

hab jetzt minesweeper umgeschrieben mit malloc und es ist jetzt voll funktionsfähig, wer es einmal testen will hier der source:

http://pastebin.com/cgeX2xHv

Vielen Danke fuer die Hilfe nocheinmal chrizel! :okay:
 
Kleine Frage zu C++. Ich hab Memory Leaks und zwar an folgender Stelle:

Code:
    emitActiveVMsState(activeVMs->getVMsWithPort(FTPPORT)->getSize(), FTPPORT);
    emitFreeVMsSignal(freeVMs->getVMsWithPort(FTPPORT)->getSize(), FTPPORT);
Die Methode, die aufgerufen wird sieht so aus:
Code:
ServiceMap *ServiceMap::getVMsWithPort(int serviceport) {
	ServiceMap *vmsWithPort = new ServiceMap();
	for (service_iterator it = this->begin(); it != this->end(); it++) {
		if (it->second == serviceport) {
			vmsWithPort->addVM(it->first, it->second);
		}
	}
	return vmsWithPort;
}
d.h. ich bekomme diese Map zurückgeliefert, nutze sie beim Aufruf mit getSize(), aber lösche sie nicht, weil ich sie direkt nutze, ohne sie in eine Variable zu speichern.

Darum meine Frage, gibt es da einen Kniff, damit die Map gelöscht wird, oder muss ich überall so wie folgt die Methode aufrufen?

Code:
	    ServiceMap *webvms = freeVMs->getVMsWithPort(HTTPPORT);
	    ServiceMap *ftpvms = freeVMs->getVMsWithPort(FTPPORT);
	    emitFreeVMsSignal(webvms->getSize(), HTTPPORT);
	    emitFreeVMsSignal(ftpvms->getSize(), FTPPORT);
	    delete webvms;
	    webvms = NULL;
	    delete ftpvms;
	    ftpvms = NULL;
Es ist spät und falls es keinen Sinn macht darauf zu schieben ^^
 
Danke, kleine Anschlussfrage, ist beim Methodenaufruf dem Smart Pointer dann am Ende der Funktion klar, dass hier die Ownership endet, bzw. eigentlich schon direkt nach dem Aufruf? Da ja der Pointer nicht in eine Pointervariable geht, oder meinst du es so, dass ich mir zumindest die deletes spare? Ich will mal später so ein inline Beispiel suchen.
 
PSN-Name: chrizeliq
Danke, kleine Anschlussfrage, ist beim Methodenaufruf dem Smart Pointer dann am Ende der Funktion klar, dass hier die Ownership endet, bzw. eigentlich schon direkt nach dem Aufruf? Da ja der Pointer nicht in eine Pointervariable geht, oder meinst du es so, dass ich mir zumindest die deletes spare? Ich will mal später so ein inline Beispiel suchen.
Du kannst es genau so verwenden wie du es willst, und das laesst sich auch sehr einfach testen:
Code:
#include <iostream>
#include <memory>

using namespace std;

class Foobar
{
public:
  Foobar() { cout << "- constructor" << endl; }
  virtual ~Foobar() { cout << "- destructor" << endl; }
  int getCount() { return 42; }
};

auto_ptr<Foobar> doSomething()
{
  return auto_ptr<Foobar>(new Foobar());
}

int main(int argc, char *argv[])
{
  cout << "=== Inline ===" << endl;
  cout << doSomething()->getCount() << endl;

  cout << endl << "=== Ohne das Resultat zu verwenden ==" << endl;
  doSomething();

  cout << endl << "=== Mit Variable ===" << endl;
  auto_ptr<Foobar> foo = doSomething();
  cout << foo->getCount() << endl;

  cout << "- return 0" << endl;
  return 0;
}
Dieses Programm gibt folgendes aus:
Code:
=== Inline ===
- constructor
42
- destructor

=== Ohne das Resultat zu verwenden ==
- constructor
- destructor

=== Mit Variable ===
- constructor
42
- return 0
- destructor
Im ersten Fall wird vom Resultat direkt inline getCount aufgerufen, also im Grunde genau das was du machen willst. Wie du an der Ausgabe siehst, wird zuerst korrekt der Konstruktor aufgerufen, danach getCount aufgerufen und 42 ausgegeben, und erst danach korrekterweise der Destruktor. Im Grunde funktioniert es genau so wie man es erwartet, und man muss den Speicher nicht manuell verwalten.

Im zweiten Fall wird doSomething einfach so aufgerufen ohne das Resultat zu verarbeiten. Auch hier schuetzt uns der smart pointer davor, dass wir einen memory leak erzeugen: es wird sofort der Konstruktor und gleich danach automatisch der Destruktor aufgerufen, sobald doSomething() beendet wird.

Der dritte Fall speichert das Resultat von doSomething in einer eigenen Variable und arbeitet damit. Das Ownership geht also von doSomething nach main ueber, und das Objekt wird erst zerstoert wenn der Stack von main abgebaut wird d.h. main komplett beendet wurde.

Das ist ziemlich genial, da sieht man mal wieder wie maechtig C++ eigentlich ist. Im Grunde hast du hier eine automatische Speicherverwaltung, wo du dich nicht selber um die Speicherverwaltung kuemmern musst, und das ohne den Performance-Nachteilen einer Garbage Collection.
 
Danke dir, hatte mittlerweile alle von der Funktion betroffenen Teile umgeschrieben und das hier hat mir jetzt valgrind ausgegeben:

Vorher:
loss record of 1,488

==737== LEAK SUMMARY:
==737== definitely lost: 627,600 bytes in 11,788 blocks
==737== indirectly lost: 4,379,410 bytes in 69,315 blocks
==737== possibly lost: 51,781,718 bytes in 1,074,907 blocks
==737== still reachable: 885,591 bytes in 1,443

==737== ERROR SUMMARY: 532 errors from 532 contexts

Nachher:
loss record of 1,334

LEAK SUMMARY:
==6069== definitely lost: 383,336 bytes in 7,469 blocks
==6069== indirectly lost: 54,137,354 bytes in 1,108,019 blocks
==6069== possibly lost: 2,054 bytes in 58 blocks
==6069== still reachable: 885,592 bytes in 1,443

==6069== ERROR SUMMARY: 80 errors from 80 contexts
 
systems, systems, systems, systems, systems, systems
PSN-Name: Tenschi
Spielt gerade: PS5
hab mal ne frage an die Leute, die mit xcode apps programmieren.

ich habe vor, eine app zu programmieren. Diese soll aus mehreren taps bestehen, also habe ich eine tabbed application ausgewählt. Ich habe dann, die app vorerst designt und zwar im mainstoryboard.storyboard. Soweit hat ja alles geklappt, ich hab ja auch noch nicht programmiert, aber jetzt hab ich doch n etwas größeres problem.

Irgendwie bekomme ich keinen gescheiten tableView hin ... ich habe schon alles probiert ...
Ich weiß echt nicht woran es liegt, vllt. hab ich das falsche Projekt ausgewählt, also keine tabbed application sondern ne single view application oder so .... kP ...

Wär echt cool, wenn mir jemand helfen könnte, oder ein gutes Tutorial geben könnte ...

Ach, ich habe XCode 4.3
 
systems, systems, systems, systems, systems, systems
PSN-Name: Tenschi
Spielt gerade: PS5
ja schon, aber das haut nicht so hin wie ich will^^da kommen immer Fehlermeldungen -.- Also beim Ausführen zeigt er dann zwar build succeed aber nach 2 sek. kommt dann ne Fehlermeldung mit appdelegat.h oder so ...

#import <UIKit/UIKit.h>

#import "AppDelegate.h"

int main(int argc, char *argv[])
{
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
Fehlermeldung: Thread 1 Signal SIGABRT
 
Zuletzt editiert:
Top