Programmering

Hvorfor C-programmeringsspråket fremdeles styrer

Ingen teknologi holder fast i 50 år med mindre den gjør jobben sin bedre enn det meste annet - spesielt en datateknologi. C-programmeringsspråket har levd og sparket siden 1972, og det regjerer fortsatt som en av de grunnleggende byggesteinene i vår programvaredefinerte verden.

Men noen ganger holder en teknologi seg fordi folk bare ikke har fått til å erstatte den. I løpet av de siste tiårene har dusinvis av andre språk dukket opp - noen eksplisitt utformet for å utfordre Cs dominans, andre fliser C fra siden som et biprodukt av deres popularitet.

Det er ikke vanskelig å argumentere for at C må byttes ut. Programmeringsspråkforskning og programvareutviklingspraksis antyder alt hvordan det er langt bedre måter å gjøre ting på enn C. Men C fortsetter likevel, med flere tiår med forskning og utvikling bak seg. Få andre språk kan slå det for ytelse, for kompatibilitet med bare metall eller for allestedsnærværende. Likevel er det verdt å se hvordan C stabler opp mot språkkonkurranse med store navn i 2018.

C mot C ++

Naturligvis sammenlignes C vanligst med C ++, språket som - som navnet selv indikerer - ble opprettet som en utvidelse av C. Forskjellene mellom C ++ og C kan karakteriseres som omfattende, elleroverflødig, avhengig av hvem du spør.

Mens C ++ fremdeles er C-lignende i sin syntaks og tilnærming, gir den mange virkelig nyttige funksjoner som ikke er tilgjengelige i C: navneområder, maler, unntak, automatisk minnestyring og så videre. Prosjekter som krever topp ytelse - databaser, maskinlæringssystemer - blir ofte skrevet i C ++ ved å bruke disse funksjonene for å vri hver dråpe ytelse ut av systemet.

Videre fortsetter C ++ å ekspandere langt mer aggressivt enn C. Den kommende C ++ 20 bringer enda mer til bordet, inkludert moduler, coroutines, et synkroniseringsbibliotek og konsepter, som gjør maler lettere å bruke. Den siste revisjonen av C-standarden tilfører lite og fokuserer på å beholde bakoverkompatibilitet.

Tingen er at alle plussene i C ++ også kan fungere som minus. Store. Jo flere C ++ -funksjoner du bruker, jo mer kompleksitet introduserer du og jo vanskeligere blir det å temme resultatene. Utviklere som begrenser seg til en delmengde av C ++ kan unngå mange av de verste fallgruvene og overdreven. Men noen butikker vil beskytte mot C ++ - kompleksitet. Å holde fast ved C tvinger utviklere til å begrense seg til den delmengden. Linux-kjerneutviklingsteamet, for eksempel, unngår C ++.

Å velge C over C ++ er en måte for deg - og alle utviklere som opprettholder koden etter deg - for å unngå å måtte floke med C ++ overdrivelser, ved å omfavne en håndhevet minimalisme. Selvfølgelig har C ++ et rikt sett med funksjoner på høyt nivå med god grunn. Men hvis minimalisme passer bedre for nåværende og fremtidige prosjekter - og prosjekter lag—Så gir C mer mening.

C mot Java

Etter flere tiår er Java fortsatt en stift for programvareutvikling for bedrifter - og en stift for utvikling generelt. Mange av de viktigste bedriftens programvareprosjekter ble skrevet på Java - inkludert de aller fleste Apache Software Foundation-prosjekter - og Java er fortsatt et levedyktig språk for utvikling av nye prosjekter med krav til bedriftens kvalitet.

Java-syntaks låner mye fra C og C ++. I motsetning til C kompilerer Java imidlertid ikke som standard innfødt kode. I stedet kompilerer Java runtime-miljøet, JVM, JIT (just-in-time) Java-kode for å kjøre i målmiljøet. Under de rette omstendighetene kan JITted Java-kode nærme seg eller til og med overgå ytelsen til C.

Filosofien "skriv en gang, kjør hvor som helst" bak Java lar også Java-programmer kjøre med relativt lite justering for en målarkitektur. Derimot, selv om C har blitt portet til mange arkitekturer, kan et hvilket som helst C-program fremdeles trenge tilpasning for å kjøre riktig, for eksempel, Windows versus Linux.

Denne kombinasjonen av bærbarhet og sterk ytelse, sammen med et enormt økosystem av programvarebiblioteker og rammer, gjør Java til et språk og kjøretid for å bygge bedriftsapplikasjoner.

Der Java faller under C er et område der Java aldri var ment å konkurrere: å løpe nær metallet, eller jobbe direkte med maskinvare. C-kode blir samlet i maskinkode, som utføres direkte av prosessen. Java er samlet til bytekode, som er mellomkode som JVM-tolken deretter konverterer til maskinkode. Videre, selv om Javas automatiske minneadministrasjon er en velsignelse i de fleste tilfeller, er C bedre egnet for programmer som må utnytte begrensede minnesressurser optimalt.

Når det er sagt, er det noen områder der Java kan komme nær C når det gjelder hastighet. JVMs JIT-motor optimaliserer rutiner ved kjøretid basert på programadferd, og gir mulighet for mange klasser av optimalisering som ikke er mulig med kompilering i forkant av C. Og mens Java-kjøretiden automatiserer minnestyring, fungerer noen nyere applikasjoner rundt det. For eksempel optimaliserer Apache Spark delvis minnebehandling ved å bruke tilpasset minnestyringskode som omgår JVM.

C mot C # og .Net

Nesten to tiår etter introduksjonen er C # og .Net Framework store deler av bedriftens programvareverden. Det er blitt sagt at C # og .Net var Microsofts svar på Java - et administrert kodekompilatorsystem og universell kjøretid - og så mange sammenligninger mellom C og Java holder også for C og C # /. Net.

Som Java (og til en viss grad Python), tilbyr .Net bærbarhet på tvers av en rekke plattformer og et stort økosystem med integrert programvare. Dette er ingen små fordeler gitt hvor mye bedriftsrettet utvikling skjer i .Net-verdenen. Når du utvikler et program i C #, eller et hvilket som helst annet .Net-språk, kan du trekke på et univers med verktøy og biblioteker skrevet for. Net-kjøretiden.

En annen Java-lignende .NET-fordel er JIT-optimalisering. C # og .Net-programmer kan kompileres på forhånd i henhold til C, men de er hovedsakelig bare i tide samlet av. Net-kjøretiden og optimalisert med kjøretidsinformasjon. JIT-kompilering tillater alle slags optimaliseringer på stedet for et kjørende .Net-program som ikke kan utføres i C.

I likhet med C gir C # og .Net forskjellige mekanismer for direkte tilgang til minne. Heap, stack og ikke-administrert systemminne er alle tilgjengelige via .Net APIer og objekter. Og utviklere kan bruke usikre modus i .Net for å oppnå enda større ytelse.

Ingenting av dette kommer gratis. Administrerte objekter og usikre objekter kan ikke utveksles vilkårlig, og marskalering mellom dem koster ytelse. Derfor betyr å maksimere ytelsen til .Net-applikasjoner å holde bevegelsen mellom administrerte og ikke-administrerte objekter til et minimum.

Når du ikke har råd til å betale straffen for administrert vs. ikke-administrert minne, eller når .Net-kjøretiden er et dårlig valg for målmiljøet (f.eks. Kjerneplass) eller kanskje ikke er tilgjengelig i det hele tatt, så er C det du trenge. Og i motsetning til C # og .Net, låser C opp som standard direkte minnetilgang.

C vs. Go

Go syntaks skylder mye C - krøllete bukseseler som avgrensere, utsagn avsluttet med semikolon, og så videre. Utviklere som er dyktige i C, kan vanligvis hoppe rett inn i Go uten store vanskeligheter, til og med å ta hensyn til nye Go-funksjoner som navneplasser og pakkehåndtering.

Lesbar kode var et av Go's ledende designmål: Gjør det enkelt for utviklere å komme i gang med ethvert Go-prosjekt og bli dyktige med kodebasen på kort tid. C-kodebaser kan være vanskelig å grok, da de er tilbøyelige til å bli et rottehus av makroer og #ifdefer spesifikk for både et prosjekt og et gitt team. Go’s syntaks, og den innebygde kodeformateringen og prosjektledelsesverktøyene, er ment å holde slike institusjonelle problemer i sjakk.

Go har også statister som goroutines og kanaler, språknivåverktøy for håndtering av samtidighet og melding som går mellom komponenter. C vil kreve at slike ting håndrulles eller leveres av et eksternt bibliotek, men Go gir dem rett ut av esken, noe som gjør det langt enklere å lage programvare som trenger dem.

Hvor Go skiller seg mest fra C under panseret er i minnestyring. Go-objekter administreres automatisk og søppel samles som standard. For de fleste programmeringsjobber er dette utrolig praktisk. Men det betyr også at ethvert program som krever deterministisk håndtering av minne vil være vanskeligere å skrive.

Go inkluderer usikre pakke for å omgå noen av Go-typene håndteringssikkerheter, for eksempel å lese og skrive vilkårlig minne med en Peker type. Men usikre kommer med en advarsel om at programmer skrevet med den “kan være ikke-bærbare og ikke er beskyttet av Go 1-retningslinjene for kompatibilitet.”

Go er velegnet for å bygge programmer som kommandolinjeverktøy og nettverkstjenester, fordi de sjelden trenger slike finkornede manipulasjoner. Men enhetsdrivere på lavt nivå, operativsystemkomponenter i kjerneområdet og andre oppgaver som krever nøyaktig kontroll over minnelayout og administrasjon, opprettes best i C.

C mot rust

På noen måter er Rust et svar på minnestyringsproblemene som er opprettet av C og C ++, og på mange andre mangler på disse språkene også. Rust kompilerer med den opprinnelige maskinkoden, så det er på nivå med C så langt som ytelse. Minnesikkerhet er som standard Rusts viktigste salgsargument.

Rusts syntaks- og kompileringsregler hjelper utviklere med å unngå vanlige minnestyringsfeil. Hvis et program har et minnehåndteringsproblem som krysser Rust-syntaksen, vil det ganske enkelt ikke kompilere. Nykommere på språket, spesielt fra et språk som C som gir god plass til slike feil, bruker den første fasen av Rust-utdannelsen sin på å lære å berolige kompilatoren. Men Rust-talsmenn hevder at denne kortsiktige smerten har en langsiktig gevinst: sikrere kode som ikke ofrer hastighet.

Rust forbedrer også C med sitt verktøy. Prosjekt- og komponentadministrasjon er en del av verktøykjeden som følger med Rust som standard, det samme som med Go. Det er en standard, anbefalt måte å administrere pakker, organisere prosjektmapper og håndtere mange andre ting som i C i beste fall er ad hoc, med hvert prosjekt og team som håndterer dem forskjellig.

Likevel, det som er spioneringen som en fordel i Rust, kan ikke virke som en for en C-utvikler. Rusts sikkerhetsfunksjoner for kompileringstid kan ikke deaktiveres, så selv det mest trivielle Rust-programmet må være i samsvar med Rusts minnesikkerhetsstrikturer. C kan være mindre trygt som standard, men det er mye mer fleksibelt og tilgivende når det er nødvendig.

En annen mulig ulempe er størrelsen på Rust-språket. C har relativt få funksjoner, selv når man tar hensyn til standardbiblioteket. Rust-funksjonssettet er viltvoksende og fortsetter å vokse. Som med C ++ betyr det større Rust-funksjonssettet mer kraft, men også mer kompleksitet. C er et mindre språk, men det er mye lettere å modellere mentalt, så kanskje bedre egnet for prosjekter der Rust ville være for mye.

C vs. Python

I disse dager, når snakkene handler om programvareutvikling, ser det alltid ut til at Python kommer inn i samtalen. Tross alt er Python "det nest beste språket for alt", og utvilsomt et av de mest allsidige, med tusenvis av tredjepartsbiblioteker tilgjengelig.

Det Python legger vekt på, og hvor det skiller seg mest fra C, favoriserer utviklingshastighet fremfor eksekveringshastighet. Et program som det kan ta en time å sette sammen på et annet språk - som C - kan samles i Python på få minutter. På baksiden kan det ta sekunder å kjøre programmet i C, men et minutt å kjøre i Python. (En god tommelfingerregel: Python-programmer kjører vanligvis en størrelsesorden langsommere enn deres C-kolleger.) Men for mange jobber på moderne maskinvare er Python rask nok, og det har vært nøkkelen til opptaket.

En annen stor forskjell er minnehåndtering. Python-programmer er fullstendig minnestyrt av Python-kjøretiden, så utviklere trenger ikke å bekymre seg for det nitty-gritty å tildele og frigjøre minne. Men også her koster utvikleren bekostning av ytelse på kjøretiden. Å skrive C-programmer krever nøye oppmerksomhet mot minnehåndtering, men de resulterende programmene er ofte gullstandarden for ren maskinhastighet.

Under huden deler Python og C imidlertid en dyp forbindelse: referansen Python-kjøretid er skrevet i C. Dette gjør at Python-programmer kan pakke inn biblioteker skrevet i C og C ++. Betydelige biter av Python-økosystemet til tredjepartsbiblioteker, for eksempel for maskinlæring, har C-kode i kjernen.

Hvis utviklingshastigheten betyr mer enn utførelseshastigheten, og hvis de fleste av de utførende delene av programmet kan isoleres i frittstående komponenter (i motsetning til å være spredt over hele koden), gjør enten ren Python eller en blanding av Python- og C-biblioteker et bedre valg enn C alene. Ellers hersker C fremdeles.