Kihagyás

2. előadás

Statikus vagy dinamikus típusrendszer

Statikus

Statikus típusrendszer: fordítási időben meg tudja határozni a részkifejezések típusát

Dinamikus

Interpretált nyelvekre jellemző, hogy egy változó egymás után más típusú adatra is mutathat. Ezek a nyelvek dinamikut típusrendszerrel rendelkezik.

A helytelen típushasználatok jellemzően az erősen típusos nyelveknél okozhat hibát szemben a gyengén típusos nyelvekkel.

Feladat - Temperature conversion

String substitution (formázás)

  • %d: ~~szám~~ intédzser kiírás
  • %s: String kiírás

  • %c: Karakter

Első hibás megoldás

/*
 *  Convert Fahrenheit to Celsius
 *  between -100F and +400F  by 100F  
 */
#include <stdio.h>

int main()
{
  int fahr;

  for ( fahr = -100; fahr <= 400; fahr += 100 )
  {
    printf("Fahr = %d,\tCels = %d\n",fahr,5/9*(fahr-32));
  }
  return 0;
}
$ gcc -ansi -pedantic -W -Wall -std=c11 fahrenheit.c -o fahrenheit

$ ./fahrenheit
Fahr = -100,    Cels = 0
Fahr = 0,    Cels = 0
Fahr = 100,    Cels = 0
Fahr = 200,    Cels = 0
Fahr = 300,    Cels = 0
Fahr = 400,    Cels = 0

Error

Hiba oka: Automatikus típuskonverzió (az osztásban használt két konstans típusa int, így az eredmény is int.)

\[ 5\\, div\\, 9 = 0 \]

Javítás: A két konstans valamelyikének típuskényszerítése (az érték konverziója, vagy egyből megfelelő típusban megadva)

Második hibás megoldás

/*
 *  Convert Fahrenheit to Celsius
 *  between -100F and +400F  by 100F  
 */
#include <stdio.h>

int main()
{
  //Változó deklaráció
  int fahr;

  //Ciklus
  for ( fahr = -100; fahr <= 400; fahr += 100 )
  {
    //Kiírás (lásd: formázás)
    printf("Fahr = %d,\tCels = %d\n",fahr,5./9.*(fahr-32));
  }
  return 0;
}
$ gcc -ansi -pedantic -W -Wall fahrenheit.c -o fahrenheit
fahrenheit.c: In function ‘main’:
fahrenheit.c:17:5: warning: format ‘%d’ expects argument of type ‘int’, but argument 3 has type ‘double’ [-Wformat=]
     printf( "Fahr = %d,\tCels = %d\n", fahr, 5./9.*(fahr-32) );
     ^

$ ./fahrenheit 
Fahr = -100,    Cels = 913552376
Fahr = 0,        Cels = -722576928
Fahr = 100,        Cels = -722576928
Fahr = 200,        Cels = -722576928
Fahr = 300,        Cels = -722576928
Fahr = 400,        Cels = -722576928

Hiba oka: A számítás helyes, hiszen az eredmény típusa double, azonban a kiíratáskor egészt várunk (TypeError).

Megoldás: Kiíratásnál a várt típus módosítása, vagy az eredmény típuskonverziója.

A C nyelvben paraméterátadáskor csak akkor történik konverzió, ha a hívott függvényt teljes paraméterlistával előzetesen deklaráljuk. A printf esetében a paraméterek feloldása futási időben történik.

Első helyes megoldás

/*
 *  Convert Fahrenheit to Celsius
 *  between -100F and +400F  by 100F  
 */
#include <stdio.h>
int main()
{
  int fahr;

  for ( fahr = -100; fahr <= 400; fahr += 100 )
  {
    printf("Fahr = %d,\tCels = %f\n",fahr,5./9.*(fahr-32));
  }
  return 0;
}
$ gcc -ansi -pedantic -W -Wall fahrenheit.c -o fahrenheit

$ ./fahrenheit 
Fahr = -100,    Cels = -73.333333
Fahr = 0,    Cels = -17.777778
Fahr = 100,    Cels = 37.777778
Fahr = 200,    Cels = 93.333333
Fahr = 300,    Cels = 148.888889
Fahr = 400,    Cels = 204.444444

A megoldás helyes, azonban vannak potenciális refaktorálási lehetőségek.

A metódus szignatúrája double fahr2cels(double), ezért az int típusú aktuális paraméter lebegőpontossá konvertálva adódik át. A %7.2f formátum 7 karakter szélességben, 2 tizedesre kerekítve írja ki az eredményt.

Első refaktorálási iteráció

/*
 *  Convert Fahrenheit to Celsius
 *  between -100F and +400F  by 100F  
 */
#include <stdio.h>
double fahr2cels( double f)
{
  return 5./9. * (f-32);
}
int main()
{
  int fahr;

  for ( fahr = -100; fahr <= 400; fahr += 100 )
  {
    printf("Fahr = %4d,\tCels = %7.2f\n",
                       fahr, fahr2cels(fahr));
  }
  return 0;
}
$ gcc -ansi -pedantic -W -Wall fahrenheit.c -o fahrenheit

$ ./fahrenheit 
Fahr = -100,    Cels =  -73.33
Fahr =    0,    Cels =  -17.78
Fahr =  100,    Cels =   37.78
Fahr =  200,    Cels =   93.33
Fahr =  300,    Cels =  148.89
Fahr =  400,    Cels =  204.44

Az eredmény könnyebben kezelhető, azonban van még egy refaktorálási potenciál.

Használjunk előfordító direktívákat (preprocessor directive) konstansok deffiniálására

Konstansok előfordított direktívákkal

/*
 *  Convert Fahrenheit to Celsius
 *  between -100F and +400F  by 100F  
 */
#include <stdio.h>

#define LOWER -100
#define UPPER  400
#define STEP   100

double fahr2cels( double f)
{
  return 5./9. * (f-32);
}
int main()
{
  int fahr;

  for ( fahr = LOWER; fahr <= UPPER; fahr += STEP )
  {
    printf( "Fahr = %4d,\tCels = %7.2f\n", 
                        fahr, fahr2cels(fahr) );
  }
  return 0;
}

Konstansok névvel ellátva

/*
 *  OK, with const
 *  Convert Fahrenheit to Celsius
 *  between -100F and +400F  by 100F  
 */
#include <stdio.h>

const int lower = -100;
const int upper =  400;
const int step  =  100;

double fahr2cels( double f)
{
  return 5./9. * (f-32);
}

int main()
{
  int fahr;

  for ( fahr = lower; fahr <= upper; fahr += step )
  {
    printf( "Fahr = %4d,\tCels = %7.2f\n", 
                        fahr, fahr2cels(fahr) );
  }
  return 0;
}

A C programok szerkezete

Alapvető tokenek

/*
 * my first C program    <--- comment 
 *
 */
#include <stdio.h>       //<---- preprocessor directvive

int main()               // int    <-- type name: keyword
                         // main   <-- function name: identifier
                         // ()     <-- function call: operator
{                        // {      <-- block begin: separator
                         // printf <-- function name: identifier
                         // (      <-- function call: operator
  printf("Hello world"); // "Hello world"  <-- string literal, type char[12]
                         // )      <-- function call: operator
                         // ; <-- command-end separator
                         // return <-- keyword
  return 0;              // 0      <-- decimal int literal, type int
}                        // }      <-- block end: separator

include

Relációs jelek (<>) helyett idézőjelek (""): jelenlegi mappából tölt be, nem a szabványos helyről.

Szintaktikai elemek

  • Kulcsszó (keyword)
    • A nyelvben előre definiált jelentéssel bír
  • Azonosító (identifier)
    • Csak az adott kontextusban bír jelentéssel, jellemzően futási időben kap jelentést
  • Szeparátor
  • Literál
  • Komment
    • Fordítási időben ignorálásra kerül, erősen tájékoztató jellegű

Szintaktikus/Sin-patikus csapat!

Azonosítók

  • betűvel kezdődnek (_ is betű)
  • betűk és számok
  • TILOS kulcsszót használni

Megszokott konvenciók

  • camelCaseNotation
  • CTypenamesStartsWithUpperCase
  • under_score_notation or snake_case
  • STRING_HUNGARIAN_NOTATION also known as.. oops it got stolen
Megnevezés példa típus értéke
decimális egész 25 int 25
oktális egész 031 int 25
hexadecimális egész 0x19 int 25
hosszú egész 12L long int 12
C99 még hosszabb egész 12LL long long int 12
előjel nélüli egész 12u unsigned int 12

Legtöbb nyelv kettes-komplemens használ

  • Előjel nélküli egész szám (unsigned): csak pozitív számokat tud ábrázolni
  • Előjeles egész szám (signed): negatív számokat is tud ábrázolni, de feleannyi értéket tud felvenni

C - Típusok

Escape karakterek

Karakterek, amiknek bevitele a szerkesztőbe nem lehetséges, vagy a fordító tokenként értelmezné.

Például:

  • \' -> '
  • \" -> "
  • \? -> ?
  • \\ -> \
  • \a -> bell
  • \b -> backspace
  • \f -> új oldal
  • \n -> új sor
  • \r -> soremelés
  • \t -> horizontal tab
  • \v -> vertical tab

Integer

sizeof(short) <= sizeof(int) <= sizeof(long) <= sizeof(long long)

  • short min. 16 bit
  • long min. 32 bit
  • long long min. 64 bit

Fix és lebegőpontos számok (fix and floating point numbers)

Fix pontos: megadott bit számú tört rész

Lebegőpontos számok

Levegőpontos számok: nincs fixen megadva a "tizedes" hely. Helyette normálalak-szerű leírás.

IEEE 754 (lásd: SzámRend)