Kann mir jemand erklären was die Variable s genau macht?

4 Antworten

Vom Fragesteller als hilfreich ausgezeichnet

Eigentlich eine elegante Lösung um zu Prüfen das alle eingaben getätigt wurden, auch wenn mit einem kleine Makel behaftet... siehe letzter Satz.

scanf gibt ja im Rückgabewert (hier an s) die Anzahl der erfolgreich gelesenen Argumente zurück ... und nicht ,wie man als nicht C bewanderter glauben könnte, den gelesenen Wert.
Im Beispiel wir jeweils 1 Argument abgefragt. Gibst Du statt 20 Enter ... 20 5 Enter ein, hast Du schon einen Rückgabewert von 2. Nach der nächsten Eingabe wäre es dann 3 oder größer.
Makel: wird die Zweite Eingabe in diesem Fall mit Enter ohne gültige Eingabe (0 Argumente gelesen) abgeschlossen ist s=2 ...trotz falscher Eingabe.

Gibst Du statt 20 Enter ... 20 5 Enter ein, hast Du schon einen Rückgabewert von 2.

Nein, es wird nur ein Wert umgewandelt, und " 5\n" bleibt im Puffer stehen. Für einen Rückgabewert von 2 brauchst du mindestens zwei %-Einträge im Format-Argument. Beispiel:

int h, m, s;
switch ( scanf("%d:%d:%d", &h, %m, &s) ) {
  case 3: return 3600*h+60*m+s; /* H:M:S */
  case 2: return 60*h+m;        /* M:S */
  case 1: return h;             /* S */
  default: return 0;
}
2
@ralphdieter

Wow, schick! Erinnert vom Stil her etwas an eine der gängigen Implementierungen von Duff's Device ... und das ist ausdrücklich als Lob zu verstehen! ;)

0
Eigentlich eine elegante Lösung um zu Prüfen das alle eingaben getätigt wurden, ...

Soooo elegant ist die Lösung gar nicht, mal ganz von dem von dir entdeckten Makel abgesehen.

Denn egal ob die erste Eingabe fehlschlägt, kommt erst mal die Aufforderung zur zweiten Eingabe, und zwar BEVOR es eine Fehlermeldung gibt.

In diesem Falle wäre der Einsatz von "goto" vermutlich die sauberste Lösung:

/*
gcc -std=c11 -Wall -Wextra -Wpedantic -Werror main.c && ./a.out
*/

#include <stdio.h> /* fflush, printf, scanf */
#include <stdlib.h> /* EXIT_FAILURE, EXIT_SUCCESS */

typedef enum {
	STATUS_OK,

	STATUS_ERROR_READ_SURFACE,
	STATUS_ERROR_READ_FORCE,

	STATUS_ERROR_VALUE_SURFACE,
	STATUS_ERROR_VALUE_FORCE,

	STATUS_UNKNOWN
} Status;

const char * Status_string(const Status status) {
	switch (status) {
		case STATUS_OK:
			return "no error";

		case STATUS_ERROR_READ_SURFACE:
			return "error reading surface";
		case STATUS_ERROR_READ_FORCE:
			return "error reading force";

		case STATUS_ERROR_VALUE_SURFACE:
			return "invalid surface value";
		case STATUS_ERROR_VALUE_FORCE:
			return "invalid force value";

		case STATUS_UNKNOWN:
			return "unknown status";
		default:
			return "invalid status code";
	}
}

Status do_magic(void) {
	Status status = STATUS_UNKNOWN;

	float surface = 0.0f;
	float force = 0.0f;

	printf("Surface: ");
	fflush(stdout);

	if (1 != scanf("%f", &surface)) {
		status = STATUS_ERROR_READ_SURFACE;
		goto out;
	}

	if (surface <= 0.0f) {
		status = STATUS_ERROR_VALUE_SURFACE;
		goto out;
	}

	printf("Max force: ");
	fflush(stdout);

	if (1 != scanf("%f", &force)) {
		status = STATUS_ERROR_READ_FORCE;
		goto out;
	}

	if (force <= 0.0f) {
		status = STATUS_ERROR_VALUE_FORCE;
		goto out;
	}

	/* TODO do something useful with the values */
	printf("surface -> %.3f\n", surface);
	printf("force -> %.3f\n", surface);

	status = STATUS_OK;

out:
	return status;
}

int main(void) {
	const Status status = do_magic();

	printf("Status: %s\n", Status_string(status));

	return STATUS_OK == status ? EXIT_SUCCESS : EXIT_FAILURE;
}

Das ganze Drumherum mit dem Status-enum usw. dient nur zur Verdeutlichung eines sinnvollen Einsatzes von "goto". Normalerweise würde ich auch noch die ganzen printf() und fflush() Aufrufe absichern, aber wollte jetzt den Rahmen nicht sprengen.

Wenn man sich diesen Stil angewöhnt, kann man sehr sehr sauberen, übersichtlichen, fehlerarmen und leicht erweiterbaren Code bauen. Das wäre ohne den Einsatz von "goto" so nicht möglich, weder annähernd so effizient, noch ohne sich in Verschachtelungen zu verrennen.

Leider wird Anfängern meist nur eingetrichtert, wie böse "goto" doch eigentlich ist, aber fast nie auf die sinnvollen Einsatzzwecke hingewiesen. Eigentlich sehr schade. Naja, zumindest gibt es kein nennenswertes größeres Open-Source-Projekt, welches den obigen Stil nicht verwendet, allen Unkenrufen zum Trotz. (Außer natürlich qualitativ schlechte Software, wie dem ollen PHP-Interpreter, oder so ... aber Projekte, die eine gute Codequalität haben, machen das eigentlich alle so wie im obigen Snippet, evtl. mit leichten Anpassungen und Modifikationen.)

0
@TeeTier

Wieso springst du nicht gleich mit return an den jeweiligen Stellen, wo du goto verwendest, aus der Methode heraus? Die Variable status würde, so wie ich das sehe, gar nicht benötigt werden.

2
@regex9

Weil normalerweise noch zwischen out-Label und return-Anweisung noch einige Funktionen stehen, um evtl. reservierten Speicher wieder frei zu geben, Dateien zu schließen, etc.

Das ist ja der Hauptgrund für den Einsatz von goto, kommt im obigen Code aber leider nicht gut rüber, da hast du natürlich Recht.

Außerdem steht direkt vor dem out-Label noch ein fail-Label und davor wiederum ein "goto out". Das hat den Vorteil, dass man sehr komfortabel unterschiedliche Ressourcen freigeben kann, je nach Erfolg oder Misserfolg.

Lange Rede kurzes Beispiel:

Status foobar(const char * outfile) {
  Status status = STATUS_UNKNOWN;

  char * buf = NULL;
  FILE * fh = NULL;

  if (!outfile) {
    status = STATUS_OUTFILE;
    goto fail;
  }

  buf = malloc(BUF_SZ);
  if (!buf) {
    status = STATUS_ALLOC;
    goto fail;
  }

  fh = fopen(outfile, "wb");
  if (!fh) {
    status = STATUS_IO;
    goto fail;
  }

  if (OK != do_something(fh, buf)) {
    status = STATUS_NOT_OK;
    goto fail;
  }

  /* success */
  status = STATUS_OK;
  goto out;

fail:
  /* roll back */

  unlink(outfile);

out:
  /* clean up */

  if (fh) {
    fclose(fh);
  }

  if (buf) {
    free(buf);
  }

  return status;
}

Das ist nicht perfekt, aber es sollte ersichtlich sein, wie das System funktioniert. Wenn man jede Funktion so aufbaut, ist sie trivial erweiterbar und der Aufwand für Änderungen skaliert linear.

Auch das ist allerdings noch nicht ganz der Stil, den ich normalerweise verwende. Normalerweise behandle ich z. B. den Aufruf von "do_something()" anders und fange auch Fehler nach den fail- und out-Labels ab, aber ich will hier jetzt bei diesem Snippet auch nicht übertreiben.

Auf jeden Fall solltest du jetzt grob erkennen können, warum ein einfaches "return" ausscheidet. :)

1
@regex9

Übrigens, da du ja auch zu großen Teilen mit Java arbeitest ...

An Stellen, die Zeitkritisch sind, und an denen oft viel schiefgehen kann, nutzt man diese C-Herangehensweise, um das langsame Exception-Handling zu umgehen und stattdessen mit Statusvariablen zu arbeiten.

Da Java das Schlüsselwort "goto" zwar reserviert, aber diese Funktionalität nicht eingebaut hat, muss man sich mit einem extrem hässlichen Knickei behelfen:

Status status = STATUS_UNKNOWN;

out: {
fail: {

  if (null == outfile) {
    status = STATUS_OUTFILE;
    break fail;
  }

  // ... more tests here ...

  // success
  status = STATUS_OK;
  break out;

} // fail

/* roll back */
// ...

} // out

/* clean up */
// ...

return status;

Ich habe das aber in Java ehrlich gesagt noch nie benutzt, deshalb hoffe ich, dass ich da jetzt keinen Fehler eingebaut habe. Aber ich habe das schon in Java-Spieleengines gesehen, bei denen es auf Geschwindigkeit ankommt. (Exceptions fressen - wenn geworfen - DEUTLICH mehr Systemressourcen, als ein Integer- oder Enum-Status mit Fehlercode!)

Disclaimer: Die Java-Version empfinde ich als alles andere als "schick". Die C-Variante mit "goto" macht einen viel sauberen Eindruck, zumindest in meinen Augen.

Aber wie gesagt ... sind alles nur Beispiele, also bitte keine Erbsen zählen. Ich weiß dass es dabei Verbesserungspotential gibt. :)

1

scanf() liefert keinen float zurück, sondern befüllt eine float-Referenz mit einem Wert. Wenn das geklappt hat liefert die Methode einen int zurück (1 oder 0, erfolgreich oder nicht erfolgreich).

Das heißt die Operation oben war nur erfolgreich, wenn s nachher 2 ist (sonst war einer von beiden Einlesevorgängen fehlerhaft).

scanf liefert die Anzahl der beschriebenen Elementen zurück.

da s mit 0 initialisiert wurde und bei den scanf's auf jeweils der dem rückgabewert erhöht wird, muss am Ende bei erfolgreichem einlesen s==2 sein

Das s==20 ist würde ich mal stark bezweifeln. Bitte zeige mir ein Foto aus dem Debugger, dass das so ist

Mein C Programm stürzt nach Eingabe ab....

Ich habe ein kleines Programm geschrieben das die Lösung einer Quadratischen Gleichung angibt, es ließt zuerst die Variablen a,b und c ein, und nachdem a eingescannt ist, stüzt das Prog. ab... Hat jemand einen Rat für mich? Die includes gehen mit dem code von gutefrage kaputt sorry...

include <stdio.h>
include <stdlib.h>
include <math.h>

int main(int argc, char **argv[])
{
    double a,b,c,x1,x2;
    double p = b/a;
    double q = c/a;
    double D = p*p/4-q;
    printf("Dieses Programm sagt dir wie viele Loesungen deine Quadratische Gleichung hat,\nund oder loest sie!\n");
    printf("Die Strucktur der Gleichung muss der Normalform entsprechen.\n Es werden die Variablen 'a,b und c' benoetigt.\n");
    printf("Bitte gebe den Wert a ein.\n");
    scanf("%lf,&a");
    fflush(stdin);
    printf("Bitte gebe den Wert b ein\n");
    scanf("%lf,&b");
    fflush(stdin);
    printf("Bitte gebe den Wert c ein\n");
    scanf("%lf,&c");
    fflush(stdin);

        if(D<0);
         printf("Es gibt keine reelle L\x94sung\n");
        if(D>0);
         x1 = -(p/2)+sqrt(D);
         x2 = -(p/2)-sqrt(D);        
         printf("Es gibt 2 reelle L\x94sungen:\n");
         printf("x1 = %lf\n",x1);
         printf("x2 = %lf\n",x2);
        if(D=0);
         printf("Es gibt eine reelle L\x94sung\n");
         printf("x = %lf\n",x1); 

getch();
return 0;
}
...zur Frage

Caldera Forms (WordPress)?

Kennt sich zufällig jemand mit dem Plugin Caldera Forms aus?

Wenn ich normalerweise eine Eingabe, von bspw. einem Textfeld überprüfen will, kann ich vor dem Absenden eine Datenvalidierung durchführen, bspw. mit document.getElementById().value.

Aber ich verstehe die Syntax bzw. die Logik bei Caldera Forms nicht wirklich. Ich habe mich versucht einzulesen und was ich an Informationen mitgenommen habe ist, dass ich einen Preprocessor brauche, oder?

Ich muss in der functions.php-Datei addfilter oder addfunction hinzufügen und dort meine Funktion schreiben. Aber wie genau hole ich dort aus meinem Formular die Werte meiner Felder? Oder wie kann ich eine Fehlermeldung in einem Feld ausgeben, wenn ein falscher Wert in einem anderen Feld eingegeben wurde?Oder wie kann ich von dort aus auf meine Datenbank zugreifen um mit diesen Daten eine Überprüfung durchzuführen?

Wäre wirklich über jede Hilfe dankbar.

...zur Frage

Raspberry Pi Servo Motor dreht sich beim ansteuern im Kreis ?

Hi,

Ich benutze einen Micro Servo SG90. Wenn ich ihn auf 90° stelle fährt er in Position. Wenn ich in jetzt in eine Richtung um nochmals 90° drehe (im Code = 180°) funktioniert es problemlos. In die andere Richtung (0 °) fährt er einmal komplett im Kreis.

Manchmal hört er gar nicht mehr auf zu drehen.

Woran kann das liegen, wie kann man das beheben?

Danke

...zur Frage

Wo ist der Fehler in diesem C-Programm?

Es soll der Benzinverbrauch eines Autos pro 100km aus verbrauchtem Kraftstoff in Litern und gefahrener Strecke in Kilometern berechnet werden. Das Programm hört nach der 2ten Eingabe auf und gibt "Drücken Sie eine beliebige Taste...aus.

include

main(){

double verbrauch,strecke;

printf("\n\tBenzinverbrauch");

printf("\n\nBitte verbrauchten Kraftstoff in Litern eingeben: ");

scanf("%lf",verbrauch);

printf("Bitte gefahrene Strecke in Kilometern eingeben: ");

scanf("%lf",strecke);

printf("\nIhr Auto verbraucht ");

printf("%lf",verbrauch*100/strecke);

printf(" Liter pro 100km.");

}

...zur Frage

Was ist der Unterschied zwischen: scanf("%i",&x); und scanf("%i",x);

Hi Leute frage steht zwar oben aber hier noch mal mit einem Beispiel programm:

include

int main() {

int x;

printf("\nBitte gebe eine Zahl ein\n");

scanf("%i",&x); //Hier meine ich den Unterschied zwischen scanf("%i",x);scanf("%i",&x);

printf("\nDu bist %i Jahre alt!",x);

return 0;

}

Also der Unterschied zwischen 'scanf("%i",&x);' und 'scanf("%i",x);'

Falls jemand rum meckern wollen würde (oder so) ich benutze nur Linux(ubuntu) KEIN windows!!!!

Danke für eure Hilfe

LG Niklas

...zur Frage

Programmieren lernen?

Wie kann man am besten Programmieren lernen. Bin schon seid längerer Zeit dabei und kann auch schon ein bisschen, aber ab jetzt komme ich nicht weiter. Hat jemand Tipps oder Empfehlungen für mich, die auch recht einfache Voraussetzungen haben?

...zur Frage

Was möchtest Du wissen?