Variadiques

Dans la premiere version d’UNIX les variadiques etaient gerees en assumant la location des arguments en memoire d’un PDP9.

La maniere portable et standart est d’utiliser une ellipse.

Macros variadiques aussiS

Dans unix, on utile un pointeur sur le premier argument et on le fait croitre

https://www.tuhs.org/cgi-bin/utree.pl?file=V3/c/c03.c 🌍⤴

Voici printf sur UNIX v3

printn(n,b) {
	extern putchar;
	auto a;

	if(a=n/b) /* assignment, not test for equality */
		printn(a, b); /* recursive */
	putchar(n%b + '0');
}

printf(fmt,x1,x2,x3,x4,x5,x6,x7,x8,x9)
char fmt[]; {
	extern printn, putchar, namsiz, ncpw;
	char s[];
	auto adx[], x, c, i[];

	adx = &x1; /* argument pointer */
loop:
	while((c = *fmt++) != '%') {
		if(c == '\0')
			return;
		putchar(c);
	}
	x = *adx++;
	switch (c = *fmt++) {

	case 'd': /* decimal */
	case 'o': /* octal */
		if(x < 0) {
			x = -x;
			if(x<0)  {	/* - infinity */
				if(c=='o')
					printf("100000");
				else
					printf("-32767");
				goto loop;
			}
			putchar('-');
		}
		printn(x, c=='o'?8:10);
		goto loop;

	case 's': /* string */
		s = x;
		while(c = *s++)
			putchar(c);
		goto loop;

	case 'p':
		s = x;
		putchar('_');
		c = namsiz;
		while(c--)
			if (*s)
				putchar((*s++)&0177);
		goto loop;
	}
	putchar('%');
	fmt--;
	adx--;
	goto loop;
}

Naviguer entre variadiques consiste à faire de l’arithmétique de pointeurs.

Incompatibilité

A noter qu’une 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.

Pour résumer

int func1(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;
func1(a, b);
func2(a, b);
func3(a, b);
func4(a, b);
// ces 4 appels de fonction peuvent tous générer
// un code binaire différent

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