Programmering

Hvorfor C-programmeringssproget stadig regerer

Ingen teknologi holder fast i 50 år, medmindre den gør sit job bedre end det meste andet - især en computerteknologi. C-programmeringssproget har levet og sparket siden 1972, og det regerer stadig som en af ​​de grundlæggende byggesten i vores softwaredefinerede verden.

Men nogle gange holder en teknologi sig, fordi folk bare ikke er kommet til at erstatte den. I løbet af de sidste par årtier er der optrådt snesevis af andre sprog - nogle eksplicit designet til at udfordre Cs dominans, nogle fliser væk fra C som et biprodukt af deres popularitet.

Det er ikke svært at argumentere for, at C skal udskiftes. Programmeringssprogforskning og softwareudviklingspraksis antyder alt sammen, hvordan der er langt bedre måder at gøre ting på end C's måde. Men C fortsætter med det samme med årtier af forskning og udvikling bag sig. Få andre sprog kan slå det for ydeevne, for bare-metal-kompatibilitet eller for allestedsnærværende. Det er alligevel værd at se, hvordan C stabler op mod konkurrencen om store navnesprog i 2018.

C mod C ++

Naturligvis sammenlignes C mest almindeligt med C ++, det sprog, som - som navnet selv angiver - blev skabt som en udvidelse af C. Forskellene mellem C ++ og C kunne karakteriseres som omfattende, elleroverdreven, afhængigt af hvem du spørger.

Selvom det stadig er C-lignende i sin syntaks og tilgang, giver C ++ mange virkelig nyttige funktioner, der ikke er tilgængelige i C: navneområder, skabeloner, undtagelser, automatisk hukommelsesstyring og så videre. Projekter, der kræver ydeevne i topklasse - databaser, maskinlæringssystemer - skrives ofte i C ++ ved hjælp af disse funktioner til at vride hver dråbe ydeevne ud af systemet.

Yderligere fortsætter C ++ med at ekspandere langt mere aggressivt end C. Den kommende C ++ 20 bringer endnu mere til bordet inklusive moduler, coroutines, et synkroniseringsbibliotek og koncepter, der gør skabeloner lettere at bruge. Den seneste revision af C-standarden tilføjer lidt og fokuserer på at bevare bagudkompatibilitet.

Ting er, at alle plusser i C ++ også kan fungere som minus. Store. Jo flere C ++ -funktioner du bruger, jo mere kompleksitet introducerer du, og jo vanskeligere bliver det at tæmme resultaterne. Udviklere, der begrænser sig til en delmængde af C ++, kan undgå mange af dens værste faldgruber. Men nogle butikker ønsker at beskytte sig mod C ++ - kompleksitet alt sammen. At holde fast i C tvinger udviklere til at begrænse sig til den delmængde. Linux-kerneludviklingsteamet undgår for eksempel C ++.

At vælge C frem for C ++ er en måde for dig - og alle udviklere, der opretholder koden efter dig - for at undgå at skulle flokse med C ++ overdrivelser ved at omfavne en håndhævet minimalisme. Selvfølgelig har C ++ et rigt sæt funktioner på højt niveau med god grund. Men hvis minimalisme passer bedre til nuværende og fremtidige projekter - og projekt hold- derefter giver C mere mening.

C vs. Java

Efter årtier er Java fortsat et hæfteklammer til udvikling af virksomhedssoftware - og generelt et hæfteklammer til udvikling. Mange af de mest betydningsfulde virksomhedssoftwareprojekter blev skrevet i Java - inklusive langt størstedelen af ​​Apache Software Foundation-projekter - og Java er fortsat et levedygtigt sprog til udvikling af nye projekter med krav til virksomhedskvalitet.

Java-syntaks låner meget fra C og C ++. I modsætning til C kompilerer Java dog ikke som standard til native-kode. I stedet for samler Java-runtime-miljøet, JVM, JIT (just-in-time) Java-kode til at køre i målmiljøet. Under de rette omstændigheder kan JITted Java-kode nærme sig eller endda overstige præstationen for C.

Filosofien "skriv en gang, kør hvor som helst" bag Java giver også Java-programmer mulighed for at køre med relativt lidt tilpasning til en målarkitektur. I modsætning hertil, selvom C er blevet overført til mange arkitekturer, kan et hvilket som helst C-program muligvis stadig have brug for tilpasning for at køre ordentligt på f.eks. Windows versus Linux.

Denne kombination af bærbarhed og stærk ydeevne sammen med et massivt økosystem af softwarebiblioteker og -rammer gør Java til et sprog og runtime til opbygning af virksomhedsapplikationer.

Hvor Java ikke er under C, er et område, hvor Java aldrig var beregnet til at konkurrere: at løbe tæt på metallet eller arbejde direkte med hardware. C-kode kompileres til maskinkode, som udføres direkte af processen. Java er samlet til bytecode, som er en mellemliggende kode, som JVM-tolkene derefter konverterer til maskinkode. Yderligere, selvom Javas automatiske hukommelsesstyring er en velsignelse under de fleste omstændigheder, er C bedre egnet til programmer, der skal udnytte begrænsede hukommelsesressourcer optimalt.

Når det er sagt, er der nogle områder, hvor Java kan komme tæt på C med hensyn til hastighed. JVM's JIT-motor optimerer rutiner ved runtime baseret på programadfærd, hvilket giver mulighed for mange optimeringsklasser, der ikke er mulige med kompilering i forvejen. Og mens Java-runtime automatiserer hukommelsesstyring, fungerer nogle nyere applikationer omkring det. For eksempel optimerer Apache Spark delvist behandling i hukommelse ved hjælp af brugerdefineret hukommelsesadministrationskode, der omgår JVM.

C vs. C # og .Net

Næsten to årtier efter deres introduktion forbliver C # og .Net Framework vigtige dele af virksomhedens softwareverden. Det er blevet sagt, at C # og .Net var Microsofts svar på Java - et administreret kodekompilatorsystem og universel driftstid - og så mange sammenligninger mellem C og Java holder også op for C og C # /. Net.

Ligesom Java (og til en vis grad Python) tilbyder .Net bærbarhed på tværs af forskellige platforme og et stort økosystem med integreret software. Dette er ikke små fordele i betragtning af hvor meget virksomhedsorienteret udvikling finder sted i .Net-verdenen. Når du udvikler et program i C # eller ethvert andet .Net-sprog, er du i stand til at trække på et univers af værktøjer og biblioteker, der er skrevet til. Net-kørselstiden.

En anden Java-lignende .NET-fordel er JIT-optimering. C # og. Net-programmer kan kompileres i forvejen i henhold til C, men de er hovedsageligt just-in-time kompileret af. Net runtime og optimeret med runtime information. JIT-kompilering tillader alle mulige optimeringer på stedet for et kørende .Net-program, der ikke kan udføres i C.

Ligesom C giver C # og .Net forskellige mekanismer til direkte adgang til hukommelse. Heap, stack og ikke-administreret systemhukommelse er alle tilgængelige via .Net API'er og objekter. Og udviklere kan bruge usikre i .Net for at opnå endnu større ydeevne.

Intet af dette kommer dog gratis. Administrerede objekter og usikre objekter kan ikke udveksles vilkårligt, og marskalering mellem dem koster en præstationsomkostning. Derfor betyder maksimering af .Net-applikationers ydeevne at minimere bevægelsen mellem administrerede og ikke-administrerede objekter.

Når du ikke har råd til at betale bøden for administreret vs. ikke-administreret hukommelse, eller når .Net-runtime er et dårligt valg for målmiljøet (f.eks. Kernerum) eller måske slet ikke er tilgængeligt, så er C det, du brug for. Og i modsætning til C # og .Net låser C som standard direkte adgang til hukommelse.

C vs. Go

Go syntaks skylder meget C - krøllede seler som afgrænsere, udsagn afsluttet med semikoloner osv. Udviklere, der er dygtige i C, kan typisk springe direkte ind i Go uden store vanskeligheder, selv under hensyntagen til nye Go-funktioner som navneområder og pakkehåndtering.

Læsbar kode var et af Go's ledende designmål: Gør det let for udviklere at komme op i hastighed med ethvert Go-projekt og blive dygtige med kodebasen i kort rækkefølge. C-kodebaser kan være svære at groke, da de er tilbøjelige til at blive til en rotte rede af makroer og #ifdefs specifikke for både et projekt og et givet team. Go's syntaks og dets indbyggede kodeformaterings- og projektstyringsværktøjer er beregnet til at holde disse slags institutionelle problemer i skak.

Go har også ekstramateriale som goroutines og kanaler, værktøjer på sprogniveau til håndtering af samtidighed og meddelelse, der går mellem komponenter. C ville kræve, at sådanne ting håndrulles eller leveres af et eksternt bibliotek, men Go leverer dem lige ud af kassen, hvilket gør det meget lettere at konstruere software, der har brug for dem.

Hvor Go adskiller sig mest fra C under emhætten er i hukommelsesstyring. Go-objekter administreres automatisk og indsamles skrald som standard. For de fleste programmeringsjob er dette utrolig praktisk. Men det betyder også, at ethvert program, der kræver deterministisk håndtering af hukommelse, vil være sværere at skrive.

Go inkluderer også usikre pakke til at omgå nogle af Go's håndteringssikkerhed, såsom læsning og skrivning af vilkårlig hukommelse med en Markør type. Men usikre leveres med en advarsel om, at programmer skrevet med det ”muligvis ikke er bærbare og ikke er beskyttet af Go 1-kompatibilitetsretningslinjerne.”

Go er velegnet til opbygning af programmer som kommandolinjeværktøjer og netværkstjenester, fordi de sjældent har brug for sådanne finkornede manipulationer. Men enhedsdrivere på lavt niveau, komponenter til operativsystemer i kernerum og andre opgaver, der kræver nøjagtig kontrol over hukommelseslayout og -styring, oprettes bedst i C.

C mod rust

På nogle måder er Rust et svar på hukommelsesstyringsproblemer skabt af C og C ++ og på mange andre mangler på disse sprog også. Rust kompileres med den oprindelige maskinkode, så det betragtes på niveau med C for så vidt angår ydeevne. Hukommelsessikkerhed er dog som standard Rusts vigtigste salgsargument.

Rusts syntaks- og kompileringsregler hjælper udviklere med at undgå almindelige fejl i hukommelsesstyring. Hvis et program har et hukommelsesstyringsproblem, der krydser Rust-syntaks, kompileres det simpelthen ikke. Begynderne på sproget, især fra et sprog som C, der giver masser af plads til sådanne bugs, bruger den første fase af deres Rust-uddannelse på at lære at berolige kompilatoren. Men rustforkæmpere hævder, at denne smerte på kort sigt har en langsigtet udbytte: sikrere kode, der ikke ofrer hastighed.

Rust forbedrer også C med sit værktøj. Projekt- og komponentstyring er en del af værktøjskæden, der leveres med Rust som standard, det samme som med Go. Der er en standard, anbefalet måde at administrere pakker på, organisere projektmapper og håndtere en lang række andre ting, der i C i bedste fald er ad hoc, hvor hvert projekt og team håndterer dem forskelligt.

Alligevel kan det, der udråbes som en fordel i Rust, måske ikke se ud som en for en C-udvikler. Rusts kompileringstids-sikkerhedsfunktioner kan ikke deaktiveres, så selv det mest trivielle Rust-program skal overholde Rusts hukommelsessikkerhedsstrikturer. C kan være mindre sikkert som standard, men det er meget mere fleksibelt og tilgivende, når det er nødvendigt.

En anden mulig ulempe er størrelsen på Rust-sproget. C har relativt få funktioner, selv når man tager hensyn til standardbiblioteket. Rust-funktionssættet er spredt og fortsætter med at vokse. Som med C ++ betyder det større Rust-funktionssæt mere magt, men også mere kompleksitet. C er et mindre sprog, men det er meget lettere at modellere mentalt, så måske bedre egnet til projekter, hvor Rust ville være overkill.

C vs. Python

I disse dage, når samtalen handler om softwareudvikling, ser Python altid ud til at komme ind i samtalen. Når alt kommer til alt er Python "det næstbedste sprog for alt" og uden tvivl et af de mest alsidige med tusindvis af tredjepartsbiblioteker til rådighed.

Hvad Python understreger, og hvor det adskiller sig mest fra C, favoriserer udviklingshastighed frem for eksekveringshastighed. Et program, der kan tage en time at sammensætte på et andet sprog - som C - kan samles i Python på få minutter. På bagsiden kan det tage sekunder at udføre programmet i C, men et minut at køre i Python. (En god tommelfingerregel: Python-programmer kører generelt en størrelsesorden langsommere end deres C-kolleger.) Men for mange job på moderne hardware er Python hurtig nok, og det har været nøglen til dets optagelse.

En anden stor forskel er hukommelsesstyring. Python-programmer styres fuldt ud af hukommelsen af ​​Python-runtime, så udviklere behøver ikke bekymre sig om det nitty-gritty ved at allokere og frigøre hukommelse. Men her igen kommer udviklerens lethed på bekostning af runtime-ydeevne. Skrivning af C-programmer kræver omhyggelig opmærksomhed på hukommelsesstyring, men de resulterende programmer er ofte guldstandarden for ren maskinehastighed.

Under huden deler Python og C dog en dyb forbindelse: referencen Python-runtime er skrevet i C. Dette giver Python-programmer mulighed for at pakke biblioteker skrevet i C og C ++. Væsentlige bidder af Python-økosystemet fra tredjepartsbiblioteker, f.eks. Til maskinindlæring, har C-kerne i deres kerne.

Hvis udviklingshastighed betyder mere end eksekveringshastighed, og hvis de fleste af de performante dele af programmet kan isoleres i selvstændige komponenter (i modsætning til at blive spredt gennem hele koden), udgør enten ren Python eller en blanding af Python- og C-biblioteker et bedre valg end C alene. Ellers regerer C stadig.