You are here: Wissensbasis Web>UtilFaqInclude>UseStrict (19 Mar 2008)

Alle sagen, ich soll use strict verwenden, aber ich weiß gar nicht, was mir das bringt, und außerdem funktioniert mein Skript dann nicht mehr.

Inhalt:

Was ist dieses strict, von dem alle reden?

strict ist ein Perl-Pragma, welches dem Programmierer hilft, einen guten Programmierstil einzuhalten. Da Pragmas ebenso wie Module mit use eingebunden/aktiviert werden, nennt man dieses Pragma oft ausführlicher use strict statt bloß strict.

Im Gegensatz zu vielen anderen (hauptsächlich Nicht-Skript-) Sprachen lässt Perl dem Programmierer sehr viele Freiheiten und überprüft kaum, ob der gegebene Programmcode Sinn ergibt. Einige sehen dies als Vorteil von Perl. Jedoch führt es dazu, dass es viel leichter möglich ist, Flüchtigkeitsfehler zu machen, die in Verhaltensstörungen des Programms resultieren sich nur sehr schwer finden lassen. strict kann dabei helfen, dies zu vermeiden.

Wie verwendet man use strict?

Man schreibt einfach möglichst weit am Anfang seines Perl-Skriptes / Perl-Modules die Zeile use strict;. Ein gutes Perl-Skript fängt folgendermaßen an:
  #!/usr/bin/perl
  use strict;
  use warnings;

  #  Hier faengt das eigentliche Skript an
Die dritte Zeile ist dafür gut, dass nicht-fatale Warnungen ausgegeben werden, die auf mögliche Programmierfehler hinweisen. Das aber nur am Rande. Wir wollen uns hier hauptsächlich mit der zweiten Zeile beschäftigen.

In den folgenden Beispielen werden wir Zeilen 1 und 3 der Übersichtlichkeit halber fortlassen. Das bedeutet aber nicht, dass sie unwichtig sind (insbesondere "use warnings").

Warum funktioniert mein Skript dann nicht mehr?

use strict zu verwenden bedeutet aber einiges mehr, als nur am Anfang der Datei diese zwei Wörter zu notieren. Ein mögliches Anfängerskript ist
  $line = <STDIN>;
  chomp $line;
  print "Der Benutzer hat $line eingegeben.\n";
Würde man dieses folgendermaßen modifizieren
  use strict;
  $line = <STDIN>;
  chomp $line;
  print "Der Benutzer hat $line eingegeben.\n";
dann würde Perl sich weigern, das Skript auszuführen und ausgeben:
Global symbol "$line" requires explicit package name at foo.pl line 2.
Global symbol "$line" requires explicit package name at foo.pl line 3.
Global symbol "$line" requires explicit package name at foo.pl line 4.
Execution of foo.pl aborted due to compilation errors.
Dies ist aber kein Nachteil von strict sondern vielmehr ein Vorteil. Denn die Mission von strict ist es, einen gewissen Programmierstil aufzuzwingen, der von dem obigen Script aber nicht eingehalten wird.

Die Lösung dieses Problems ist das Wort "my":

  use strict;
  my $line = <STDIN>;
  chomp $line;
  print "Der Benutzer hat $line eingegeben.\n";

Warum my?

my ist eine Möglichkeit, die Variable zu deklarieren. In anderen Programmiersprachen tut man dies oft mit dem Schlüsselwort var oder in der Sprache C mit char *line.

Jeden Variable sollte (einmal) deklariert werden, bevor sie verwendet wird. Dazu schreibt man einfach

  my $line;
oder man weist dabei gleich einen Startwert zu, wie etwa in
  my $line = "Hallo";
oder
  my $line = <STDIN>;
Man kann ihren Wert auch gleich verwenden, so wie in
print my $iq = 27;
my $foo = my $bar = 30;

Warum der ganze Aufwand, wenn es auch ohne geht?

Das Tolle an use strict ist, dass es versehentlichen Programmierfehlern oder Tippfehlern vorbeugt. Folgendes Beispiel ohne use strict:
  $line = <STDIN>;
  chomp $line;
  print "Der Benutzer hat $lin eingegeben.\n"
Man beachte den Tippfehler in Zeile 3. Diese Skript wird stets "Der Benutzer hat eingegeben." ausgeben, egal was der Benutzer eingegeben hat. Der Gebrauch von use warnings würde schnell auf den Fehler aufmerksam machen:
  use warnings;
  $line = <STDIN>;
  chomp $line;
  print "Der Benutzer hat $lin eingegeben.\n";
Dies würde die Meldungen ausgeben:
Name "main::lin" used only once: possible typo at foo.pl line 4.
Use of uninitialized value in concatenation (.) or string at foo.pl line 4, <STDIN> line 1.
Nun bauen wir use strict und my ein:
  use strict;
  my $line = <STDIN>;
  chomp $line;
  print "Der Benutzer hat $lin eingegeben.\n";
So würde das Skript gar nicht erst ausgeführt werden, sondern abbrechen mit:
Global symbol "$lin" requires explicit package name at foo.pl line 5.
Execution of foo.pl aborted due to compilation errors.
Dies macht sofort auf den Tippfehler aufmerksam. Der Grund fuer den Abbruch ist, dass die Variable $line zwar deklariert wurde, $lin aber nicht.

Was tut my sonst noch?

Neben der Vorbeugung vor Tippfehlern ist my auch sinnvoll, weil der Wert der Variable verfällt, wenn die Gültigkeit der Deklaration vorüber ist (am Ende des Blocks). Damit wird Speicher freigegeben.

Ein weiterer Vorteil von der Verwendung von my ist zum Beispiel bei Rekursion bemerkbar. Für Variablen, die innerhalb einer Subroutine mit my deklariert werden, wird bei jedem Aufruf der Subroutine neuer Speicher alloziert. Das heißt, dass eine Variable mehrfach im Speicher vorhanden sein kann, jedoch unterschiedliche Werte und Lebensdauern hat.

Die Verwendung lokaler Variablen hat auch noch einen weiteren großen Vorteil: man braucht sich nicht mehr darum zu kümmern, ob die Variable, die man neu erzeugen will, schon in Verwendung ist, und man nicht mal durch Copy&Paste einem anderen Programmteil die Werte "wegschiesst". z.B.

my $line = 20;
while (my $line = <STDIN>) {
  print $line;
}
print "Endwert: $line\n"; # enthält den Wert von $line vor der Schleife
ist bezüglich der Variable $line von außen unabhängig, während
my $line = 20;
while ($line = <STDIN>) {
  print $line; # enthält das originale 20 von außerhalb der Schleife
}
print "Endwert: $line\n"; # enthält den Wert, der in der Schleife zugewiesen wurde
die "übergeordnete" Variable $line verändert, und somit als Endwert was anderes herauskommt.

Wo sollten Variablen deklariert werden? ("Scope")

Es ist schlechter Stil (und fehlerträchtig), alle Variablen am Anfang der Datei zu deklarieren. Man sollte es möglichst spät tun, also optimal erst beim ersten Gebrauch der Variable. Jedoch muss beachtet werden, dass die Deklarationen außerhalb des aktuellen Blockes (das nennt man typischerweise den Scope = Gültigkeitsbereich) nicht mehr gültig sind. Folgendes Skript ist zum Beispiel fehlerhaft:

  use strict;
  while (<STDIN>) {
    chomp(my $lastline = $_);
  }
  print "Die letzte Zeile war $lastline.\n";
Hier muss die Variable außerhalb der while-Schleife deklariert werden, damit sie auch nach der Schleife noch benutzt werden kann:
  use strict;
  my $lastline;
  while (<STDIN>) {
    chomp ($lastline = $_);
  }
  print "Die letzte Zeile war $lastline.\n";

Man muss auch beachten, dass nach my $variable; der Wert von $variable zunächst undefiniert ist. Wenn also innerhalb einer Schleife my $variable; steht, ist somit der Wert der Variable aus dem vorherigen Schleifendurchlauf nicht zugänglich. Dies kann umgangen werden, indem die Variable schon vor der Schleife mit my definiert wird.

Dass die Gültigkeit der my-Variablen sich bis zum Blockende erstreckt, kann unangenehm sein, wenn man einen länglichen Block schreibt (zum Beispiel ein langes lineares Script ohne Subroutinen, was aber eigentlich eh nicht so hübsch ist). Hier möchte man aus zwei Gründen vermeiden, dass die Variablen bis zum Ende leben.

  1. Alle Variablen belegen Speicher (den man mit undef wohl auch freigeben könnte),
  2. Ästhetische Gründe: Der Leser erkennt nicht, dass die Variable nicht mehr gebraucht wird, und es müllt den Namensraum zu.

Um Abhilfe zu schaffen, führt man oft "künstliche" Blöcke ein, die nur den Zweck haben, Variablengültigkeiten zu beenden. Beispiel:

{
    my $resolver = Net::DNS::Resolver->new;
    $resolver->send($hostname) or die "Cannot resolve hostname: " . $resolver->errorstring;
}
Der Sinn der geschweiften Klammern hier ist nur, die Lebensdauer der Variable $resolver zu begrenzen.

Oft sieht man auch Konstrukte mit do:

my $text = do {
    open my $f, '<', '/etc/motd' or die $!;
    <$f>;
};
Hier wurde ein Block eingeführt, um die Variable $f freizugeben. Dies hat auch den Nebeneffekt, dass die Datei wieder geschlossen wird. Dies ist auch gleichzeitig ein gutes Beispiel für den Gebrauch von lexikalischen Dateihandles.

Ergänzungen, Kommentare

Kommentare werden am besten in folgender Form vorgenommen, damit sie im Inhaltsverzeichnis angezeigt werden (natürlich ohne das <verbatim>):

---+++ Main.??? - 14 Jul 2003 - Betreff

--Main.ChristophBussenius - 16 Sep 2004

Weitere Features von strict (FrankCremers - 26 Jan 2005)

"strict" bietet die Möglichkeit, den Programmierer bei der Verwendung unsicherer Konstrukte in dreifacher Hinsicht zu beschränken:

   use strict 'refs';
   use strict 'vars';
   use strict 'subs';
Notiert man einfach
   use strict;
werden damit alle 3 Prüfungen bzw. Beschränkungen aktiviert.

Alles, was oben beschrieben wurde (nämlich die Überprüfung von Variablendeklarationen) bezog sich nur auf use strict 'vars'. Für Anfänger ist dies der wichtigste Teil von strict, weil sich die anderen beiden meist nur bei der Benutzung von komplizierten Features von Perl auswirken.

Die Aktivierung aller Prüfungen bietet zwar den größten Schutz, kann aber für bestimmte Aufgaben auch zu restriktiv sein. Daher können einzelne Prüfungen bei Bedarf auch wie folgt vorübergehend deaktiviert werden.

   no strict 'refs';
   no strict 'vars';
   no strict 'subs';
Aktivierungen und auch Deaktivierungen gelten jeweils von der Stelle, an der sie notiert sind bis zum Ende des direkt umschließenden Blocks. Eine teilweise Deaktivierung ist aber nur sinnvoll, wenn sie für den Programmablauf zwingend notwendig ist ... z.B. bei der Verwendung von symbolische Referenzen.

use strict 'refs';

vermeidet, dass man versehentlich symbolische Referenzen verwendet, obwohl man eigentlich eine fest Referenz verwenden wollte. Falls man bewusst symbolische Referenzen verwenden möchte, kann diese Prüfung mit
   no strict 'refs';
deaktiviert werden.

use strict 'vars';

vermeidet in erster Linie Schreibfehler bei Variablennamen und bricht mit einer aussagefähigen Fehlermeldung ab, wenn eine Variable weder mit my (Vorteile von my siehe oben) deklariert, noch importiert und auch nicht voll qualifiziert wurde.

   # Beipiel für eine voll qualifizierte Variable
     $paketname::variablenName = "was auch immer";

   # Beispiel für eine deklarierte Variable
     my $variablenName = "was auch immer";
  
   # Beispiel für eine importierte Variable
     use vars '$variablenName';

(Die Verwendung von my ist bei der Deklaration zwingend. Es kann nicht statt dessen local verwendet werden. Falls local verwendet werden soll, muss die Variable vorher mit use vars (pseudo)importiert worden sein.)

use strict 'subs';

vermeidet, dass man Subroutinen mit sog. "barewords" bezeichnet, wenn diese nicht ausdrücklich deklariert oder qualifiziert wurden. Anderenfalls würde man eine Subroutine z.B. mit einem freistehenden Wort wie tuirgendwas bezeichnen können. tuirgendwas könnte also ohne strict völlig OK ... aber ebenso - und das ist wahrscheinlicher - eine FALSCH geschriebene Variable sein, bei der das $ % oder @ vergessen wurde.

Fazit: strict reduziert die Fehlerhäufigkeit, erleichtert die Fehlersuche, macht Code lesbarer und fördert einen guten Programmstil. -- FrankCremers - 26 Jan 2005

Artikel für Beginner

Auf perl.com gibt es einen sehr guten Artikel für Beginner Doing It Right the First Time -- ReneeBaecker - 22 Feb 2005

Manches geht ohne Lokalisieren der Variable gar nicht!

Ein Beispiel, was ohne my nicht (richtig) funktioniert, ist die Rekursion. Ohne die Begrenzung der Gültigkeitsbereiche würden sich die Variablen immer selbst überschreiben und der "Zustand" des vorherigen Rekursionslevels wäre verloren.

Ein Beispiel wäre

#! /usr/bin/perl

$level=1;
do_recursion();
print $string,"\n";


sub do_recursion{
  return if($level == 5);
  $string = $level x $level;
  $level++;
  $string .= do_recursion();
}

Die dazugehörige Ausgabe:

44444444444444444444444444444444

Hier ist das Problem, dass $string immer wieder neu belegt wird. Ohne das strict (respektive my) gilt die Änderung global. Der vorherige Wert ist also komplett verloren gegangen...

und hier mit strict und my

#! /usr/bin/perl

use strict;

my $level=1;
my $string = do_recursion($level);
print $string,"\n";


sub do_recursion{
  my ($level) = @_;
  return if($level == 5);
  my $string = $level x $level;
  $level++;
  $string .= do_recursion($level);
}

Und die Ausgabe:

1223334444

Hier bleibt der "alte" Wert von $string bei jedem Methodenaufruf erhalten...

Zu beachten ist die Eintrittsvarianz.

Danke Ronnie für den Hinweis mit der Rekursion...

my vs. our

Viele Programmierer bemuehen sich, use strict und my einzusetzen, stossen dann aber auf Probleme, wenn sie ein Modul schreiben und von aussen auf die Variablen zugreifen wollen. my deklariert die Variable als gueltig fuer diese eine Datei.

Die Loesung fuer dieses Problem ist das Schluesselwort 'our'. Wenn man 'our' statt 'my' verwendet, dann klappt auch der Zugriff von aussen.

Mehr Infos in diesem Artikel im Linux-Magazin...

-- ReneeBaecker - 20 Apr 2005

-- SvenHergenhahn - 12.12.2005 - zwei Kommentare richtiggestellt (waren vertauscht)

Weitere Informationen

In perldoc strict stehen ausführliche Informationen über die Benutzung und Funktionsweise von strict.

UtilFaqSubForm edit

Titel Alle sagen, ich soll use strict verwenden, aber ich weiß gar nicht, was mir das bringt, und außerdem funktioniert mein Skript dann nicht mehr.
Autor ChristophBussenius
Bereich FaqAllgemeines
Tags
Topic revision: r18 - 19 Mar 2008 - 00:00:00 - ChristophBussenius
 
Bitte die NutzungsBedingungen beachten.
Bei Vorschlägen, Anfragen oder Problemen mit dem PerlCommunityWiki bitten wir um Rückmeldung.