Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

Drole de main()

33 réponses
Avatar
David Remacle
Salut,

En me documentant pour un petit historique des Os d'Apple, je suis tombé
sur une image de A/UX (l'un des premier Unix d'Apple) avec un drole de
façon de déclarer main.

http://www.kernelthread.com/publications/appleoshistory/images/aux.gif

J'ai bien sur essayé de voir dans mon codeblocks si ca compile. et oui
ça compile mais tout de même je me pose des questions sur cette façon de
présenter main.

A bientôt.

10 réponses

1 2 3 4
Avatar
Samuel Devulder
Marc Espie a écrit :
In article <4b2c0029$0$14550$,
Samuel Devulder wrote:
Peux tu préciser en quoi c'est piégeux? J'avoue ne pas suivre ici. Tu
veux dire que les conventions ABI sont différentes entre K&R et ANSI?



Ben oui, les conventions sont differentes. En particulier, le compilo, s'il



ok.

a un prototype, peut optimiser les choses. Si tu passes plein de char a une
fonction, avec un prototype, le compilo peut tres bien te les coller sur la
pile serres les uns contre les autres, alors qu'avec l'ancienne declaration
il sera force de les etendre en int.

Si tu ne vois pas le probleme, c'est juste que tu n'as pas regarde assez loin.



De ce que tu explique, je comprends que c'est consistant, et c'est pour
ca que je ne vois pas de problèmes en mode 100% K&R.

Je ne te parle pas d'une fonction, mais d'un programme entier, censement
portable, ou tu vas convertir plusieurs centaines de fonctions d'un monde
a l'autre. Si tu le fais a la main, tu as de fortes chances de te planter sur
au moins quelques-unes.



Dison qu'on va se planter en loupant la bonne déclaration du proto
quelque part. Mais il y a des flags du compilo pour raler dans ces
circonstances.

C'est pas pire que si dans deux fichiers C on appellait la même
fonction, mais avec des prototype différents.

f1.c:
extern double f(double a, double b);
...f(1.2, 3.4);...

f2.c:
extern char f(char a, char b);
...f('a', 'b');

f3.c:
int f(int a, int b) {return a*b;}

Comme la signature de la fonction n'intervient pas dans l'ABI, et que le
namespace est global, le linker va appeller f avec la mauvaise structure
de pile ou registre.. et bingo.. crash! C'est d'ailleurs pour cela qu'il
veut mieux mettre l'extern dans un .h unique et séparé de sorte à ce que
chaque fonction ait un et un seul prototype.

Et comme Murphy est toujours avec nous, tu es a peu
pres certain que ca va marcher sur ta machine courante, mais pas sur une
autre. Cas vecu: i386, amd64, ppc. Et paf le ppc.



Oui si mixer K&R et ANSI c'est pas bon.

sam (merci pour les explications).
Avatar
espie
In article ,
JKB wrote:
autre. Cas vecu: i386, amd64, ppc. Et paf le ppc.



Je rajouterai : "et paf le sparc !"

Il suffit de regarder mono, seamonkey, enfin tous ces trucs codés
sur i386/amd64 qui buserrorent à donf sur ces problèmes ;-)



Oui, non. Ne melange pas tout.

Les problemes de segfault des sparc proviennent le plus souvent d'un gros
melange entre entiers et pointeurs. Ils sont rarement, voire jamais,
lies a des vieilles definitions de fonctions K&R.

Cite-moi un exemple ou c'est le cas... mono et seamonkey sont bien trop
recents pour avoir des proto K&R.

Dans les vieux classiques a corriger, il y a tous les callbacks de la xlib
qui prennent des pointeurs, et des gens tres cradouilles qui ont abuse cette
interface pour y mettre des entiers (exemple: ghostview). Ou alors les
gens qui lisent des trucs directement depuis un fichier vers une structure,
et qui essaient de caster directement un bout de buffer a un offset arbitraire
vers un pointeur (exemple: unrar).

Non, je n'ai pas du tout choisi le cas ppc au hasard. C'est bien plus sournois.
Autant les bus error du sparc sont simples a diagnostiquer (et generalement
chiantes a corriger), autant les problemes de "signedness" du char, qui est
different sur ppc d'a peu pres toutes les autres architectures (sur les ABI
courantes, hein, puisque c'est un truc qui peut se changer avec des flags du
compilo) produit des problemes subtils et chiants a diagnostiquer lors de
ce type de conversion.
Avatar
espie
In article <4b2c9c2d$0$30391$,
Samuel Devulder wrote:
De ce que tu explique, je comprends que c'est consistant, et c'est pour
ca que je ne vois pas de problèmes en mode 100% K&R.

Je ne te parle pas d'une fonction, mais d'un programme entier, censement
portable, ou tu vas convertir plusieurs centaines de fonctions d'un monde
a l'autre. Si tu le fais a la main, tu as de fortes chances de te planter sur
au moins quelques-unes.



Dison qu'on va se planter en loupant la bonne déclaration du proto
quelque part. Mais il y a des flags du compilo pour raler dans ces
circonstances.



Non, tu n'as rien compris a ce qu'on fait.

Il s'agit generalement de moderniser du code, ce qui implique de changer
la definition de fonction pour se debarrasser du style K&R, et de rajouter
les prototypes qui vont bien.

Il n'y a pas de flag du compilo qui peut te donner les warnings qui vont
bien. Tu peux eventuellement proceder par etape (rajouter le prototype
d'abord, puis changer la definition), mais ca n'est pas intuitif quand meme,
et le compilateur ne t'aidera pas. L'exemple precis que je citais (passage
par un char) ne genere aucun warning meme si tu fais les choses de travers.

Pour:

f(a)
char a;
{
}

Le prototype correspondant est
extern void f(int);

et il faut que tu utilises celui-ci pour garder l'ABI (la, le compilo avertit
si tu te plantes).

Quand apres tu convertis la definition de fonction, tu ne peux pas te
contenter de:

void
f(int a)
{
}

ca depend de ce que tu fais dans la fonction avec a. Et le compilo n'avertira
pas, parce que int et char, c'est generalement interchangeable a l'interieur
de ton code, sauf que ca n'a pas la meme semantique en ce qui concerne les
extensions de signes et autres trucs rigolo.

Stricto sensu, il faut ecrire un truc du genre:
void(int a_)
{
char a = a_;
...
}

et verifier a la loupe si tu peux te debarrasser du a_...
Avatar
Samuel Devulder
Marc Espie a écrit :

Non, tu n'as rien compris a ce qu'on fait.



Oui je pense.. car ya un truc qui m'échappe.

Pour:

f(a)
char a;
{
}

Le prototype correspondant est
extern void f(int);

et il faut que tu utilises celui-ci pour garder l'ABI (la, le compilo avertit
si tu te plantes).



Question: pourquoi dans le cadre d'une ré-écriture ou un portage
tient-on à garder la compatibilité avec l'ancienne ABI? Si on change
tous les sources d'un coup il n'y a pas de pbs. Tout sera cohérent et
compatible. Je vois peu de risque la dedans.

Peut-être que tu me parles de la situation transitoire avec un mix K&R
et ANSI.. là oui mais bon c'est transitoire. Si le vieux code linké avec
la nouvelle libmachin.a plante pour des raisons de convention de passage
de params sur la pile, il faut mettre à jour ce vieux code et pas
essayer de l'hybrider avec des trucs modernes.

Enfin bon peut être que si tu convertis un bout et tu finis par devoir
convertir beaucoup plus que le source d'origine. C'est sur c'est du
boulot.. mais rien n'est gratuit/magique.

Mais quoi qu'il en soit ca n'est pas la syntaxe ou l'ABI K&R, mais le
mixe de deux conventions différentes et pas forcément compatible qui est
risqué.

Stricto sensu, il faut ecrire un truc du genre:
void(int a_)
{
char a = a_;
...
}

et verifier a la loupe si tu peux te debarrasser du a_...



Personne n'a eu l'idée de manipuler les AST (arbre syntaxique) du C pour
faire cela automatiquement? La transfo ne me semble pas super complexe
quand on a un bon AST du C (L'AST de Eclipse/CDT me semble utile pour
cela).

sam.
Avatar
Jean-Marc Bourguet
Samuel Devulder writes:

Marc Espie a écrit :

Non, tu n'as rien compris a ce qu'on fait.



Oui je pense.. car ya un truc qui m'échappe.

Pour:

f(a)
char a;
{
}

Le prototype correspondant est
extern void f(int);

et il faut que tu utilises celui-ci pour garder l'ABI (la, le compilo avertit
si tu te plantes).



Question: pourquoi dans le cadre d'une ré-écriture ou un portage tient-on à
garder la compatibilité avec l'ancienne ABI?



Pour n'avoir pas à changer toutes les sources d'un coup. (Parce que c'est
un gros boulot qu'on répartit sur une période longue plutôt que d'essayer
de vendre un "on arrête tout pendant 2 semaines le temps de faire ça",
parce qu'on veut pouvoir tester des étapes intermédiaires pour trouver plus
facilement un changement qui poserait problème -- ce problème pouvant être
existant mais maintenant demasqué --, parce qu'il y a des clients internes
ou externes qu'on ne peut pas forcer à changer avec le même planning).

Personne n'a eu l'idée de manipuler les AST (arbre syntaxique) du C pour
faire cela automatiquement? La transfo ne me semble pas super complexe
quand on a un bon AST du C (L'AST de Eclipse/CDT me semble utile pour
cela).



On parle d'un boulot qui s'est fait quand même pour l'essentiel au début
des années 90. (Même gcc a abandonnée l'idée d'être compilable en K&R et
essaie plutôt de se rendre compilable en C++).

A+

--
Jean-Marc
FAQ de fclc: http://www.levenez.com/lang/c/faq
Site de usenet-fr: http://www.usenet-fr.news.eu.org
Avatar
Samuel Devulder
Jean-Marc Bourguet a écrit :
Samuel Devulder writes:

Question: pourquoi dans le cadre d'une ré-écriture ou un portage tient-on à
garder la compatibilité avec l'ancienne ABI?



Pour n'avoir pas à changer toutes les sources d'un coup.



Oui ok.. On veut nettoyer un code, bref on tire un tout petit bout de
fil qui depasse, au au final on se rend compte qu'on tire toute la
pelotte derrière. Fallait pas commencer en fait :-)

On parle d'un boulot qui s'est fait quand même pour l'essentiel au début
des années 90. (Même gcc a abandonnée l'idée d'être compilable en K&R et
essaie plutôt de se rendre compilable en C++).



Oui ok.. mais les arbres syntaxiques existent depuis longtemps et ma foi
l'idée d'automatiser les opérations répétitives et dangereuses si faites
par un humain aurait pu émerger. Mais il est aussi probable que la
conversion de vieux source n'attire pas les foules. On préfère souvent
ré-écrire "en mieux" le truc d'avant. Mais bon souvent le mieux est
l'ennemi du bien.

sam.
Avatar
Wykaaa
Samuel Devulder a écrit :

[snip]

C'est pas pire que si dans deux fichiers C on appellait la même
fonction, mais avec des prototype différents.

f1.c:
extern double f(double a, double b);
...f(1.2, 3.4);...

f2.c:
extern char f(char a, char b);
...f('a', 'b');

f3.c:
int f(int a, int b) {return a*b;}

Comme la signature de la fonction n'intervient pas dans l'ABI, et que le
namespace est global, le linker va appeller f avec la mauvaise structure
de pile ou registre.. et bingo.. crash! C'est d'ailleurs pour cela qu'il
veut mieux mettre l'extern dans un .h unique et séparé de sorte à ce que
chaque fonction ait un et un seul prototype.



Parce que certains font autrement ?
Ils ont été mal formés alors...
Avatar
Wykaaa
Samuel Devulder a écrit :
Marc Espie a écrit :

Non, tu n'as rien compris a ce qu'on fait.



Oui je pense.. car ya un truc qui m'échappe.

Pour:

f(a)
char a;
{
}

Le prototype correspondant est
extern void f(int);

et il faut que tu utilises celui-ci pour garder l'ABI (la, le compilo
avertit
si tu te plantes).



Question: pourquoi dans le cadre d'une ré-écriture ou un portage
tient-on à garder la compatibilité avec l'ancienne ABI? Si on change
tous les sources d'un coup il n'y a pas de pbs. Tout sera cohérent et
compatible. Je vois peu de risque la dedans.

Peut-être que tu me parles de la situation transitoire avec un mix K&R
et ANSI.. là oui mais bon c'est transitoire. Si le vieux code linké avec
la nouvelle libmachin.a plante pour des raisons de convention de passage
de params sur la pile, il faut mettre à jour ce vieux code et pas
essayer de l'hybrider avec des trucs modernes.



Tu n'as pas l'air d'avoir manipulé des applications C de plusieurs
centaines de milliers de lignes toi.

Enfin bon peut être que si tu convertis un bout et tu finis par devoir
convertir beaucoup plus que le source d'origine. C'est sur c'est du
boulot.. mais rien n'est gratuit/magique.



Et sur de grosses applications, ça va prendre plusieurs mois, voire
plusieurs années....

Mais quoi qu'il en soit ca n'est pas la syntaxe ou l'ABI K&R, mais le
mixe de deux conventions différentes et pas forcément compatible qui est
risqué.




Stricto sensu, il faut ecrire un truc du genre:
void(int a_)
{
char a = a_;
...
}

et verifier a la loupe si tu peux te debarrasser du a_...



Personne n'a eu l'idée de manipuler les AST (arbre syntaxique) du C pour
faire cela automatiquement? La transfo ne me semble pas super complexe
quand on a un bon AST du C (L'AST de Eclipse/CDT me semble utile pour
cela).



Il est sûr que si l'on veut faire "tout d'un coup" sur une très grosse
application, il va falloir automatiser.
Avatar
Samuel Devulder
Wykaaa a écrit :

Peut-être que tu me parles de la situation transitoire avec un mix K&R
et ANSI.. là oui mais bon c'est transitoire. Si le vieux code linké
avec la nouvelle libmachin.a plante pour des raisons de convention de
passage de params sur la pile, il faut mettre à jour ce vieux code et
pas essayer de l'hybrider avec des trucs modernes.



Tu n'as pas l'air d'avoir manipulé des applications C de plusieurs
centaines de milliers de lignes toi.



Ne présume pas plus que tu ne peux STP. Tu ne me connais pas. Compiler
et maintenir 100 000 lignes c'est rien du tout, mais rien de rien. Tiens
la semaine dernière je faisais de la compilation de plus de 60 millions
de lignes de C (et c'est un *PETIT* projet.) Tu sais que franchement tu
commences à etre ch*ant avec tes attaques ad hominem. Retournes sur ton
compilo C super optimisé (ton baton de marechal si je comprends) et
laisse les gens discuter tranquillement. Merci.


Enfin bon peut être que si tu convertis un bout et tu finis par devoir
convertir beaucoup plus que le source d'origine. C'est sur c'est du
boulot.. mais rien n'est gratuit/magique.



Et sur de grosses applications, ça va prendre plusieurs mois, voire
plusieurs années....



A ce compte là, on peut se demander s'il ne vaut pas mieux refaire. Si
ca prend plusieurs années il y a fort à parier que quand la conversion
sera finie, quelqu'un aura déjà recodé "from scratch" le truc ou que le
produit aura été remplacé par quelque chose de plus moderne.

sam.
Avatar
Samuel Devulder
Wykaaa a écrit :

Parce que certains font autrement ?
Ils ont été mal formés alors...



Je sais pas. Ca se rencontre, c'est tout. C'est probablement pas un
problème de formation, mais un problème de projet un peu vieux avec des
incohérences introduite par de trop nombreux intervenants. Un peu comme
ce thread peut-être.

sam (peut-etre....)
1 2 3 4