struct pointer auf array

Message
Author
Tim_

struct pointer auf array

#1 Post by Tim_ »

Hallo zusammen!

Ich sitze jetzt schon seit Stunden vor einem Programm und finde den verdammten Fehler nicht.
Immerhin habe ich inzwischen alles etwas eingegrenzt und bin jetzt hoffentlich auf dem richtigen Weg.

Ich habe ein char array, darauf will ich einen struct pointer setzen.

char foo[32];
struct simplestruct *bar;
bar = (struct simplestruct *)foo;

Jetzt kann ich über bar->whatever auf die einzelnen Structvariablen zugreifen. Soweit sogut.
Jetzt möchte ich das ganze aber in einer Funktion betreiben während das ursprüngliche Array immer noch in der main ist.
Also habe ich sowas hier versucht:

func1(foo);

func1(char *fp)
{
struct simplestruct *bar;
bar = (struct simplestruct *)fp;
...
}

Ich vermute mal daß ich da noch einen Denkfehler drin habe, da ich erst vor kurzem mit C angefangen habe.
Zumindest hoffe ich inzwischen daß da der Fehler liegt weil mir sonst solangsam die Ideen ausgehen woran es noch liegen könnte.

Vielen Dank im Vorraus für jede Hilfe
Tim

Alex.MH

#2 Post by Alex.MH »

Aus der Entfernung sieht dein Code korrekt aus. Aber warum der Umweg ueber den char pointer? Warum nicht sowas wie:

struct simplestruct bar;
bar.whatever = value;
func1(&bar);

void func1(simplestruct* bar)
{
bar->whatever = newvalue;
}

Was ist denn nun genau dein Problem? Was geht denn nicht?

Gruss,
Alex

Tim_

#3 Post by Tim_ »

Hab den Code jetzt grade nicht hier, da ich unterwegs bin, aber im Grunde brauch ich sowas:

Ich hab ein int Array in dem bestimmte Werte gesammelt werden sollen.
Über eine Schleife bekomme ich jetzt immer wieder ein char Array.
Auf das packe ich mein struct und extrahiere dann einen neuen Int-Wert. Da kein Wert im Int Array doppelt vorkommen soll, muss geprüft werden ob es schon vorhanden ist.
Das Ganze hab ich versucht in eine Funktion zu packen, der dann ein char Pointer auf das char Array, ein int Pointer auf das int Array und ein Int mit der Anzahl der bisher gespeicherten Werte übergeben wird.

func(char *foo, int *bar, int x)

In der Funktion hab ich dann den neuen int Wert aus foo extrahiert, und dann sowas versucht:

for(i = 0; i < x; i++)
{
if(neuerWert == *(bar++))
{
return;
}
}
*bar = neuerWert;

Aber irgendwo ist der Wurm drin.

Gruß und danke schonmal!
Tim

Alex.MH

#4 Post by Alex.MH »

Hi,

Zuallererst: ein char pointer auf struct zu casten ist ein ziemlich unsauberer Stil. Was spricht dagegen ein Array aus structs zu nehmen?

Code: Select all

for&#40;i = 0; i < x; i++&#41; 
&#123; 
if&#40;neuerWert == *&#40;bar++&#41;&#41; 
&#123; 
return; 
&#125; 
&#125; 
*bar = neuerWert;
Dein Programm wird bei "*bar = neuerWert;" mit einem "Segmentation Fault" beendet, weil "bar" nach dem durchlaufen der for Schleife auf einen Bereich ausserhalb des Arrays zeigt. Du kannst nicht einfach an das array was dranhaengen, ohne vorher zusätzlichen Speicher zu reservieren.

Ausserdem wird der erste Eintrag deines Integer Arrays nie geprueft, weil "*(bar++)" dazu fuehrt, dass erst bar hochgezaehlt wird. Der Ausdruck in Klammern wird zuerst evaluiert und dann folgt die Dereferenzierung.

In C++ waere dein Problem ein Klacks und du muesstest dich auch nicht um die Speicherverwaltung kuemmern. Waere das eine Alternative fuer dich?

Gruss,
Alex

Tim_

#5 Post by Tim_ »

Erstmal schon vielen Dank für deine Hilfe!
ein char pointer auf struct zu casten ist ein ziemlich unsauberer Stil.
Naja, ich bekomme halt an der einen Stelle nur ein char Array rein, dann brauch ich das struct um die entsprechende Variable auszulesen und dann brauch ich ja nur noch selbige weshalb ich auf das int-Array verwenden wollte.
Wenn es da einen besseren/saubereren Weg gibt, immer her damit!

Ein Segmentation Fault bekomme ich nicht, da das int-array eine bestimmte Größe hat und in der Funktion zuerst die Anzahl der bisherigen Werte darauf geprüft wird, dass nichts überschritten wird.

Der Hinweis mit den Klammer war schonmal sehr hilfreich!
Ich hatte die nämlich echt nicht beachtet, sondern angenommen es wird erst dereferenziert, dann der Pointer erhöht so dass er aufs nächste Element zeigt.
Aber das geht dann wohl garnicht in einem Schritt sondern muss innerhalb der Schleife gemacht werden oder?

Da ich grade versuche einigermaßen mit C klarzukommen möcht ich erstmal nicht auf C++ ausweichen, sonst lern ich das ja nie :wink:

Gruß
Tim

Gast

#6 Post by Gast »

Richtig klar ist mir dein anfängliches Problem nicht.
Mit sauber usw. muss ich Alex_MH beipflichten, aber die Einstellung »Erstmal C« finde ich gut.

Code: Select all

#include <stdio.h>

char foo&#91;32&#93;;
/* struct simplestruct *bar;	*/


int func1&#40; char *fp &#41;
&#123;
  char a, b, c, *Pd;

  struct simplestruct &#123;
    char	a;
    char	b;
    char	c;
    char	d;
    /* ...	*/
  &#125; *bar;

  bar = &#40;struct simplestruct *&#41;fp;

  /* Wollte Tim_ so etwas?	*/
  a = bar->a;
  b = bar->b;
  c = bar->c;
  Pd = &&#40;bar->d&#41;;
  
  printf&#40; "a&#58; %c, b&#58; %c, c&#58; %c\n", a, b, c &#41;; 
  printf&#40; "Der Rest&#58; %s", Pd &#41;; 
&#125;


int main&#40; int argc, char* argv&#91;&#93; &#41;
&#123;
  int i;

  for&#40; i = 0; i < 32; foo&#91;i&#93; = 'a' + i, ++i &#41;;	/* richtig 33, aber ...	*/
  foo&#91; 32 &#93; = '\0';				/* ASCIIZ wegen	...	*/
  puts&#40; foo &#41;;					/* ... Bequemlichkeit	*/

  func1&#40; foo &#41;;

  return 0;
&#125;

Tim_

#7 Post by Tim_ »

Auch dir schonmal danke!

Ich habe das Problem inzwischen zumindest 'lokalisiert' auch wenn ich keine Ahnung habe warum das ganze da verrückt spielt. Mit dem struct hat es jetzt wohl garnichts zu tun, aber am besten ich poste einfach mal den Code:

Code: Select all

#include <stdio.h>
#include <string.h>
#include <netinet/ip.h>
#include <net/ethernet.h>


int find_client&#40;char *, int *, int&#41;;


int main&#40;void&#41;
&#123;
    int hosts&#91;256&#93;;
    memset&#40;&hosts, '\0', 256&#41;;
    int cnt = 0;
    
    int s, bytes;
    char buffer&#91;34&#93;;

    int i, h;

    s = socket&#40;AF_INET, SOCK_PACKET, htons&#40;0x3&#41;&#41;;
    if&#40;s == -1&#41;
    &#123;
        perror&#40;"failed to create socket"&#41;;
        return 1;
    &#125;

	for&#40;i = 0; i < 5; i++&#41;  /* zum testen 5 Packete sammeln */
	&#123;
    	if&#40;&#40;bytes = recv&#40;s, buffer, sizeof&#40;buffer&#41;, 0&#41;&#41; > 0&#41;
	    &#123;
	        if&#40;&#40;h = find_client&#40;buffer, &hosts&#91;0&#93;, cnt&#41;&#41; == 0&#41;
	        &#123;
	            cnt++;
	        &#125;
	    &#125;
	&#125;
    close&#40;s&#41;;
    
    for&#40;i = 0; i < cnt; i++&#41;
    &#123;
    	printf&#40;"&#40;%d&#41; host&#58; %s\n", i, hosts&#91;i&#93;&#41;;
    &#125;

    return 0;
&#125;




int find_client&#40;char *buf, int *hsts, int c&#41;
&#123;
	if&#40;c >= 255&#41;
	&#123;
		perror&#40;"maximum hosts found"&#41;;
		return 1;
	&#125;
	
    struct ether_header *eh;
    int p, i;

	eh = &#40;struct ether_header *&#41;buf;
	
	printf&#40;"-------BEFORE--------&#58; %s\n", *hsts&#41;;

    p = ether_ntoa&#40;eh->ether_shost&#41;;     
    
    printf&#40;"-------AFTER---------&#58; %s\n", *hsts&#41;;

	if&#40;&#40;int *&#41;p == NULL&#41;
	&#123;
		return 1;
	&#125;

    for&#40;i = 0; i < c; i++&#41;
    &#123;
    	if&#40;p == *hsts&#41;
    	&#123;
    		printf&#40;"host already known&#58; %s\n", p&#41;;
    		return 1;
    	&#125;
    	hsts++;
    &#125;
    *hsts = p;
    
    printf&#40;"new host found&#58; %s\n", *hsts&#41;;
    
    return 0;
&#125;
Das Ganze ist dazu gedacht, rauszufinden wer sich alles in meinem Netzwerk rumtreibt.
Dazu wird ein Rawsocket erzeugt, die Hardwareadresse des Senders bei allen Paketen ausgelesen und dann eben in dem Array gespeichert sofern sie nicht schon vorhanden ist.

Das wirklich seltsame ist, dass in Zeile 66 (zwischen dem BEFORE und AFTER) sich der Inhalt von hsts ändert. Warum ist mir absolut schleierhaft, da ich einfach keinen Bezug zu dem Pointer finden kann.
Es ist auch nicht so, dass der Pointer danach einfach nur irgendwelchen Datenmüll enthält, sondern er springt von der Senderadresse auf die Empfängeradresse um.

Ich habe inzwischen einfach keine Idee mehr, warum sich da was an dem Inhalt des Pointers ändern sollte, soweit ich das sehe komm ich in keinster Weise weder an den Pointer, noch an das worauf er zeigt...

Vielleicht hat ja einer von euch da ein bissl mehr Durchblick und kann mir mal auf die Sprünge helfen. Oder eventuell ist es auch ein lokales Problem und taucht bei euch garnicht auf?
Wie auch immer, wäre super wenn ihr mir helfen könntet!

Und wie schon weiter oben erwähnt bin ich auch immer daran interessiert, wie man das vielleicht hätte besser lösen können bzw wie ihr das angegangen wärt.

Gruß
Tim

Gast

#8 Post by Gast »

int hosts[256];
Das ist ein Array mit 257 Integern, nichts anderes. Bekommst Du keine Compiler-Warnungen? Gerade für den C-Übenden ist -Wall pflicht!. (An einigen Unis ist es wohl auch Mode, die Azubis zu striktem ANSI-C zu zwingen, wobei GCC (unter *x) als auch M$-C keine Warnungen schmeißen dürfen.)
Ob das nun die Ursache deines Problems ist, weiß ich jetzt nicht 100%ig

Tim_

#9 Post by Tim_ »

Irgendwie steh ich grade auf dem Schlauch.
Ich will doch ein int Array haben, was ist da also falsch dran??

Gast

#10 Post by Gast »

Ich dachte, es soll das aaray mit den Zeigern auf die hosts sein:
hosts[0] == 0:e0:7e:c7:72:71
hosts[1] == 0:e0:7e:c7:72:72
....
hosts[255] == MACdes256zigstenHosts

Aber auch das versteh' ich nicht:

memset(&hosts, '\0', 256);


So, nun zum Dilemma:

The ether_ntoa() function converts the Ethernet host address addr given
in network byte order to a string in standard hex-digits-and-colons
notation, omitting leading zeroes. The string is returned in a stati-
cally allocated buffer, which subsequent calls will overwrite.

Nach dem letzten Komma genau lesen!

Du mußt also den Inhalt auf den der Zeiger zeigt speichern (String kopieren) und nicht nur den Zeiger, weil »which subsequent calls will overwrite.«
_______________


Hab dein Prog. nun mal probiert:

# Erste Runde:
-------BEFORE--------:
-------AFTER---------:
# die naechsten 4:
-------BEFORE--------: 0:40:1:43:0:30
-------AFTER---------: 0:40:1:43:0:30

Gast

#11 Post by Gast »

Es wird schon interessanter, nur der 1. enthält noch Muell

(0) host: 0:40:1:43:0:30
(1) host: 0:0:e2:5b:d5:5d
(2) host: 0:e0:7d:b6:8b:bd

Tim_

#12 Post by Tim_ »

Hm, du hast natürlich recht, ich war irgendwie in der falschen Annahme, dass ether_ntoa einen int zurückliefert den ich dann in das Array packen könnte.
Keine Ahnung wie ich da draufgekommen bin.

Mit dem memset wollte ich das Array mit 0 initialisiern, da ich einerseits dachte das macht man bei 'sauberem' C so, und andererseits anfangs das Array mit einer Schleife durchgelaufen bin wo die Abbruchbedingung eben null war.

Ganz klar ist mir das Problem aber immer noch nicht.
Wenn ich dich richtig verstanden habe, speichere ich mit *hsts = p nicht den Wert selbst sondern nur die Adresse, welche aber bei einem neuen Aufruf von ether_ntoa überschrieben wird. Aber p ist doch kein Pointer, oder? Wie kann er dann eine Addresse repräsentieren??
Hm, ich hab das Gefühl ich hab da irgendwie was Grundlegendes noch net ganz verstanden.

Aber du meinst wenn ich das int Array durch ein char Array ersetze (und entsprechend vergrößere, 256 * Anzahl der Bytes die ether_ntoa zurückgibt) und anstelle von *hsts = p lieber strncpy(*hsts, p, anzahl) verwende sollte es gehen, richtig?

Vielen Dank, werde ich auf jeden Fall testen!!

Gast

#13 Post by Gast »

Quatsch, nur bei

Code: Select all

  printf&#40; "-------BEFORE--------  phsts&#91; %i &#93;&#58; %s\n"
        , cnt, phsts&#91; cnt &#93; &#41;;
  p = ether_ntoa&#40; &#40;struct ether_addr *&#41;eh->ether_shost &#41;;     
  printf&#40; "-------AFTER---------  phsts&#91; %i &#93;&#58; %s\n"
        , cnt, phsts&#91; cnt &#93; &#41;;
enthielt hosts[0] noch nichts, weil noch nicht String kopiert.
Mit (struct ether_addr *) die letzte Warnug weggecastet. Dies u. anderes geht bestimmt auch ordentlicher. Aber hier war ja Zielsetzung, wegen des lernes deinen Fehlern bei zu kommen, anstatt alles neu u. ordentlich zu machen.

Code: Select all

int find_client&#40; char *buf, char *phsts&#91;&#93;, int cnt &#41;
&#123;
 ...
  for&#40; i = 0; i < cnt; i++ &#41;
  &#123;
    Stringvergleich phsts&#91; i &#93;,  p
    &#123;
      printf&#40; "host already known&#58; %s\n", p &#41;;
      return 1;
    &#125;
    /*++phsts;*/
  &#125;
  strcpy ...
  printf&#40; "new host &#40;#%i&#41; found&#58; %s\n", cnt, phsts&#91; cnt &#93; &#41;;
   
  return 0;
&#125;
Noch ein paar main-Schnipsel, dann sollte es bei dir mit noch etwas Arbeit klappen

Code: Select all

int main&#40; int argc, char* argv&#91;&#93; &#41;
&#123;
  /*int hosts&#91;256&#93;;*/
  char* phosts&#91;255&#93;;
  /*memset&#40; &hosts, NULL, 256 &#41;;*/
  int cnt = 0, cnt_last = -1;
  ...
      if&#40; cnt != cnt_last &#41;	/* erster, bzw. wenn letzter belegt wurde */
      &#123;
        ... malloc fuer phosts&#91; cnt &#93; ...
        &#123;
          perror&#40; "Bitte Speicherriegel nachruesten!" &#41;;
          exit&#40; errno &#41;;
        &#125;
	cnt_last = cnt;
      &#125;
    
      if&#40; find_client&#40; buffer, phosts, cnt &#41; == 0 &#41;
      &#123;
        ++cnt;
      &#125;
  ...
[/code]

Gast

#14 Post by Gast »

Hatte dein letztes Posting 03. Feb 2006 22:34 nicht vor meinem 03. Feb 2006 22:48 gelesen.

Der String p ist ein Pointer auf:

(ntoa:) The string is returned in a statically allocated buffer

Es wird eben irgendwie als selbstverständlich angesehen, das Arrays (also auch Strings) Pointer sind.

C meint mit String die Adresse eines Char arrays. Die eckigen Klammern dereferenzieren, wieder referenziert gilt Adresse arr[0] == arr. Initialisierst Du eine Variable mit "abc" ist diese Variable eine Zeiger (auf das erste Element) von ( 'a', 'b', 'c', '\0' )

Gast

#15 Post by Gast »

»Mit dem memset wollte ich das Array mit 0 initialisiern,«
Verwirrt hat mich '\0', wenn integer, dann doch 0. Auch wenn '\0' selbiges ist,. soll es doch verdeutlichen das es sich um ein char-array handelt.

Sauber (Lob deinen Mühen):

pointer ohne allozierten Speicher auf NULL
strings ohne sinnvollen Inhalt auf Länge 0 terminieren cstr[0] = '\0' (bzw *cstr = '\0')

das kann oft helfen

Post Reply