Warum wird C verwendet, wenn es so gefährlich ist?


132

Ich überlege mir, C. zu lernen.

Aber warum verwenden die Leute C (oder C ++), wenn es "gefährlich" verwendet werden kann?

Mit gefährlich meine ich mit Zeigern und ähnlichem Zeug.

Wie die Stapelüberlauf-Frage Warum ist die gets-Funktion so gefährlich, dass sie nicht verwendet werden sollte? . Warum verwenden Programmierer nicht einfach Java oder Python oder eine andere kompilierte Sprache wie Visual Basic?


152
Warum verwenden Köche Messer, wenn sie "gefährlich" eingesetzt werden können?
Oerkelens

78
Mit großer Macht kommt große Verantwortung.
Pieter B

10
Joe Blow, viel Pontifikat?
Matthew James Briggs

4
Weil "Back In The Day", als C die Sprache der Wahl wurde, erwartet wurde, dass wir in der Lage sind, mit solchen Dingen umzugehen, weil wir das mussten. Interpretierte oder bytecodierte Sprachen waren zu langsam, weil die Prozessoren des Tages so viel langsamer waren. (Heute kann ich für 279 US-Dollar einen Low-End-Desktop-PC mit einer 2-GHz-Multi-Core-CPU und 4 GB Arbeitsspeicher von Dell kaufen . Sie haben keine Ahnung, wie unglaublich das für jemanden wie mich ist, für den ein 4- MHz- PC gilt mit 640 Kilobyte Speicher war Glückseligkeit ...). Seien Sie ehrlich - Moores Gesetz hat gewonnen. Spiel. Über!
Bob Jarvis

6
@ Bob Jarvis: Spiel nicht vorbei. Wenn Sie glauben, dass Ihr PC mit 2+ GHz und 4 GB oder Ihr Cluster mit mehreren hundert 4-GHz-PCs mit den neuesten CUDA-GPUs oder was auch immer schnell genug ist, arbeiten Sie einfach nicht hart genug an Problemen :-)
Jamesqf

Antworten:


246
  1. C ist älter als viele andere Sprachen, an die Sie denken. Vieles, was wir jetzt wissen, um das Programmieren "sicherer" zu machen, kommt aus der Erfahrung mit Sprachen wie C.

  2. Viele der sichereren Sprachen, die seit C herausgekommen sind, basieren auf einer größeren Laufzeit, einem komplizierteren Funktionsumfang und / oder einer virtuellen Maschine, um ihre Ziele zu erreichen. Infolgedessen ist C so etwas wie ein "kleinster gemeinsamer Nenner" unter allen populären / Mainstream-Sprachen geblieben.

    • C ist eine Sprache, die viel einfacher zu implementieren ist, da sie relativ klein ist und selbst in der schwächsten Umgebung mit größerer Wahrscheinlichkeit eine angemessene Leistung erbringt. Daher ist es für viele eingebettete Systeme, die ihre eigenen Compiler und andere Tools entwickeln müssen, wahrscheinlicher, dass sie einen funktionsfähigen Compiler bereitstellen für C.

    • Da C so klein und einfach ist, kommunizieren andere Programmiersprachen in der Regel über eine C-ähnliche API miteinander. Dies ist wahrscheinlich der Hauptgrund, warum C niemals wirklich sterben wird, selbst wenn die meisten von uns immer nur durch Wrapper damit interagieren.

  3. Viele der "sichereren" Sprachen, die versuchen, C und C ++ zu verbessern, versuchen nicht, "Systemsprachen" zu sein, mit denen Sie die Speichernutzung und das Laufzeitverhalten Ihres Programms nahezu vollständig steuern können. Zwar benötigen heutzutage immer mehr Anwendungen einfach nicht diese Kontrollebene, doch es wird immer eine kleine Handvoll Fälle geben, in denen dies erforderlich ist (insbesondere in den virtuellen Maschinen und Browsern, die all diese netten, sicheren Sprachen für das Internet implementieren) Wir Übrigen).

    Heutzutage gibt es einige Systemprogrammiersprachen (Rust, Nim, D, ...), die sicherer als C oder C ++ sind. Sie haben im Nachhinein den Vorteil, dass eine solche Feinsteuerung in den meisten Fällen nicht erforderlich ist. Bieten Sie daher eine allgemein sichere Schnittstelle mit ein paar unsicheren Hooks / Modi, auf die Sie bei Bedarf umschalten können.

  4. Sogar innerhalb von C haben wir eine Menge Regeln und Richtlinien gelernt, die dazu neigen, die Anzahl heimtückischer Fehler, die in der Praxis auftreten, drastisch zu reduzieren. Es ist im Allgemeinen unmöglich, den Standard dazu zu bringen, diese Regeln rückwirkend durchzusetzen, da dies zu viel vorhandenen Code beschädigen würde. Es ist jedoch üblich, Compiler-Warnungen, Linters und andere statische Analysetools zu verwenden, um diese Art von leicht vermeidbaren Problemen zu erkennen. Die Untermenge von C-Programmen, die diese Tools mit Bravour bestehen, ist bereits weitaus sicherer als "nur C", und jeder kompetente C-Programmierer wird heutzutage einige von ihnen verwenden.


Außerdem werden Sie niemals einen verschleierten Java-Wettbewerb so unterhaltsam machen wie den verschleierten C-Wettbewerb .


7
"kleinster gemeinsamer Nenner" klingt abfällig. Ich würde sagen, dass C im Gegensatz zu den vielen weitaus schwereren Sprachen keine Tonne zusätzliches Gepäck erzwingt, die Sie nicht benötigen, und Ihnen eine blitzschnelle Basis bietet, auf der Sie Dinge implementieren können, die Sie benötigen. Ist es das, was du meintest?
Underscore_d

9
"Man kann nicht wirklich eins ohne das andere haben.": Tatsächlich versuchen es viele neue Sprachen (Rust, Nim, D, ...). Dies läuft im Grunde darauf hinaus, eine "sichere" Teilmenge der Sprache mit ein paar "unsicheren" Primitiven abzugleichen, wenn diese Kontrollebene absolut notwendig ist. Alle bauen jedoch auf dem Wissen auf, das sich aus C und C ++ angesammelt hat. Vielleicht sollte man sagen, dass man zu der Zeit, als C und C ++ entwickelt wurden, keines ohne das andere haben konnte und heutzutage gibt es eine Sprache, die versucht, sich zu trennen ihre "unsicheren" Teile, aber sie haben noch nicht aufgeholt.
Matthieu M.

6
1) ist kein gültiger Punkt! C hat Merkmale aus Algol 68 übernommen, das in diesem Sinne eine "sichere" Sprache ist. so wussten die Autoren über solche Sprachen. Die anderen Punkte sind großartig.
Reinierpost

4
@MatthieuM. Vielleicht möchten Sie sich auch Microsoft Research ansehen. Sie haben in den letzten zehn oder zwei Jahren einige verwaltete Betriebssysteme entwickelt, darunter einige, die schneller sind (ganz zufällig - das Hauptziel der meisten dieser Forschungsbetriebssysteme ist die Sicherheit, nicht die Geschwindigkeit) als ein gleichwertiges nicht verwaltetes Betriebssystem für bestimmte reale Workloads . Die Beschleunigung ist hauptsächlich auf die Sicherheitsbeschränkungen zurückzuführen. Die statische und dynamische Überprüfung der ausführbaren Dateien ermöglicht Optimierungen, die in nicht verwaltetem Code nicht verfügbar sind. Alles in allem gibt es einiges zu sehen und Quellen sind enthalten;)
Luaan

3
@Luaan: Midori sieht so toll aus; Ich freue mich immer auf Joes Blog-Artikel.
Matthieu M.,

41

Erstens ist C eine Systemprogrammiersprache. Wenn Sie beispielsweise eine Java Virtual Machine oder einen Python-Interpreter schreiben, benötigen Sie eine Systemprogrammiersprache, um sie zu schreiben.

Zweitens bietet C eine Leistung, die Sprachen wie Java und Python nicht bieten. In der Regel werden beim Hochleistungsrechnen in Java und Python Bibliotheken verwendet, die in einer Hochleistungssprache wie C geschrieben sind, um das schwere Heben durchzuführen.

Drittens hat C eine viel kleinere Stellfläche als Sprachen wie Java und Python. Dies macht es für eingebettete Systeme verwendbar, die möglicherweise nicht über die erforderlichen Ressourcen verfügen, um die großen Laufzeitumgebungen und den Speicherbedarf von Sprachen wie Java und Python zu unterstützen.


Eine "Systemprogrammiersprache" ist eine Sprache, mit der sich industrietaugliche Systeme aufbauen lassen. Java und Python sind derzeit keine Systemprogrammiersprachen. "Genau das, was eine Systemprogrammiersprache ausmacht" ist nicht Gegenstand dieser Frage, aber eine Systemprogrammiersprache muss Unterstützung für die Arbeit mit der zugrunde liegenden Plattform bieten.

Andererseits muss eine Systemprogrammiersprache (als Antwort auf Kommentare) nicht selbsthostend sein. Dieses Problem trat auf , weil die ursprüngliche Frage gestellt „ warum die Leute C verwenden“, der erste Kommentar gefragt : „Warum würden Sie eine Sprache wie C benötigen“ , wenn Sie PyPy haben, und ich stellte fest , dass PyPy ist in der Tat C. verwenden Sie es also, war ursprünglich relevant für die Frage, aber leider (und verwirrend) ist "Selbsthosting" für diese Antwort nicht relevant. Es tut mir leid, dass ich es angesprochen habe.

Zusammenfassend lässt sich sagen, dass Java und Python nicht für die Systemprogrammierung geeignet sind, nicht weil ihre primären Implementierungen interpretiert werden oder weil nativ kompilierte Implementierungen nicht selbst gehostet werden, sondern weil sie nicht die erforderliche Unterstützung für die Arbeit mit der zugrunde liegenden Plattform bieten.


19
"Wenn Sie eine Java Virtual Machine oder einen Python-Interpreter schreiben, benötigen Sie eine Systemprogrammiersprache, um sie zu schreiben." Äh? Wie erklären Sie PyPy? Warum brauchen Sie eine Sprache wie C, um einen Compiler, einen Interpreter oder eine virtuelle Maschine zu schreiben?
Vincent Savard

10
Ich glaube, Sie haben behauptet, Sie benötigen eine Systemprogrammiersprache, um eine Java Virtual Machine oder einen Python-Interpreter zu schreiben, als Sie sagten: "Wenn Sie eine Java Virtual Machine oder einen Python-Interpreter schreiben, benötigen Sie eine Systemprogrammiersprache, um sie zu schreiben." Wenn PyPy Sie nicht zufrieden stellt, können Sie sich auch einen in Haskell geschriebenen Interpreter oder Compiler ansehen. Oder wirklich, fügen Sie einfach eine Referenz hinzu, die Ihren Anspruch stützt.
Vincent Savard

16
Sogar wenn es sich um Schildkröten handelt, könnte so etwas wie PyPy ohne einen Python-Interpreter, der in einer anderen Sprache geschrieben ist, nicht existieren.
Blrfl

18
@Birfl Aber das sagt eigentlich nicht viel. Ohne einen Assembly-Compiler hätte C nicht geschrieben werden können, und ohne Hardware, die es implementiert, können Sie keine Assembly schreiben.
Gardenhead

11
Wenn PyPy keine "Systemprogrammiersprache" ist, weil irgendwo ein C-Compiler beteiligt ist, dann ist C auch keine Systemprogrammiersprache, weil irgendwo ein Assembler verwendet wird. Ist es heutzutage nicht beliebt, C in eine andere Sprache zu übersetzen? zB LLVM

30

Es tut uns leid, noch eine Antwort hinzuzufügen, aber ich glaube, keine der vorhandenen Antworten spricht direkt Ihren ersten Satz an und lautet:

"Ich denke darüber nach, C zu lernen"

Warum? Möchten Sie die Dinge tun, für die C heutzutage normalerweise verwendet wird (z. B. Gerätetreiber, VMs, Game Engines, Medienbibliotheken, eingebettete Systeme, Betriebssystemkernel)?

Wenn ja, dann ja, lernen Sie C oder C ++, je nachdem, für wen Sie sich interessieren. Möchten Sie es lernen, damit Sie ein tieferes Verständnis dafür haben, was Ihre Hochsprache tut?

Anschließend erwähnen Sie die Sicherheitsbedenken. Sie brauchen nicht unbedingt ein tiefes Verständnis für sicheres C, um Letzteres zu tun, so wie ein Codebeispiel in einer höheren Sprache Ihnen das Wesentliche vermitteln könnte, ohne produktionsbereit zu sein.

Schreiben Sie einen C-Code, um das Wesentliche herauszufinden. Dann legen Sie es wieder in das Regal. Sorgen Sie sich nicht zu sehr um die Sicherheit, es sei denn, Sie möchten Produktions- C-Code schreiben .


2
Gute Arbeit bei der Beantwortung der eigentlichen Frage! Eine gute Möglichkeit, C / C ++ und "sicherere" Sprachen für alles zu schätzen, was sie wirklich sind, besteht darin, etwas wie eine einfache Datenbank-Engine zu schreiben. Sie werden ein gutes Gefühl für jeden Ansatz bekommen, den Sie versuchen, und sehen, wohin Sie das führt. Sie werden sehen, was sich in beiden Fällen natürlich anfühlt, und Sie werden feststellen, wo der natürliche Ansatz fehlschlägt (z. B. ist es sehr einfach, Rohdaten in C zu "serialisieren" - schreiben Sie einfach die Daten!), Aber das Ergebnis ist nicht portierbar kann von begrenztem Nutzen sein). Das Verständnis der Sicherheit ist schwierig, da die meisten Probleme schwer zu lösen sind.
Luaan

1
@Luaan genau zu lernen, wie man einen String mit Hilfe von Zeigern kopiert, bringt einen auf die Idee, zu lernen, wie man das sicher macht, ist eine andere Ebene und abhängig von den eigenen Zielen eine vielleicht unnötige.
Jared Smith

2
Das war nicht wirklich die Frage. Aber es war hilfreich, um eine Entscheidung zu treffen. Ich habe beschlossen, es zu tun. Ich bin bereit, mehr über das Innenleben von allem zu lernen, worauf man aufbaut. Und ich programmiere einfach gern. Auf diese Weise hoffe ich, die Funktionsweise von Computern besser zu verstehen. Es ist nur zum Spaß.
Tristan

10
Ich bin mit dem letzten Satz nicht einverstanden. Erfahren Sie, wie Sie es richtig machen, bevor Sie schlechte Gewohnheiten entwickeln.
Glglgl

2
@glglgl IDK, wenn ich ein JavaScript-Snippet im Web lese (oder schreibe), tue ich dies mit dem Verständnis, dass es nicht produktionsbereit ist: Es wird keine Ausnahmebehandlung haben, es könnte O (n ^ 2) usw. sein davon ist notwendig, um den Punkt zu vermitteln. All dies ist für den Produktionscode erforderlich. Warum ist das anders? Ich kann naives C für meine eigene Erbauung schreiben, während ich intellektuell verstehe, dass ich viel mehr arbeiten muss, wenn ich es dort veröffentlichen möchte.
Jared Smith

14

Dies ist eine RIESIGE Frage mit Tonnen von Antworten, aber die Kurzversion ist, dass jede Programmiersprache für verschiedene Situationen spezialisiert ist. Zum Beispiel JavaScript für das Web, C für Dinge auf niedriger Ebene, C # für alles Windows usw. Es ist hilfreich zu wissen, was Sie tun möchten, sobald Sie mit der Programmierung vertraut sind, um zu entscheiden, welche Programmiersprache Sie auswählen möchten.

Um Ihren letzten Punkt anzusprechen, warum C / C ++ über Java / Python, kommt es oft auf die Geschwindigkeit an. Ich mache Spiele, und Java / C # erreicht erst kürzlich Geschwindigkeiten, die gut genug sind, um Spiele auszuführen. Wenn Sie möchten, dass Ihr Spiel mit 60 Bildern pro Sekunde läuft und dass Ihr Spiel viel leistet (Rendering ist besonders teuer), müssen Sie den Code so schnell wie möglich ausführen. Python / Java / C # / Viele andere verwenden "Interpreter", eine zusätzliche Softwareschicht, die all die mühsamen Dinge handhabt, die C / C ++ nicht tut, z. B. das Verwalten von Speicher und die Garbage Collection. Dieser zusätzliche Aufwand verlangsamt die Arbeit, sodass fast jedes große Spiel, das Sie sehen, (jedenfalls in den letzten 10 Jahren) in C oder C ++ ausgeführt wurde. Es gibt Ausnahmen: Die Unity-Spiele-Engine verwendet C # * und Minecraft Java, aber dies ist die Ausnahme, nicht die Regel. Im Allgemeinen,

* Auch Unity ist nicht alles in C #, große Teile davon sind C ++ und Sie verwenden einfach C # für Ihren Spielcode.

BEARBEITEN Um auf einige der Kommentare zu antworten, die auftauchten, nachdem ich dies gepostet hatte: Vielleicht habe ich zu viel vereinfacht, ich habe nur ein allgemeines Bild gegeben. Bei der Programmierung ist die Antwort nie einfach. Es gibt Interpreter für C, Javascript kann außerhalb des Browsers ausgeführt werden und C # kann dank Mono auf fast allem ausgeführt werden. Verschiedene Programmiersprachen sind auf verschiedene Domänen spezialisiert, aber einige Programmierer haben wahrscheinlich herausgefunden, wie man eine Sprache in einem beliebigen Kontext zum Laufen bringt. Da das OP nicht viel über Programmierung zu wissen schien (Annahme meinerseits, sorry, wenn ich mich irre), versuchte ich, meine Antwort einfach zu halten.

In Bezug auf die Kommentare zu C #, das fast so schnell ist wie C ++, gibt es fast das Schlüsselwort. Als ich auf dem College war, haben wir viele Spielefirmen besucht, und mein Lehrer (der uns das ganze Jahr über dazu ermutigt hatte, von C # auf C ++ umzusteigen) hat Programmierer bei jeder Firma gefragt, warum C ++ über C # und bei jeder einzelnen sagte C # ist zu langsam. Im Allgemeinen läuft es schnell, aber der Garbage Collector kann die Leistung beeinträchtigen, da Sie nicht kontrollieren können, wann es ausgeführt wird, und er hat das Recht, Sie zu ignorieren, wenn es nicht ausgeführt werden soll, wenn Sie es empfehlen. Wenn Sie etwas brauchen, um eine hohe Leistung zu erzielen, möchten Sie nicht, dass etwas so unvorhersehbar ist.

Um auf meinen Kommentar zu antworten, dass ich gerade erst die Geschwindigkeit erreicht habe, sind viele der Geschwindigkeitssteigerungen in C # auf bessere Hardware zurückzuführen. Da sich jedoch das .NET Framework und der C # -Compiler verbessert haben, wurden einige Geschwindigkeitssteigerungen durchgeführt.

Über den Kommentar "Spiele sind in der gleichen Sprache wie die Engine geschrieben" kommt es darauf an. Einige sind, aber viele sind in einer Mischung von Sprachen geschrieben. Unreal kann UnrealScript und C ++, Unity C # Javascript und Boo, viele andere in C oder C ++ geschriebene Engines verwenden Python oder Lua als Skriptsprachen. Da gibt es keine einfache Antwort.

Und nur weil es mich nervte, zu lesen, "Wen interessiert es, ob Ihr Spiel mit 200 fps oder 120 fps läuft", wenn Ihr Spiel schneller als 60 fps läuft, verschwenden Sie wahrscheinlich CPU-Zeit, da der durchschnittliche Monitor dies nicht einmal aktualisiert schnell. Einige High-End- und neuere Versionen sind verfügbar, aber (noch) nicht Standard.

Und was die Bemerkung angeht, dass ich Jahrzehnte der Technik ignoriere, bin ich noch in den frühen 20ern. Wenn ich also rückwärts extrapoliere, spreche ich hauptsächlich von dem, was ältere und erfahrenere Programmierer mir erzählt haben. Natürlich wird das auf einer Seite wie dieser bestritten, aber es lohnt sich, darüber nachzudenken.


4
" C # für alles Windows " - Oh, das ist so ein Trugschluss. Und Sie geben sogar ein Beispiel. Einheit. AFAIK Es ist nicht geschrieben, es bietet C # API, weil die Sprache nett und anpassungsfähig ist. Es ist wirklich gut gestaltet. Und ich mag C ++ mehr, aber der Kredit sollte gegeben werden, wo es fällig ist. Vielleicht haben Sie C # mit .NET gemischt? Sie sind ziemlich oft zusammen.
Luk32

2
"Auch Unity ist nicht alles C #, große Teile davon sind C ++" Und? Spiele in Unity verwenden häufig C # und sind bereits seit geraumer Zeit auf dem Markt. Die Annahme, dass C # erst vor kurzem die Geschwindigkeit erreicht hat, erfordert entweder mehr Kontext oder läuft Gefahr, für diese jahrzehntelange Technologie blind zu sein.
NPSF3000

2
Fast jedes große Spiel wurde in der Sprache der verwendeten Engine geschrieben: Der Aufwand für das Duplizieren war so groß, dass keine andere technische Überlegung in Betracht gezogen werden musste. Das Rendern ist zwar teuer, aber heutzutage ist alles in Shadern geschrieben und die Sprache der Logikschleife ist irrelevant.
Peter Taylor

2
C # wurde schon immer mit JIT kompiliert (im Gegensatz zu Java, wo Ihr Kommentar korrekt ist), und es war von Anfang an in der Lage, C ++ sehr ähnliche Ausführungsgeschwindigkeiten zu bieten, wenn Sie wussten, was Sie taten. Das ist 2003 - ich würde es nicht für neu halten. Raw-Geschwindigkeit ist nicht das Hauptproblem für Spiele (insbesondere bei programmierbaren Shadern auf der GPU), es gibt andere Dinge, die Sprachen wie C # zuweilen mehr oder weniger populär machten. Zwei Hauptprobleme sind APIs (die stark C-orientiert sind und deren Schnittstellen möglicherweise teuer sind) und GC (hauptsächlich bei Latenzproblemen, nicht bei Rohdaten).
Luaan

1
@gbjbaanb Es ist nicht nur so, dass CPUs schneller sind - eine große Sache ist, dass C ++ und C Jahrzehnte Zeit hatten, um ihre Compiler und ihre Laufzeit zu perfektionieren, während Java im Grunde genommen von Null startete (hauptsächlich als Plattform für mehrere Plattformen konzipiert). Mit der Verbesserung der VM (z. B. Umstellung vom Interpreter auf einen JIT-Compiler, verbesserte GC ...) nahm auch die Leistung von Java-Anwendungen zu. Ein Großteil der Vorteile, die C / C ++ noch hat, liegt in dem Ansatz "Hoffen wir, dass nichts kaputt geht", bei dem viele Prüfungen vermieden werden, die als "unnötig" erachtet werden. Aber es ist immer noch ein großes Gedächtnisproblem - tatsächlich bedeuteten Verbesserungen der CPU-Auslastung oft eine schlechtere Speicherleistung :)
Luaan

13

Es ist lustig, dass Sie behaupten, C sei unsicher, weil "es Zeiger hat". Das Gegenteil ist der Fall: Java und C # haben praktisch nur Zeiger (für nicht native Typen). Der häufigste Fehler in Java ist wahrscheinlich die Null-Zeiger-Ausnahme (siehe https://www.infoq.com/presentations/Null-References-The-Billion-Dollar-Mistake-Tony-Hoare ). Der zweithäufigste Fehler ist wahrscheinlich, dass versteckte Verweise auf nicht verwendete Objekte (z. B. geschlossene Dialoge werden nicht entsorgt) vorhanden sind, die daher nicht freigegeben werden können, was zu lang laufenden Programmen mit einem ständig wachsenden Speicherbedarf führt.

Es gibt zwei grundlegende Mechanismen, die C # und Java auf zwei verschiedene Arten sicherer machen:

  • Durch die Garbage Collection ist es weniger wahrscheinlich, dass das Programm versucht, auf verworfene Objekte zuzugreifen. Dadurch wird die Wahrscheinlichkeit verringert, dass das Programm unerwartet beendet wird. Im Gegensatz zu C ordnen Java und C # nicht native Daten standardmäßig dynamisch zu. Dies macht die Programmlogik tatsächlich komplexer, aber die integrierte Speicherbereinigung übernimmt - zu einem hohen Preis - den schwierigen Teil.

Aktuelle C ++ - Zeiger erleichtern den Programmierern diese Arbeit.

  • Java und C # kompilieren zu einem Zwischencode, der von einer aufwendigen Laufzeit interpretiert / ausgeführt wird. Dies erhöht die Sicherheit, da die Laufzeit illegale Aktivitäten eines Programms erkennen kann. Auch wenn das Programm unsicher codiert ist (was in beiden Sprachen möglich ist), verhindert die jeweilige Laufzeit theoretisch ein "Ausbrechen" in das System.
    Die Laufzeit schützt nicht vor zB versuchten Pufferüberläufen, erlaubt aber theoretisch keine Exploits solcher Programme. Bei C und C ++ muss der Programmierer dagegen sicher codieren, um Exploits zu verhindern. Dies wird in der Regel nicht sofort erreicht, sondern erfordert Überprüfungen und Iterationen.

Es ist jedoch zu beachten, dass die aufwendige Laufzeit auch ein Sicherheitsrisiko darstellt. Es scheint mir, dass Oracle die JVM aufgrund neu entdeckter Sicherheitsprobleme alle paar Wochen aktualisiert. Es ist natürlich viel schwieriger , die JVM zu überprüfen als ein einzelnes Programm.

Die Sicherheit einer aufwändigen Laufzeit ist daher mehrdeutig und zu einem gewissen Grad trügerisch: Ihr durchschnittliches C-Programm kann mit Überprüfungen und Iterationen einigermaßen sicher gemacht werden. Ihr durchschnittliches Java-Programm ist nur so sicher wie die JVM. das ist nicht wirklich. Noch nie.

Der Artikel gets(), auf den Sie über diesen Link verweisen, spiegelt historische Bibliotheksentscheidungen wider, die heute anders getroffen würden, nicht die Kernsprache.


3
Ich denke, der Punkt des ursprünglichen Autors ist, dass Sie in C in der Lage sind, diese Zeiger ungeprüft zu bearbeiten. In Java erhalten Sie sofort eine nette Ausnahme - in C bemerken Sie möglicherweise nicht, dass Sie einen ungültigen Speicherort lesen, bis Ihr Anwendungsstatus beschädigt ist.
Sam Dufel

1
Auch stackoverflow.com/questions/57483/… - es wäre genauer zu sagen, dass Java "praktisch nur Referenzen" hat.
Sam Dufel

4
Ich habe alle Arten von interessanten Fehlern gesehen, die sich aus Versuchen ergeben, explizite Zeiger zu vermeiden. In der Regel ist das grundlegende Problem die Verwechslung von Kopie und Referenz. Wenn ich Ihnen in C einen Zeiger auf etwas übergebe, wissen Sie, dass sich das Ändern auf das Original auswirkt. Einige der Versuche, Zeiger zu vermeiden, verschleiern diese Unterscheidung und führen zu einem Labyrinth tiefer Kopien, flacher Kopien und allgemeiner Verwirrung.
Arlie Stephens

Die Behauptung, dass die JVM von Oracle (unter dem Gesichtspunkt der Hämorrhagie ausnutzbarer Sicherheitslücken) und damit die Laufzeiten verwalteter Sprachen im Allgemeinen mehr Sicherheitsbedenken aufwerfen als die Verwendung einer verwalteten Sprache, lässt den Schluss zu, dass Adobe Flash als Quelle der Unsicherheit und des damit verbundenen Programms schrecklich ist Muss die Wiedergabe von Videos und Animationen von einer Website von Natur aus unsicher sein? Nicht alle Java-Laufzeiten sind annähernd so schlecht wie die JVM-Abscheulichkeiten von Oracle / Sun aus den 1990er-Jahren, und nicht alle verwalteten Sprachen sind Java. (Nun, offensichtlich.)
Meistens am

@halfinformed Gut; Ich sagte, dass ein Programm nur so sicher ist wie seine Laufzeit, und dass "Ihr durchschnittliches" Programm (gelesen: klein) mit einer Anstrengung sicherer gemacht werden kann als jede große Laufzeit wie ein Bytecode-Interpreter. Diese Aussage scheint unbestreitbar. Ob ein bestimmtes Standalone-Programm oder eine bestimmte Laufzeit mehr oder weniger sicher ist, hängt von ihrer jeweiligen Komplexität und Design-, Codierungs- und Wartungsqualität ab. Ich würde zum Beispiel nicht sagen, dass sendmail sicherer ist als die Java-VM von Oracle. aber qmail kann sein.
Peter A. Schneider

10

Weil "Sicherheit" Geschwindigkeit kostet, arbeiten die "sichereren" Sprachen langsamer.

Sie fragen, warum Sie eine "gefährliche" Sprache wie C oder C ++ verwenden, jemand Ihnen einen Grafiktreiber oder ähnliches in Python oder Java usw. schreiben lassen und sehen, wie Sie sich in Bezug auf "Sicherheit" fühlen :)

Im Ernst, Sie müssen so nah am Hauptspeicher der Maschine sein, um Pixel, Register usw. manipulieren zu können. Java oder Python können dies nicht mit jeder Art von leistungsfähiger Geschwindigkeit ... C und C ++ beides Ermöglichen Sie dies durch Zeiger und dergleichen zu tun ...


20
Es ist im Allgemeinen nicht wahr, dass Sicherheit Geschwindigkeit kostet . Die sichersten verfügbaren Sprachen führen die meisten Prüfungen zur Kompilierungszeit durch. O'Caml, Ada, Haskell, Rust liegen alle nicht weit hinter C, was die durchschnittliche Laufzeitgeschwindigkeit betrifft. Was sie tun entstehen in der Regel ist erheblicher Aufwand in Programmgröße, Speichereffizienz, Latenz und offensichtlich Zeit kompilieren. Und, ja, sie haben Schwierigkeiten mit metallähnlichen Dingen. Aber das ist kein wirkliches Geschwindigkeitsproblem.
links um ca.

6
Außerdem macht C nicht das, was Sie denken. C ist eine abstrakte Maschine . Es gibt dir keinen direkten Zugang zu irgendetwas - das ist eine gute Sache. Sie können sich nicht einmal die moderne Baugruppe ansehen, um zu sehen, wie viel C sich vor Ihnen verbirgt - die moderne Baugruppe (z. B. TASM) wäre bei der Entwicklung von C als Hochsprache angesehen worden. Ich würde mich sehr freuen, wenn jemand Treiber in einer "sicheren" Sprache geschrieben hätte, danke - das würde helfen, viele dieser BSODs und Einfrierungen zu vermeiden, ganz zu schweigen von Sicherheitslücken :) Und vor allem gibt es Systemsprachen, die viel sicherer sind als C.
Luaan

3
@Wintermute Sie möchten Rust unbedingt nachschlagen, bevor Sie sich dazu äußern, wie schnell Sicherheitsfunktionen sein müssen. Hölle, niedriges Niveau Typ - System C tatsächlich hemmt viele sehr nützliche Optimierungen , die Compiler sonst tun könnten ( vor allem , wenn man bedenkt , dass so ziemlich kein großes c - Projekt nicht zu verletzen strenges Aliasing zu vermeiden , schafft irgendwo ).
Voo

7
@Wintermute Ja, der Mythos, dass man C / C ++ nicht sicherer machen kann, ohne einen Performance-Overhead einzuführen, ist sehr hartnäckig, weshalb ich das ziemlich ernst nehme (es gibt einige Bereiche, in denen dies mit Sicherheit zutrifft [Grenzwertprüfung]). Warum ist Rust nicht weiter verbreitet? Geschichte und Komplexität. Rust ist noch relativ neu und viele der größten in C geschriebenen Systeme existierten, bevor Rust erfunden wurde - Sie werden nicht eine Million LOC in einer neuen Sprache schreiben, selbst wenn es viel sicherer wäre. Auch jeder Programmierer und sein Hund kennt C, Rust? Viel Glück, genug Leute zu finden.
Voo

3
@Voo Dogs tun C? ... kein Wunder, dass ich so viel schlechten Code da draußen gesehen habe ... j / k Punkt über Rust genommen (Ich habe es gerade heruntergeladen und installiert, so dass Sie möglicherweise einen anderen Konverter haben, um hinzuzufügen) Übrigens in Bezug zu Rust macht es richtig ... Ich könnte das gleiche Augment für "D" machen :)
Wintermut3

9

Neben alledem gibt es auch einen ziemlich häufigen Anwendungsfall, bei dem C als gemeinsame Bibliothek für andere Sprachen verwendet wird.

Grundsätzlich haben fast alle Sprachen eine API-Schnittstelle zu C.

Einfaches Beispiel: Versuchen Sie, eine gemeinsame Anwendung für Linux / IOS / Android / Windows zu erstellen. Abgesehen von all den Tools, die es gibt, haben wir am Ende eine Kernbibliothek in C erstellt und dann die GUI für jede Umgebung geändert:

  • IOS: ObjectiveC kann C-Bibliotheken nativ verwenden
  • Android: Java + JNI
  • Linux / Windows / MacOS: Mit GTK / .Net können Sie native Bibliotheken verwenden. Wenn Sie Python, Perl oder Ruby verwenden, verfügen alle über native APIs-Schnittstellen. (Java wieder mit JNI).

Meine zwei Cent,


Einer der Gründe, warum ich PHP so gerne benutze, ist, dass fast alle seine Bibliotheken tatsächlich in C geschrieben sind - zum Glück, oder PHP wäre unerträglich langsam :) PHP ist großartig, um schlampigen Code zu schreiben, ohne befürchten zu müssen Ich mache alles, was 'gefährlich' ist (und deswegen schreibe ich viel mehr PHP-Code als alles andere - ich mag schlampigen Code!: D), aber es ist schön zu wissen, dass sich unter vielen dieser Funktionsaufrufe die guten alten Vertrauenswürdigen befinden C-Bibliotheken, um die Leistung zu steigern ;-) Im Gegensatz dazu ist das Schreiben von Sloppy-Code in C ein großes No-No ...
Gwyneth Llewelyn

7

Eine grundlegende Schwierigkeit bei C besteht darin, dass der Name verwendet wird, um eine Reihe von Dialekten mit identischer Syntax, aber sehr unterschiedlicher Semantik zu beschreiben. Einige Dialekte sind viel sicherer als andere.

In C, wie es ursprünglich von Dennis Ritchie entworfen wurde, würden C-Anweisungen im Allgemeinen in vorhersagbarer Weise auf Maschinenbefehle abgebildet. Da C auf Prozessoren ausgeführt werden kann, die sich anders verhalten, wenn ein vorzeichenbehafteter arithmetischer Überlauf auftritt, weiß ein Programmierer, der nicht weiß, wie sich eine Maschine im Falle eines arithmetischen Überlaufs verhält, auch nicht, welcher C-Code auf dieser Maschine ausgeführt wird Wenn bekannt ist, dass sich eine Maschine auf eine bestimmte Art und Weise verhält (z. B. ein stiller Wraparound mit zwei Komplementen), können Implementierungen auf dieser Maschine in der Regel ebenfalls ausgeführt werden. Einer der Gründe, warum C für seine Schnelligkeit bekannt war, war, dass in Fällen, in denen Programmierer wussten, dass das natürliche Verhalten einer Plattform in Edge-Case-Szenarien ihren Anforderungen entspricht, der Programmierer oder Compiler keinen Code schreiben musste, um solche Szenarien zu generieren .

Leider haben Compiler-Autoren die Ansicht vertreten, dass Compiler sich frei fühlen sollten, Code zu generieren, der Gesetze negiert, da der Standard in solchen Fällen keine Anforderungen an die Implementierung stellt (Laxität, die Hardware-Implementierungen ermöglichen sollte, die sich möglicherweise nicht vorhersehbar verhalten) von Zeit und Kausalität.

Betrachten Sie etwas wie:

int hey(int x)
{
   printf("%d", x);
   return x*10000;
}
void wow(int x)
{
  if (x < 1000000)
    printf("QUACK!");
  hey(x);    
}

Hypermoderne (aber modische) Compilertheorie würde vorschlagen, dass der Compiler "QUACK!" Bedingungslos, da in jedem Fall, in dem die Bedingung falsch war, das Programm undefiniertes Verhalten aufrief und eine Multiplikation durchführte, deren Ergebnis ohnehin ignoriert werden würde. Da der Standard einem Compiler in einem solchen Fall erlauben würde, alles zu tun, was er möchte, kann der Compiler "QUACK!" Ausgeben.

Während C früher sicherer war als die Assemblersprache, ist bei der Verwendung von hochmodernen Compilern das Gegenteil der Fall. In der Assemblersprache kann ein Ganzzahlüberlauf dazu führen, dass eine Berechnung zu einem bedeutungslosen Ergebnis führt. Auf den meisten Plattformen ist dies jedoch das Ausmaß der Auswirkungen. Wenn die Ergebnisse trotzdem ignoriert werden, spielt der Überlauf keine Rolle. In hypermodernem C können jedoch auch normalerweise "harmlose" Formen von Undefiniertem Verhalten (wie ein ganzzahliger Überlauf in einer Berechnung, der letztendlich ignoriert wird) eine willkürliche Programmausführung verursachen.


1
Selbst in einem hochmodernen Compiler überprüft C Arrays nicht auf ihre Grenzen. Wenn dies der Fall wäre, wäre dies nicht mit der Definition der Sprache vereinbar. Ich benutze diese Tatsache manchmal, um Arrays mit einem zusätzlichen Zeiger in der Mitte des Arrays zu machen, um negative Indizes zu haben.
Robert Bristow-Johnson

1
Ich würde gerne Beweise dafür sehen, dass Ihr Beispiel "QUACK!" bedingungslos. x kann zum Zeitpunkt des Vergleichs sicherlich größer als 1000000 sein, und eine spätere Auswertung, die zu einem Überlauf führen würde, verhindert dies nicht. Wenn Sie Inlining aktiviert haben, das das Entfernen des überlaufenden Multiplikators ermöglicht, ist Ihr Argument über implizite Bereichsbeschränkungen nicht zutreffend.
Graham

2
@ robertbristow-johnson: Eigentlich sagt der Standard ganz explizit, dass ein gegebener int arr[5][[5]Zugriffsversuch z. B. arr[0][5]undefiniertes Verhalten zur Folge hat. Eine solche Regel macht es möglich, dass ein Compiler, dem so etwas wie arr[1][0]=3; arr[0][i]=6; arr[1][0]++;der Schluss gegeben arr[1][0]wird, gleich 4 ist, ohne Rücksicht auf den Wert von i.
Supercat

2
@ robertbristow-johnson: Auch wenn der Compiler Arrays innerhalb einer Struktur sequentiell ohne Lücken zuordnet, kann nicht garantiert werden, dass die Indizierung eines der Arrays sich auf ein anderes auswirkt. Unter godbolt.org/g/Avt3KW finden Sie ein Beispiel dafür, wie gcc solchen Code behandelt.
Supercat

1
@ robertbristow-johnson: Ich habe die Versammlung kommentiert, um zu erklären, was sie tut. Der Compiler sieht, dass Code 1 in s-> arr2 [0] speichert und dann s-> arr2 [0] inkrementiert. Daher kombiniert gcc diese beiden Operationen, indem Code einfach den Wert 2 speichert, ohne die Möglichkeit zu berücksichtigen, dass der Schreibvorgang dazwischen liegt to s-> arr1 [i] könnte den Wert von s-> arr1 [0] beeinflussen (da dies laut Standard nicht möglich ist).
Supercat

5

Historische Gründe. Ich kann nicht oft brandneuen Code schreiben, meistens kann ich das alte Zeug, das seit Jahrzehnten läuft, warten und erweitern. Ich bin nur froh, dass es C ist und nicht Fortran.

Ich kann mich ärgern, wenn ein Student sagt: "Aber warum machst du dieses schreckliche X, wenn du Y machen könntest?". Nun, X ist der Job, den ich habe, und er zahlt die Rechnungen sehr gut. Ich habe gelegentlich Y gemacht und es hat Spaß gemacht, aber X ist das, was die meisten von uns tun.


5

Was ist "gefährlich"?

Die Behauptung, C sei "gefährlich", ist ein häufiges Gesprächsthema in Sprachflammenkriegen (am häufigsten im Vergleich zu Java). Die Beweise für diese Behauptung sind jedoch unklar.

C ist eine Sprache mit bestimmten Funktionen. Einige dieser Funktionen können bestimmte Arten von Fehlern zulassen, die von anderen Arten von Sprachen nicht zugelassen werden (das Risiko der Speicherverwaltung von C wird normalerweise hervorgehoben). Dies ist jedoch nicht das gleiche wie ein Argument , das C gefährlicher als andere Sprachen ist insgesamt . Mir ist nicht bekannt, dass jemand in diesem Punkt überzeugende Beweise liefert.

Ebenfalls, „gefährlich“ ist abhängig von Kontext: Was Sie versuchen zu tun, und welche Arten von Risiken sind Sie besorgt über?

In vielen Zusammenhängen würde ich C für "gefährlicher" halten als eine Hochsprache, da Sie die grundlegenden Funktionen mehr manuell implementieren müssen, um das Risiko von Fehlern zu erhöhen. Zum Beispiel wäre eine einfache Textverarbeitung oder die Entwicklung einer Website in C normalerweise dumm, da andere Sprachen über Funktionen verfügen, die dies erheblich vereinfachen.

C und C ++ werden jedoch häufig für unternehmenskritische Systeme verwendet, da eine kleinere Sprache mit direkterer Kontrolle über die Hardware in diesem Zusammenhang als "sicherer" angesehen wird. Aus einer sehr guten Stack Overflow-Antwort :

Obwohl C und C ++ nicht speziell für diese Art von Anwendung entwickelt wurden, werden sie aus mehreren Gründen häufig für eingebettete und sicherheitskritische Software verwendet. Die Haupteigenschaften von note sind die Kontrolle über die Speicherverwaltung (mit der Sie beispielsweise das Garbage Collect vermeiden können), einfache, gut getestete Kernlaufzeitbibliotheken und ausgereifte Toolunterstützung. Viele der heute verwendeten eingebetteten Entwicklungs-Toolketten wurden in den 1980er und 1990er Jahren entwickelt, als dies die aktuelle Technologie war, und stammen aus der damals vorherrschenden Unix-Kultur. Daher sind diese Tools für diese Art von Arbeit nach wie vor beliebt.

Der manuelle Speicherverwaltungscode muss sorgfältig überprüft werden, um Fehler zu vermeiden. Er ermöglicht jedoch eine gewisse Kontrolle über die Antwortzeiten der Anwendung, die bei Sprachen, die von der Garbage Collection abhängen, nicht verfügbar sind. Die Kernlaufzeitbibliotheken von C- und C ++ - Sprachen sind relativ einfach, ausgereift und gut verstanden, sodass sie zu den stabilsten Plattformen auf dem Markt gehören.


2
Ich würde sagen, hypermodernes C ist auch gefährlicher als Assemblersprache oder echte Low-Level-Dialekte von C, die sich konsistent so verhalten, als würden sie C-Operationen konsistent in Maschinencode-Operationen umsetzen, ohne die Randfälle zu berücksichtigen, in denen die natürlichen Maschinencode-Operationen auftreten würden Verhalten definiert haben, aber der C-Standard würde keine Anforderungen auferlegen. Der hypermoderne Ansatz, bei dem ein ganzzahliger Überlauf die Zeit- und Kausalitätsregeln negieren kann, scheint für die Generierung von sicherem Code weitaus weniger geeignet zu sein.
Supercat

5

Um die vorhandenen Antworten zu ergänzen, ist es gut und schön zu sagen, dass Sie sich für Python oder PHP für Ihr Projekt entscheiden, da diese relativ sicher sind. Aber irgendjemand muss diese Sprachen implementieren und wenn, dann werden sie es wahrscheinlich in C tun. (Oder so ähnlich.)

Aus diesem Grund verwenden die Benutzer C - , um die weniger gefährlichen Tools zu erstellen, die Sie verwenden möchten.


2

Gestatten Sie mir, Ihre Frage neu zu formulieren:

Ich denke darüber nach, [Werkzeug] zu lernen.

Aber warum benutzen Leute [Werkzeug] (oder [zugehöriges Werkzeug]), wenn [sie] "gefährlich" verwendet werden können?

Jedes interessante Tool kann gefährlich eingesetzt werden, einschließlich Programmiersprachen. Sie lernen mehr, damit Sie es tun können können (und damit weniger Gefahr entsteht, wenn Sie das Tool verwenden). Insbesondere lernen Sie das Werkzeug, damit Sie das tun können, wofür das Werkzeug gut ist (und vielleicht erkennen, wann dieses Werkzeug das beste Werkzeug der Ihnen bekannten Werkzeuge ist).

Wenn Sie beispielsweise ein 5 cm tiefes, zylindrisches Loch mit 6 mm Durchmesser in einen Holzblock stecken müssen, ist ein Bohrer ein viel besseres Werkzeug als ein LALR-Parser. Wenn Sie wissen, was diese beiden Werkzeuge sind, wissen Sie, welches das richtige Werkzeug ist. Wenn Sie bereits wissen, wie man einen Bohrer benutzt, voila !, hole.

C ist nur ein weiteres Werkzeug. Es ist für einige Aufgaben besser als für andere. Die anderen Antworten hier sprechen dies an. Wenn Sie etwas C lernen, werden Sie erkennen, wann es das richtige Werkzeug ist und wann nicht.


Diese Menge an Antworten ist der Grund, warum Fragen als "hauptsächlich meinungsbasiert" ausgeworfen werden. Sagen Sie nicht, dass C seine Vorteile hat, sagen Sie, was sie sind!
Reinierpost

1

Ich denke darüber nach, C zu lernen

Es gibt keinen bestimmten Grund, C nicht zu lernen, aber ich würde C ++ vorschlagen. Es bietet viel von dem, was C kann (da C ++ eine super Menge von C ist), mit einer großen Menge von "Extras". Das Erlernen von C vor C ++ ist nicht erforderlich - es handelt sich effektiv um separate Sprachen.

Anders ausgedrückt, wenn C eine Reihe von Holzbearbeitungswerkzeugen wäre, wäre es wahrscheinlich:

  • Hammer
  • Nägel
  • Handsäge
  • Handbohrmaschine
  • Blockschleifer
  • Meißel (vielleicht)

Mit diesen Tools können Sie alles erstellen - aber alles, was gut ist, erfordert möglicherweise viel Zeit und Können.

C ++ ist die Sammlung von Elektrowerkzeugen in Ihrem örtlichen Baumarkt.

Wenn Sie sich zunächst an die grundlegenden Sprachfunktionen halten, hat C ++ relativ wenig zusätzlichen Lernaufwand.

Aber warum verwenden die Leute C (oder C ++), wenn es "gefährlich" verwendet werden kann?

Weil manche Leute keine Möbel von IKEA wollen. =)

Im Ernst, obwohl viele Sprachen, die "höher" als C oder C ++ sind, Dinge haben, die sie (möglicherweise) in bestimmten Aspekten "einfacher" machen, ist dies nicht immer eine gute Sache. Wenn Ihnen die Art und Weise, wie etwas getan wird oder eine Funktion nicht bereitgestellt wird, nicht gefällt, können Sie wahrscheinlich nicht viel dagegen tun. Auf der anderen Seite bieten C und C ++ genügend "Low-Level" -Sprachenfunktionen (einschließlich Zeigern), mit denen Sie auf viele Dinge ziemlich direkt (insbesondere Hardware oder Betriebssystem) zugreifen oder sie selbst erstellen können, was in anderen möglicherweise nicht möglich ist Sprachen wie implementiert.

Insbesondere hat C die folgenden Funktionen, die es für viele Programmierer wünschenswert machen:

  • Geschwindigkeit - Aufgrund der relativen Einfachheit und der Compileroptimierung im Laufe der Jahre ist es von Haus aus sehr schnell. Viele Leute haben auch viele Verknüpfungen zu bestimmten Zielen gefunden, wenn sie die Sprache verwenden, was sie möglicherweise noch schneller macht.
  • Größe - Aus ähnlichen Gründen wie den für die Geschwindigkeit angegebenen können C-Programme sehr klein gemacht werden (sowohl in Bezug auf die ausführbare Größe als auch auf die Speichernutzung), was für Umgebungen mit begrenztem Speicher (dh eingebettet oder mobil) wünschenswert ist.
  • Kompatibilität - C gibt es schon lange und jeder hat Tools und Bibliotheken dafür. Die Sprache selbst ist auch nicht wählerisch - sie erwartet von einem Prozessor, dass er Anweisungen ausführt und Speicher, um Dinge zu speichern, und das ist alles.

    Darüber hinaus gibt es ein so genanntes Application Binary Interface (ABI) . Kurz gesagt, es ist eine Möglichkeit für Programme, auf Maschinencodeebene zu kommunizieren, was Vorteile gegenüber einer Anwendungsprogrammierschnittstelle (Application Programming Interface, API) haben kann . Während andere Sprachen wie C ++ eine ABI haben können, sind diese normalerweise weniger einheitlich (vereinbart) als die von C, so dass C eine gute Basissprache ist, wenn Sie eine ABI verwenden möchten, um aus irgendeinem Grund mit einem anderen Programm zu kommunizieren.

Warum verwenden Programmierer nicht nur Java oder Python oder eine andere kompilierte Sprache wie Visual Basic?

Effizienz (und gelegentlich Speicherverwaltungsschemata, die ohne relativ direkten Speicherzugriff nicht implementiert werden können).

Der direkte Zugriff auf das Gedächtnis mit Zeigern bringt eine Menge netter (normalerweise schneller) Tricks mit sich, wenn Sie Ihre schmutzigen Pfoten direkt auf die kleinen Einsen und Nullen in Ihren Gedächtnisräumen setzen und nicht darauf warten müssen, dass der alte Lehrer das Spielzeug einfach austeilt zur spielzeit schaufeln sie sie dann wieder auf.

Kurz gesagt, das Hinzufügen von Dingen kann zu Verzögerungen führen oder auf andere Weise zu unerwünschter Komplexität.

In Bezug auf Skriptsprachen und Ähnliches müssen Sie hart arbeiten, um Sprachen zu erhalten, für die sekundäre Programme so effizient ausgeführt werden müssen wie C (oder eine kompilierte Sprache). Das Hinzufügen eines Interpreters im laufenden Betrieb bietet die Möglichkeit, die Ausführungsgeschwindigkeit zu verringern und den Speicherbedarf zu erhöhen, da Sie der Mischung ein anderes Programm hinzufügen. Die Effizienz Ihres Programms hängt ebenso stark von der Effizienz dieses sekundären Programms ab wie davon, wie gut (schlecht =) Sie Ihren ursprünglichen Programmcode geschrieben haben. Ganz zu schweigen davon, dass Ihr Programm oft vollständig darauf angewiesen ist, dass das zweite Programm überhaupt ausgeführt wird. Das zweite Programm existiert aus irgendeinem Grund nicht auf einem bestimmten System? Code no go.

In der Tat, die Einführung etwas „extra“ verlangsamt potenziell oder kompliziert Ihren Code. In Sprachen „ohne beängstigend Zeiger“, warten Sie immer für andere Teile des Codes hinter sich aufzuräumen oder auf andere Weise herausfinden „sicher“ Möglichkeiten , Dinge zu tun - weil Ihr Programm immer noch die gleichen Speicherzugriffsoperationen zu tun wie es mit getan werden Zeiger. Sie sind einfach nicht derjenige, der damit umgeht (Sie können es also nicht ficken, Genie = P).

Mit gefährlich meine ich mit Zeigern und ähnlichen Sachen. [...] Wie die Stack Overflow-Frage Warum ist die gets-Funktion so gefährlich, dass sie nicht verwendet werden sollte?

Nach der akzeptierten Antwort:

"Es blieb ein offizieller Teil der Sprache bis zur ISO-C-Norm von 1999, wurde jedoch von der Norm von 2011 offiziell entfernt. Die meisten C-Implementierungen unterstützen es immer noch, aber zumindest gibt gcc eine Warnung für jeden Code aus, der es verwendet."

Die Vorstellung, dass etwas in einer Sprache getan werden kann, muss es , ist dumm. Sprachen haben Fehler, die behoben werden. Aus Kompatibilitätsgründen mit älterem Code kann dieses Konstrukt weiterhin verwendet werden. Es gibt jedoch (wahrscheinlich) nichts, was einen Programmierer dazu zwingt, gets () zu verwenden, und tatsächlich wurde dieser Befehl im Wesentlichen durch sicherere Alternativen ersetzt.

Genauer gesagt ist das Problem mit gets () kein Zeigerproblem per se Zeigerproblem. Es ist ein Problem mit einem Befehl, der nicht unbedingt weiß, wie man Speicher sicher verwendet. In einem abstrakten Sinne sind dies alles Zeigerfragen - Lesen und Schreiben von Dingen, die Sie nicht sollten. Das ist kein Problem mit Zeigern; Es ist ein Problem mit der Zeiger-Implementierung.

Zur Verdeutlichung sind Zeiger erst dann gefährlich, wenn Sie versehentlich auf einen Speicherort zugreifen, den Sie nicht beabsichtigt haben. Und selbst dann ist nicht garantiert, dass Ihr Computer schmilzt oder explodiert. In den meisten Fällen funktioniert Ihr Programm nicht mehr (korrekt).

Da Zeiger den Zugriff auf Speicherorte ermöglichen und Daten und ausführbarer Code zusammen im Speicher vorhanden sind, besteht jedoch die Gefahr einer versehentlichen Beschädigung, dass Sie den Speicher ordnungsgemäß verwalten möchten.

Da Operationen mit direktem Speicherzugriff im Allgemeinen häufig weniger Vorteile bieten als noch vor Jahren, haben selbst Sprachen ohne Speicherbereinigung wie C ++ Funktionen wie intelligente Zeiger eingeführt , um die Lücke zwischen Speichereffizienz und Sicherheit zu schließen.

Zusammenfassend lässt sich sagen, dass es sehr wenig Grund gibt, den Zeiger zu fürchten, solange er sicher verwendet wird. Nehmen Sie einfach einen Hinweis von South Parks Version von Steve "The Crocodile Hunter" Irwin - stecken Sie Ihren Daumen nicht in die Irrenlöcher der Crocs .


2
Ich bin mit dem Vorschlag, C ++ statt C zu lernen, nicht einverstanden. Das Schreiben von gutem C ++ ist schwieriger als das Schreiben von gutem C und das Lesen von C ++ ist viel schwieriger als das Lesen von C. Die Lernkurve von C ++ ist also viel steiler. "C ++ ist ein super Satz von C" Dies ist mehr oder weniger so, als würde man sagen, dass Stiefel eine Obermenge von Hausschuhen sind. Sie haben unterschiedliche Vorteile und Verwendungszwecke, und jeder hat Funktionen, die der andere nicht bietet.
Martinkunev

"Gutes C ++ zu schreiben ist schwieriger als gutes C zu schreiben" - Absolut. =) "[R] eading C ++ ist viel schwieriger als das Lesen von C" - Jede fortgeschrittene Programmierung ist wahrscheinlich nicht von Magie zu unterscheiden in dieser Kategorie. "Die Lernkurve von C ++ ist also viel steiler." - Auf lange Sicht ja. Kurzfristig weniger (meiner Meinung nach). Anekdotenhafterweise decken die meisten Grundsprachenkurse in C und C ++ mit Ausnahme der Kurse für C ++ ungefähr die gleichen allgemeinen Materialtypen ab.
Anaksunaman

2
"Sie haben unterschiedliche Vorteile und Verwendungszwecke und jeder hat Funktionen, die der andere nicht bietet." - Wie bereits erwähnt "Es gibt keinen bestimmten Grund, C [.] Nicht zu lernen" C ist eine gute Sprache und daran halte ich fest. Wenn es zu OP oder zu irgendjemand anderem passt, unterstütze ich das Lernen voll und ganz. =)
Anaksunaman

Wenn Sie C lernen, lernen Sie, wie die Maschine funktioniert (nicht ganz nach unten, aber immer noch einen Schritt näher am Metall). Das ist ein sehr guter Grund, es zu lernen.
Agent_L

1

Wie immer ist die Programmiersprache nur eine Folge der Problemlösung. Sie sollten in der Tat nicht nur C lernen, sondern auch viele verschiedene Sprachen (und andere Möglichkeiten zur Programmierung eines Computers, seien es GUI-Tools oder Befehlsinterpreter), um eine anständige Toolbox zur Lösung von Problemen zu haben.

Manchmal werden Sie feststellen, dass sich ein Problem gut für etwas eignet, das in den Java-Standardbibliotheken enthalten ist. In diesem Fall können Sie Java auswählen, um dies zu nutzen. In anderen Fällen müssen Sie unter Windows möglicherweise etwas tun, das in der .NET-Laufzeit viel einfacher ist, sodass Sie möglicherweise C # oder VB verwenden. Möglicherweise gibt es ein grafisches Tool oder ein Befehlsskript, das Ihr Problem löst, und Sie können diese verwenden. Möglicherweise müssen Sie eine GUI-Anwendung auf mehreren Plattformen schreiben. Aufgrund der im JDK enthaltenen Bibliotheken kann Java eine Option sein. Andererseits fehlt einer Zielplattform möglicherweise eine JRE. Wählen Sie stattdessen C und SDL (oder eine ähnliche).

C hat eine wichtige Position in diesem Toolset, da es allgemein, klein und schnell ist und auch zu Maschinencode kompiliert. Es wird auch auf jeder Plattform unter der Sonne unterstützt (jedoch nicht ohne Neukompilierung).

Fazit ist, dass Sie so viele Tools, Sprachen und Paradigmen lernen sollten, wie Sie nur können.

Bitte entfernen Sie sich von der Einstellung: "Ich bin ein X-Programmierer" (X = C, C ++, Java usw.)

Verwenden Sie einfach "Ich bin ein Programmierer".

Ein Programmierer löst Probleme und entwirft Algorithmen, indem er Maschinen anweist, die Arbeitslast auszuführen. Ende der Geschichte. Dies ist für die Sprache irrelevant. Ihre wichtigste Fähigkeit ist das Lösen von Problemen und die logische Aufschlüsselung strukturierter Probleme. Sprachkenntnisse / Sprachwahl sind IMMER zweitrangig und / oder eine Folge der Art des Problems.

Ein interessanter Weg, wenn Sie an C interessiert sind, ist es, Ihre Fähigkeiten mit Go zu erweitern. Go ist wirklich ein verbessertes C mit Garbage Collection und Interfaces sowie einem netten eingebauten Threading-Modell / -Kanälen, das auch viele der Vorteile von C mit sich bringt (wie Zeigerarithmetik und Kompilieren von Maschinencode).


0

Es hängt davon ab, was Sie damit vorhaben. C wurde als Ersatz für Assemblersprache entwickelt und ist die Hochsprache, die der Maschinensprache am nächsten kommt. Daher hat es einen geringen Overhead in Bezug auf Größe und Leistung und eignet sich für die Systemprogrammierung und andere Aufgaben, die einen geringen Platzbedarf erfordern und sich der zugrunde liegenden Hardware nähern.


0

Wenn Sie auf der Ebene der Bits und Bytes, des Speichers als homogene Rohdatensammlung arbeiten, wie es häufig erforderlich ist, um die effizientesten Allokatoren und Datenstrukturen effektiv zu implementieren, ist keine Sicherheit zu haben. Sicherheit ist in erster Linie ein stark datentypbezogenes Konzept, und ein Speicherzuweiser funktioniert nicht mit Datentypen. Es arbeitet mit Bits und Bytes, um dieselben Bits und Bytes zusammenzufassen, die möglicherweise einen Datentyp zu einem bestimmten Zeitpunkt und zu einem späteren Zeitpunkt darstellen.

In diesem Fall spielt es keine Rolle, ob Sie C ++ verwenden. Sie würden immer noch den static_castsgesamten Code aus void*Zeigern streuen und immer noch mit Bits und Bytes arbeiten und einfach mehr Probleme im Zusammenhang mit dem Respektieren des Typsystems in diesem Kontext haben als C, das ein viel einfacheres Typsystem hat, in dem Sie frei sind um memcpyBits und Bytes herum, ohne über das Typensystem Bulldozing zu sorgen.

Tatsächlich ist es oft schwieriger, in C ++, einer insgesamt sichereren Sprache, in solch einfachen Kontexten von Bits und Bytes zu arbeiten, ohne noch gefährlicheren Code zu schreiben als in C, da Sie über das Typensystem von C ++ hinwegblödeln und Dinge wie tun könnten Überschreiben von vptrs und Nichtaufrufen von Konstruktoren und Destruktoren für Kopien zu geeigneten Zeiten. Wenn Sie sich die Zeit nehmen, diese Typen zu respektieren und die Platzierung neu und manuell aufzurufen usw., werden Sie mit der Welt der Ausnahmebehandlung in einem Kontext konfrontiert, der für RAII zu niedrig ist, um praktisch zu sein, und Ausnahmebedingungen zu erreichen. Sicherheit in solch einem Kontext auf niedriger Ebene ist sehr schwierig (Sie müssen so tun, als ob fast jede Funktion alle Möglichkeiten auslotet und abfängt und alle Nebenwirkungen als unteilbare Transaktion zurückwirft, als ob nichts passiert wäre). Der C-Code kann oft "

Und es wäre unmöglich, solche Allokatoren in Sprachen zu implementieren, die es Ihnen nicht erlauben, hier "gefährlich" zu werden. Sie müssten sich auf die von ihnen bereitgestellten Allokatoren stützen (die höchstwahrscheinlich in C oder C ++ implementiert sind) und hoffen, dass sie für Ihre Zwecke gut genug sind. Und es gibt fast immer effizientere, aber weniger allgemeine Allokatoren und Datenstrukturen, die für Ihre spezifischen Zwecke geeignet sind, die jedoch viel enger anwendbar sind, da sie speziell auf Ihre Zwecke zugeschnitten sind.

Die meisten Leute brauchen kein C oder C ++, da sie nur Code aufrufen können, der ursprünglich in C oder C ++ implementiert war, oder möglicherweise sogar Assembler, der bereits für sie implementiert war. Viele könnten von Innovationen auf hoher Ebene profitieren, wie dem Zusammenfügen eines Bildprogramms, das nur Bibliotheken vorhandener Bildverarbeitungsfunktionen verwendet, die bereits in C implementiert sind, wo sie auf der niedrigsten Ebene des Durchlaufens einzelner Pixel weniger innovativ sind, aber möglicherweise Bietet eine sehr benutzerfreundliche Oberfläche und einen Workflow, der noch nie zuvor gesehen wurde. In diesem Fall ist es möglicherweise eine vorzeitige Optimierung , wenn die Software nur versucht, übergeordnete Aufrufe in untergeordneten Bibliotheken auszuführen ( "Verarbeiten Sie das gesamte Bild für mich und nicht für jedes Pixel. Tun Sie etwas" ) zu versuchen, einen solchen Antrag in C zu schreiben.

Wenn Sie jedoch auf niedriger Ebene etwas Neues tun, bei dem der Zugriff auf Daten auf niedriger Ebene erleichtert wird, beispielsweise durch einen brandneuen Bildfilter, der noch nie zuvor gesehen wurde und der schnell genug ist, um HD-Videos in Echtzeit zu bearbeiten, müssen Sie im Allgemeinen auf Folgendes zugreifen ein bisschen gefährlich.

Es ist einfach, dieses Zeug für selbstverständlich zu halten. Ich erinnere mich an einen Facebook-Beitrag, in dem jemand darauf hinwies, wie es möglich ist, mit Python ein 3D-Videospiel zu erstellen, das impliziert, dass Sprachen auf niedriger Ebene veraltet sind, und das mit Sicherheit ein anständiges Spiel war. Aber Python hat hochrangige Aufrufe in in C implementierten Bibliotheken durchgeführt, um die ganze schwere Arbeit zu erledigen. Sie können Unreal Engine 4 nicht einfach erstellen, indem Sie übergeordnete Aufrufe in vorhandene Bibliotheken tätigen. Unwirkliche Maschine 4 istdie Bibliothek. Es hat alle Arten von Dingen erledigt, die es in anderen Bibliotheken und Engines nie gegeben hat, von der Beleuchtung bis hin zu seinem knotigen Blaupausen-System und wie es Code im laufenden Betrieb kompilieren und ausführen kann. Wenn Sie Innovationen auf einer niedrigen Engine- / Core- / Kernel-Ebene durchführen möchten, müssen Sie auf einer niedrigen Ebene arbeiten. Wenn alle Spieleentwickler auf sichere Hochsprachen umstellen würden, gäbe es keine Unreal Engine 5, 6 oder 7. Wahrscheinlich werden die Leute Unreal Engine 4 Jahrzehnte später noch verwenden, weil Sie nicht auf dem erforderlichen Niveau innovieren können raus mit einem Next-Gen-Motor, indem Sie nur hochrangige Anrufe in den alten machen.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.