Unschön oder falsch? mehrere Arrays mit einem Pointer beschreiben

mogbo

Banned
Hallo,
glaube das Programming Forum beanschlage ich jetzt offiziell für mich :)

Code:
...
struct node {
    char one[10];
...
    char ten[10];
    TAILQ_ENTRY(node) nodes;
};

struct node *order = NULL;

int
main(void)
{
...
    char *ptr;
    int i;

    order = malloc(sizeof(struct node));
    ptr = order;
    unsigned long space = sizeof(struct node) - sizeof(TAILQ_ENTRY(node));

    for (i = 0; i <= (int) space; i++) {
        ptr[i] = ' ';
    }
....
    return 0;
}

kann leider nicht meinen richtigen Code posten! Syntaxfehler ignorieren, mir gehts nur um den char-Pointer auf den struct node *order Pointer :)

Hintergrund ist, ich möchte eine Ascii Datei erstellen, welche in eine Datenbank überspielt werden muss. Die verschiedenen Einträge im struct node haben Offsets, diese würde ich gerne vor der Nutzung erstmal vollständig mit whitespaces überschreiben.

Meine Möglichkeit geht, ergibt aber folgende Fehlermeldung:
Code:
$ make ascii
cc  ascii.c  -o ascii
ascii.c: In function ‘main’:
ascii.c:78:9: warning: assignment from incompatible pointer type [-Wincompatible-pointer-types]
  ptr = order;
  ^
ascii.c:107:21: warning: assignment from incompatible pointer type [-Wincompatible-pointer-types]
  ptr = order;
  ^
ascii.c:308:13: warning: assignment from incompatible pointer type [-Wincompatible-pointer-types]
  ptr = order;

Gibts für sowas vielleicht einen Standart? Auch wenns klappt haben die warnings sicher einen Grund :)


Falls meine Frage unklar ist, bitte nachfragen
 
Zuletzt bearbeitet:
Ein struct node ist kein char, damit ist die Warnung vollkommen korrekt.
 
Indem du die Inhalte des Nodes setzt und nicht den Node selbst. :D

(Gerade arbeiten - später ggf. ausführlicher.)
 
Wieso einzeln? Zähl durch und mach eine Schleife - machst du doch schon!
 
Hallo @mogbo,

Du kannst einen Cast machen (ptr = (char *)order), aber der Code wird klarer, wenn Du die Offsets in eine Struktur packst, und diese dann initialisierst:

Code:
struct node {
     struct offset_s {
        char one[10];
         ...
        char ten[10];
    } offsets;
    TAILQ_ENTRY(node) nodes;
};
...
memset(&order->offsets, ' ', sizeof(order->offsets));
 
Code:
#include <stdio.h>
#include <stdlib.h>
#include <err.h>
#include <unistd.h>
#include <string.h>

#include <sys/queue.h>

struct node {
  struct offset *next;  // Pointer fuers offset
  struct offset {
      char one[3];
      char two[3];
      char three[3];
      char four[3];
  } offsets;
  TAILQ_ENTRY(node) nodes;
};


int
main(int argc, char *argv[])
{
  if (argc < 2)
      err(1, "argc < 2");

  TAILQ_HEAD(head_s, node) head;
  TAILQ_INIT(&head);

  int i;
  unsigned long space;
  struct node *order = NULL;


  for (i = 1; i < argc; i++) {  // ersten und letzten Eintrag ignorieren

      /* order->next auf struct offset setzen */
      order = malloc(sizeof(struct node));
      order->next = &(order->offsets);

      space = sizeof(order->offsets);

      /* order->next in der groesse von struct offset whitespace'n */
      memset((char *) order->next, ' ', space);

      snprintf((char *) order->next, sizeof(struct offset), "%s", argv[i]);
      TAILQ_INSERT_TAIL(&head, order, nodes);
      order = NULL;
  }

  /* print loop */
  TAILQ_FOREACH(order, &head, nodes) {
      write(1, order->next, space);
  }

  /* Speicher leeren */
  while (!TAILQ_EMPTY(&head)) {
      order = TAILQ_FIRST(&head);
      TAILQ_REMOVE(&head, order, nodes);
      free(order);
      order = NULL;
  }

  return 0;
}
Klappt :)

Output:
./a.out danke fuer die Hilfe
Code:
danke  fuer  die  Hilfe
(löscht leider paar whitespaces raus)
 
Hi,

#include <sys/queue.h>

ich fand diesen Tipp von das.chaos leider nicht hilfreich. Ich empfehle dir, diese API vorerst zu vergessen.

Der Grund ist einfach, dass du gerade programmieren lernen willst. Die ganzen komplexen Datentypen sollte man erst selbst ein paar Mal implementiert haben damit man sie überhaupt versteht. Desweiteren ist das Interface FreeBSD-spezifisch (analog zum Hinweis von Yamagi auf MAXPHYS aus param.h) - das willst du nicht.
Liebgemeinter Rat: du schießt mit Kanonen auf Spatzen. Versuche, deine zu lösenden Probleme besser zu analysieren.

Rob
 
ich fand diesen Tipp von das.chaos leider nicht hilfreich. Ich empfehle dir, diese API vorerst zu vergessen.
Fand TAILQ in der Anwendung fast leichter wie einen Pointer ans Ende der Struktur zu hängen und dort neu Speicher zu initialisieren, darum hab ich auf einen Tipp hin OpenBSD-style zu verwenden ( https://man.openbsd.org/style ) einfach auf TAILQ gewechselt (war nicht auf den Tipp von das.chaos hin).

Desweiteren ist das Interface FreeBSD-spezifisch (analog zum Hinweis von Yamagi auf MAXPHYS aus param.h) - das willst du nicht.
Hm, bisher verwende ich nur OpenBSD, bzw. Cygwin auf Win7 - was genau meinst du mit spezifisch? Das Thema MAXPHYS habe ich vorerst völlig hinten angestellt, geht mir etwas zu tief ins Detail :)

Liebgemeinter Rat: du schießt mit Kanonen auf Spatzen. Versuche, deine zu lösenden Probleme besser zu analysieren.
Danke
 
Du wirst außerhalb von BSD kein TAILQ finden, das ist "spezifisch".
 
Du wirst außerhalb von BSD kein TAILQ finden, das ist "spezifisch".
Code:
x@y ~/AHP
$ make test
cc  test.c  -o test

x@y ~/AHP
$ cat test.c
#include <stdio.h>
#include <stdlib.h>
#include <err.h>
#include <unistd.h>
#include <string.h>

#include <sys/queue.h>

struct node {
  struct offset *next;  // Pointer fuers offset
  struct offset {
  char one[3];
  char two[3];
  char three[3];
  char four[3];
  } offsets;
  TAILQ_ENTRY(node) nodes;
};


int
main(int argc, char *argv[])
{
  if (argc < 2)
  err(1, "argc < 2");

  TAILQ_HEAD(head_s, node) head;
  TAILQ_INIT(&head);

  int i;
  unsigned long space;
  struct node *order = NULL;


  for (i = 1; i < argc; i++) {  // ersten und letzten Eintrag ignorieren

  /* order->next auf struct offset setzen */
  order = malloc(sizeof(struct node));
  order->next = &(order->offsets);

  space = sizeof(order->offsets);

  /* order->next in der groesse von struct offset whitespace'n */
  memset((char *) order->next, ' ', space);

  snprintf((char *) order->next, sizeof(struct offset), "%s", argv[i]);
  TAILQ_INSERT_TAIL(&head, order, nodes);
  order = NULL;
  }

  /* print loop */
  TAILQ_FOREACH(order, &head, nodes) {
  write(1, order->next, space);
  }

  /* Speicher leeren */
  while (!TAILQ_EMPTY(&head)) {
  order = TAILQ_FIRST(&head);
  TAILQ_REMOVE(&head, order, nodes);
  free(order);
  order = NULL;
  }

  return 0;
}

x@y ~/AHP
$ uname -a
CYGWIN_NT-6.1 xxxxxx 2.9.0(0.318/5/3) 2017-09-12 10:18 x86_64 Cygwin
Stimmt so nicht ganz, oben ein Copy + Paste aus der Shell ( make + cat + uname -a )

EDIT: Einrückungen fehlen, da Leerzeichen im Editor entfernt werden :)
 
Außerdem sind die verschiedenen Queue-Varianten und ihr gestörter großer Bruder Tree keine Funktionen sondern Makros. Nun sind Makros in C eine etwas eklige Notwendigkeit, nicht zuletzt da die Sprache ja keine "richtigen" Generika unterstützt. Die Diskussion, wann und wie weit man Makros verwenden soll, ist dann auch gleich so alt wie der Präprozessor selbst... Aber: Zumindest bis man einigermaßen fest im Sattel sitzt und etwas Erfahrung gewonnen hat, würde ich von der exzessiven Nutzung von Makros abraten. Denn Makros bedeuten, dass der Code, den du im Editor siehst, nicht dem Code entspricht, den der Compiler vom Präprozessor übergeben bekommt. Das macht Debugging von Compiler- und Laufzeitfehlern innerhalb der Makros zur Herausforderung.
 
Ich erlaube mir noch ein paar Bemerkungen, die Dir hoffentlich helfen, @mogbo.

Code:
struct node {
    struct offset *next;  // Pointer fuers offset
     ...
     TAILQ_ENTRY(node) nodes;
};

Traditionell benutzt man einen next-Pointer für verkettete Listen. Dieser müsste sich aber dann in der offset-Struktur befinden. So würde er sich auf die node-Struktur beziehen, was einerseits nicht funktioniert, weil der Typ (struct offset) nicht mit struct node übereinstimmt, andererseits unnötig ist, da Du ja schon implizit durch TAILQ_ENTRY() Verweise auf den nächste und den vorherigen Knoten hast. Wenn Du next nur für die Referenz auf offsets benutzt, ist der Zeiger unnötig und verwirrend. Benutze stattdessen den Adressoperator & in memset() und entferne den next-Zeiger:

Code:
memset(&order->offsets, ' ', space);

Code:
// ersten und letzten Eintrag ignorieren
Der Kommentar stimmt nicht mit dem Code überein. Hier wird nur das erste Argument ausgelassen, was wohl auch Deiner Absicht entspricht.

Code:
write(1, order->next, space);
1 ist sicher auf den meisten Systemen der Dateideskriptor für die Standardausgabe, aber besser ist es, die Konstante STDOUT_FILENO zu benutzen, die neben STDIN_FILENO und STDERR_FILENO in unistd.h definiert ist.

Code:
order = NULL;
ist in Deinem Code unnötig. order wird gleich am Schleifenanfang initialisiert, und nach der for-Schleife gleich als Laufvariable von TAILQ_FOREACH() benutzt. Soll order nach der Schleife auf jeden Fall NULL sein, dann kann order gleich nach der Schleife auf NULL gesetzt werden. Das Gleiche gilt für die while-Schleife. Hier wird gleich am Anfang der Schleife order die Adresse TAILQ_FIRST(&head) zugewiesen. Nach der Schleife endet das Programm, ohne dass dabei noch mal Bezug auf den Zeiger order genommen wird.
 
for (i = 1; i < argc; i++) { // ersten und letzten Eintrag ignorieren
Code:
i <= argc
würde noch (null) ausgeben, deshalb der Kommentar.

1 ist sicher auf den meisten Systemen der Dateideskriptor für die Standardausgabe, aber besser ist es, die Konstante STDOUT_FILENO zu benutzen, die neben STDIN_FILENO und STDERR_FILENO in unistd.h definiert ist.
Vielen Dank, nehme ich so an

Code:
struct node {
    struct offset *next;  // Pointer fuers offset
     ...
     TAILQ_ENTRY(node) nodes;
};
Traditionell benutzt man einen next-Pointer für verkettete Listen. Dieser müsste sich aber dann in der offset-Struktur befinden. So würde er sich auf die node-Struktur beziehen, was einerseits nicht funktioniert, weil der Typ (struct offset) nicht mit struct node übereinstimmt, andererseits unnötig ist, da Du ja schon implizit durch TAILQ_ENTRY() Verweise auf den nächste und den vorherigen Knoten hast. Wenn Du next nur für die Referenz auf offsets benutzt, ist der Zeiger unnötig und verwirrend. Benutze stattdessen den Adressoperator & in memset() und entferne den next-Zeiger:

Code:
memset(&order->offsets, ' ', space);
In meiner Welt hat das wirklich Sinn gemacht :ugly:

1 ist sicher auf den meisten Systemen der Dateideskriptor für die Standardausgabe, aber besser ist es, die Konstante STDOUT_FILENO zu benutzen, die neben STDIN_FILENO und STDERR_FILENO in unistd.h definiert ist.
Klingt besser, das mit den Zahlen fand ich auch eher verwirrend. Danke


ist in Deinem Code unnötig. order wird gleich am Schleifenanfang initialisiert, und nach der for-Schleife gleich als Laufvariable von TAILQ_FOREACH() benutzt. Soll order nach der Schleife auf jeden Fall NULL sein, dann kann order gleich nach der Schleife auf NULL gesetzt werden. Das Gleiche gilt für die while-Schleife. Hier wird gleich am Anfang der Schleife order die Adresse TAILQ_FIRST(&head) zugewiesen. Nach der Schleife endet das Programm, ohne dass dabei noch mal Bezug auf den Zeiger order genommen wird.
Dachte ich mir auch schon öfters, kam aber in vielen Guides in dieser Form vor und dachte es wäre dann doch irgendwie sinnig. Gut das mal gesagt zu bekommen!

Außerdem sind die verschiedenen Queue-Varianten und ihr gestörter großer Bruder Tree keine Funktionen sondern Makros. Nun sind Makros in C eine etwas eklige Notwendigkeit, nicht zuletzt da die Sprache ja keine "richtigen" Generika unterstützt. Die Diskussion, wann und wie weit man Makros verwenden soll, ist dann auch gleich so alt wie der Präprozessor selbst... Aber: Zumindest bis man einigermaßen fest im Sattel sitzt und etwas Erfahrung gewonnen hat, würde ich von der exzessiven Nutzung von Makros abraten. Denn Makros bedeuten, dass der Code, den du im Editor siehst, nicht dem Code entspricht, den der Compiler vom Präprozessor übergeben bekommt. Das macht Debugging von Compiler- und Laufzeitfehlern innerhalb der Makros zur Herausforderung.
Dann lasse ich den style-Guide mal außen vor, wollte gleich einigermaßen "richtig" anfangen :)
Code:
Use queue(3) macros rather than rolling your own lists, whenever possible. Thus, the previous example would be better written:
https://man.openbsd.org/style

Eigene Strukturen fand ich ohnehin deutlich simpler (bzw. einfacher), mit dem Umstand kann ich gut leben :)
 
i <= argc

würde noch (null) ausgeben, deshalb der Kommentar.
Du hast argc Argumente: 0 (erstes Argument) bis argv - 1 (letztes Argument).

Eigene Strukturen fand ich ohnehin deutlich simpler (bzw. einfacher), mit dem Umstand kann ich gut leben
Wenn Du das Prinzip der verketteten Listen verinnerlicht und dieselben ein paar mal implementiert hast, wirst Du auf die queue-Makros wieder zurückkommen, und den Komfort, den sie bieten, zu schätzen wissen. Aber wie hier schon gesagt worden ist, ist es didaktisch sinnvoller, erst mal den Steinigen Weg zu gehen.
 
Zurück
Oben