Heritage

Le C a initialement été crée comme brique logicielle d’un ensemble servant à construire la troisième version du système d’exploitation UNIX. Il s’articule donc avec les programme as dont les spécificités influent le langage C lui même : nom de variables et labels limitées à 8, visibilité externe, labels numérotés dits « de knuth ». Les descripteurs de fichiers stdin, stdout et stderr ouverts par le système d’exploitation viennent de l’environnement UNIX, de même que les arguments d’entrée (argv) et sortie (return depuis main()).

digraphes trigraphes

fonctions k&r

Avant le C99, on pouvait déclarer une fonction ainsi

void f(x) { // implicitement "int x"

}

Dans ce cas x est implicitement déclaré comme une variable de type int.

En C99 il faut l’écrire ainsi

void f(x) 
int x;
{

}

int func(p, p2)
void *p;
int  p2; /* <- optionnel en C89/90, mais pas en C99 */
{
  return 0;
}

Voir

Omettre le type dans un prototype est une erreur dans tous les cas

void f(x);

Voir https://bugs.llvm.org/show_bug.cgi?id=25110 🌍⤴

Variadiques anciennes

Déclarer une fonction de la sorte

int foo();

Signifie que cette fonction prend un nombre non spécifié d’arguments de type non spécifié. Cela est surtout utile lorsqu’on déclare un tableau de fonctions “polymorphique” de la sorte

void foo(int i);
void bar(char *a, double b);
void baz(void);

int main()
{
  void (*fn[])() = { foo, bar, baz };
  fn[0](5);
  fn[1]("abc", 1.0);
  fn[2]();
}

Ce qui permet de mélanger plusieurs pointeurs de fonctions dans un même tableau et pouvoir les appeller sans nécessiter de cast. Voir https://stackoverflow.com/a/1631781 🌍⤴

A noter qu’un variadique ancienne et une variadique avec la sysntaxe nouvelle void foo(int, ...), ne sont pas compatibles et ne sont pas tenues d’obeir aux memes conventions d’appel.

De la sorte la fonction int func(int arg0, double arg1); n’est pas tenu d’avoir la meme convention d’appel, d’usage des registres, etc. que la fonction int func2().

De la même façon int func3(...); (depuis le C23) ou int func4(int arg0, ...); peuvent avoir une ABI différentes des deux premières fonctions.

int func(int arg0, double arg1);
int func2();    // interdit depuis le C23
int func3(...); // interdit avant le C23
int func4(int arg0, ...);


int a = 0;
double b = 1;
func(a, b);
func2(a, b);
func3(a, b);
func4(a, b);
// les 4 appels précédents peuvent tous générer
// un code binaire différent

int(ptr*)() = func2;
ptr(2, 1.5); // ok
ptr = func;
ptr(2, 1.5); // /!\ comportement indéterminé

Bien que la syntaxe de fonction indéterminée soit obsolète en C23, il n’y a pas de syntaxe de remplacement qui permette d’avoir un pointeur de fonction générique de la meme façon qu’il existe un pointeur d’objet générique void*, jusqu’à l’adoption de la proposition n3556 : https://www.open-std.org/JTC1/SC22/WG14/www/docs/n3556.htm 🌍⤴

int par defaut, du B

variables auto

extern printf

compilateur à une seule passe (obligé de déclarer les prototypes de fonctions récursives en amont)

variables a 8 caracteres

variables de stack seulement en tete de fonction

absence de portee specifique aux tags des structures

as: interdiction des label et variables a nombres, gestion de extern, retrocompatibilité

beaucoup de comportement indefini a cause de la multitude d’archiotectures (negatif magnitude et signe, complement a un) aujourd’hui la norme en complement a deux reduit ce comportement indéfini.

ENIX: descripteurs de fichiers stdin, stdout, stderr automatiquement ouverts par le système au démarrage du programme.

Manuel turbo C : modern vs classic https://archive.org/details/bitsavers_borlandtur1987_10489658/page/n147/mode/2up?view=theater 🌍⤴