OVH Cloud OVH Cloud

Implementation de qsort

76 réponses
Avatar
candide
Bonjour !

Plauger implémente qsort en déclarant un tableau local d'une taille de
256 bytes. Ce tableau sert à faire des transpositions (swap) de clés.
Mais comme la taille size des données est inconnue, le code doit, le cas
échéant s'y prendre en plusieurs fois pour terminer le swap, ce qui doit
être assez couteux. Alors je me demandais pourquoi il n'avait pas décidé
d'utiliser malloc pour éviter toutes ses contorsions. Quelque chose dans
la norme l'interdit-t-il ? En plus, il s'agirait d'allouer juste une
fois un buffer dynamique qui permet de faire un swap de façon classique.

10 réponses

4 5 6 7 8
Avatar
Antoine Leca
En news:492bdb97$0$6693$, candide va escriure:
-ed- a écrit :


[ et j'ai simplifié ]
int main (void) {
long x[4];
char *p = (char *) x + 1;
int *pi = (int *) p;
*pi = 0; /* accès 16-bit a une adresse impaire : BERR */
return 0; }





Est-il certain qu'il s'agisse bien d'un problème d'alignement ?



Oui. Sur une machine sans contrainte d'alignement, ce code passe sans souci.
Sur une machine qui a de telles contraintes, cela hurle.

En fait, il y a même des machines où cela sera refusé à la compilation
(notament si les pointeurs vers int sont moins larges que les pointeurs vers
char).



En quoi ai-je bien le droit d'affecter 0 à l'adresse pi



???

Tu assignes 0 à l'*objet* référencé par le pointeur pi, qui est aussi
l'objet référencé par le pointeur x, qui a la même adresse que le multiplet
(/byte/) qui suit le début de x, autrement dit quelque part dans la
représentation de x[0] (et peut-être de x[1], sur une machine 32 bits).

S'il n'y a pas de problème d'accès arbitraire à la mémoire, tu vas écraser
certaines des bits de x[0], mais la machine va te laisser faire. S'il y en
a, la machine va te rebrouer.


et ce malgré le cast en int * lors e l'initialisation?



Le transtypage en int* n'est là que pour faire taire le compilateur.


Et si c'est un problème d'alignement, quel § de la norme n'est pas


respecté ?

6.2.3.2p7, deuxième phrase.


Antoine
Avatar
espie
In article <492be1ab$0$14756$,
candide wrote:

Ce qu'il faut bien comprendre quand quelqu'un produit un énoncé entaché
d'une erreur, c'est que le comportement du lecteur peut être
complètement indéterminé (un lecteur de fclc doit comprendre ce genre
d'image).



La plupart du temps, on considere que candide ne constitue pas une
implementation conforme d'un lecteur valide de fclc. ;-)
Avatar
candide
Pierre Maurette a écrit :

Est-il certain qu'il s'agisse bien d'un problème d'alignement ? En quoi
ai-je bien le droit d'affecter 0 à l'adresse pi , et ce malgré le cast
en int * lors e l'initialisation?



?????



Si tu mets autant de points d'interrogation c'est que, à défaut d'avoir
beaucoup de C à apprendre, tu en as beaucoup à apprendre en matière
d'apprentissage du C.

pi est un int*, initialisé par un int* (le cast d'un char* en int*). *pi
est donc un int.



Je sais bien mais avec ce genre d'argument on va faire comprendre que

char *p;
*p='C';

est légitime.

Un programmeur du dimanche comme moi met des valeurs entières à des
adresses censées accueillir des entiers de la même façon que de toi tu
ne vas pas prendre ton dîner dans ta salle de bain (sauf circonstances
particulièrement exceptionnelles) mais plutôt dans ta salle-à-manger ou
dans ta cuisine.


Donc, la question que je me posais est la suivante: en quoi, malgré le
cast en int * suis-je _autorisé_ à écrire un entier à l'adresse pi ?

Je suppose que la réponse est la suivante :

La valeur de p est une adresse qui est un octet de plus que l'adresse de
la 1ère case du tableau x. En castant p en (int *), on peut placer à
cette adresse (pi donc) n'importe quel int comme il a d'ailleurs été
fait. Je suppose qu'on peut écrire un entier int car il doit y avoir la
place : 1+sizeof(int)<=4*sizeof(long).


Donc j'en conclus que, en castant et à partir du moment où il y a la
place, on peut écrire n'importe quoi n'importe où (ici un int à cheval
entre deux cases recevant des long), ce qui choque ma logique de
programmeur du dimanche, mais après tout il n'est pas interdit de faire
dans sa chambre-à-coucher ce qu'on pourrait faire dans sa cuisine et
inversement.

Et il est possible qu'à l'occasion de ce fil qui dérive j'ai pris
conscience de quelque chose.


On n'affecte pas 0 à pi.



J'ai dit : "à l'adresse pi".


On peut d'ailleurs affecter 0 à un pointeur, et on le fait très souvent.
Même la norme indique qu'il s'agit d'une des initialisations valides
d'un pointeur...



Aucun rapport, Emmanuel aurait très bien pu écrire 2014 au lieu de 0 (il
aurait du d'ailleurs, le choix des exemples étant tellement capital et
... difficile)



Et si c'est un problème d'alignement,
quel § de la norme n'est pas respecté ?



??????????
C'est un exemple de comportement sur une architecture. La norme n'a
justement rien à voir là-dedans.
Pour être plus précis, la norme "sait" que l'alignement est une notion
importante, mais elle ne peut pas le prendre en charge de façon
portable.



Après lecture un peu au hasard de ma norme C90, je tombe sur ceci :

"A pointer to an object or incomplete
type may be converted to a pointer to a different object type or a
different incomplete type. The resulting pointer might not be valid
if it is improperly aligned for the type pointed to. "

ce qui répond à ma question.


Figure-toi que dans ma candeur j'ai assimilé un bus error à la
conséquence d'un comportement indéterminé et j'avais donc toutes les
raisons de penser que la norme était explicite sur ce point.
Avatar
candide
Marc Boyer a écrit :
On 2008-11-25, candide wrote:
[Si quelqu'un a un exemple du même genre sur x86, qu'il le donne.]



Je l'ai fait dans mon post dans ce fil de discussion.

int main(){
char t[sizeof(double)*2];
double* d= (double*) (t+1);
__asm__("pushfl; popl %eax; orl $0x40000,%eax; pushl %eax; popfl;");
*d= 1.0;
return 0;
}



Oui, j'avais lu mais je m'attendais pas à trouver de l'assembleur dans
le code C.


Comme déjà dit, par défaut, les processeurs x86 acceptent les accès
non alignés, au prix d'une perte de performances.



Oui mais j'aurais voulu, je sais pas comment dire, pas un
ralentissement, quelque chose d'interdit, d'invalide, qui engendre un
plantage (et écrit en C, pas en asssembleur).





ai-je bien le droit d'affecter 0 à l'adresse pi , et ce malgré le cast
en int * lors e l'initialisation?



Mais tu n'as pas le droit justement.




Je ne comprends pas, tu dis le contraire de ce que dit Pierre Maurette
alors ?
Avatar
candide
Antoine Leca a écrit :
En news:492bdb97$0$6693$, candide va escriure:
-ed- a écrit :


[ et j'ai simplifié ]
int main (void) {
long x[4];
char *p = (char *) x + 1;
int *pi = (int *) p;
*pi = 0; /* accès 16-bit a une adresse impaire : BERR */
return 0; }





Est-il certain qu'il s'agisse bien d'un problème d'alignement ?



Oui. Sur une machine sans contrainte d'alignement, ce code passe sans souci.



J'ai essayé sur mon PC et rien, hélas.


Sur une machine qui a de telles contraintes, cela hurle.

En fait, il y a même des machines où cela sera refusé à la compilation
(notament si les pointeurs vers int sont moins larges que les pointeurs vers
char).



Donc, au départ, ce n'est pas même pas du code portable, c'est bien cela
que tu nous dis ? S'il en est ainsi, je dis "vive la portabilité !" et
"vive la norme", ça évite de se poser des questions, ce qui augmente
notre espérance de vie.


En quoi ai-je bien le droit d'affecter 0 à l'adresse pi



???

Tu assignes 0 à l'*objet* référencé par le pointeur pi, qui est aussi
l'objet référencé par le pointeur x,



C'est le même objet ? je comprends plus rien. L'objet référencé par pi
c'est bien une suite de 4 long et l'objet référencé par pi c'est un int,
non ? donc c'est pas pareil.

qui a la même adresse que le multiplet
(/byte/) qui suit



qui suit ? à cause du +1 dans

char *p = (char *) x + 1;

c'est bien ça ?


le début de x, autrement dit quelque part dans la
représentation de x[0] (et peut-être de x[1], sur une machine 32 bits).




Oui c'est ce que j'ai fini par comprendre, enfin je crois. L'entier *pi
est bien à cheval entre deux case du tableau x, c'est ça ?


OK, mais moi, en tant que personne bien éduqué, je pensais que je
mettais un entier dans une case prévu à cet effet et pas n'importe où.



S'il n'y a pas de problème d'accès arbitraire à la mémoire,



Je vois pas à quoi tu fais allusion.


tu vas écraser
certaines des bits de x[0], mais la machine va te laisser faire.



Ah, OK. Drôle de moeurs quand même.


S'il y en
a, la machine va te rebrouer.




Une histoire de taille trop petite ...



et ce malgré le cast en int * lors e l'initialisation?



Le transtypage en int* n'est là que pour faire taire le compilateur.




OK mais surtout ça permet de faire n'importe quoi. Le problème
finalement c'est que je sais plus si je fais des choses autorisées ou pas.




Et si c'est un problème d'alignement, quel § de la norme n'est pas


respecté ?

6.2.3.2p7, deuxième phrase.




Pas trouvé cette référence dans mon exemplaire pdf (ISO/IEC 9899:1999):

6.2.3 Name spaces of identifiers


La seule chose que j'aie trouvée est ce que j'ai cité dans ma réponse à
Pierre Maurette.
Avatar
candide
Marc Espie a écrit :

La plupart du temps, on considere que candide ne constitue pas une
implementation conforme d'un lecteur valide de fclc. ;-)




Assurément. Néanmoins, je pense me sentir très proche de l'immense
majorité des étudiants qui apprennent le C à un niveau ou à un autre (je
t'en parle aisément car j'en connais un certain nombre).

Je vois le C comme un magnifique terrain d'exploration des mécanismes
d'apprentissage. Pour moi, son degré de complexité est à un niveau
idéal. Un langage de programmation est un objet relativement simple mais
quand même assez complexe (comparer à une langue le exemple). Le C est
un objet relativement simple et circonscrit (comparer au C++) mais quand
même assez complexe (on sait de quoi il est capable).
Avatar
Marc Boyer
On 2008-11-26, candide wrote:
Marc Boyer a écrit :
On 2008-11-25, candide wrote:
[Si quelqu'un a un exemple du même genre sur x86, qu'il le donne.]



Je l'ai fait dans mon post dans ce fil de discussion.

int main(){
char t[sizeof(double)*2];
double* d= (double*) (t+1);
__asm__("pushfl; popl %eax; orl $0x40000,%eax; pushl %eax; popfl;");
*d= 1.0;
return 0;
}



Oui, j'avais lu mais je m'attendais pas à trouver de l'assembleur dans
le code C.


Comme déjà dit, par défaut, les processeurs x86 acceptent les accès
non alignés, au prix d'une perte de performances.



Oui mais j'aurais voulu, je sais pas comment dire, pas un
ralentissement, quelque chose d'interdit, d'invalide, qui engendre un
plantage (et écrit en C, pas en asssembleur).



Dans le langage de la norme C, le code que j'ai écrit (sans le __asm__)
est un Undefined Behavior. Est-ce le sens que tu donnes à "interdit" ?
Quand à "engendrer un plantage", ben voilà, sous x86 tel que configuré
par défaut sur la plupart des OS, les erreurs d'alignement n'en provoquent
pas. Le code assembleur que j'ai introduit dans le C ne provoque pas
l'erreur d'alignement, il dit juste au processeur de provoquer un plantage
quand il y a une erreur d'alignement au lieu de tout rattrapper tout seul
comme un grand.

Je me souviens avoir fait des TP sous Win98 (ou 95?) avec la version
de l'époque de VC++, et on pouvait déréférencer le pointeur NULL sans
provoquer le moindre "plantage".

ai-je bien le droit d'affecter 0 à l'adresse pi , et ce malgré le cast
en int * lors e l'initialisation?



Mais tu n'as pas le droit justement.



Je ne comprends pas, tu dis le contraire de ce que dit Pierre Maurette
alors ?



Je ne retrouve pas le message de Pierre où il dit cela.

Marc Boyer
--
En France, un habitant sur 1000 est en prison.
Aux USA, 7 habitants sur 1000 sont en prison.
Est-ce que les USA sont 7 fois plus sûrs ?
Avatar
candide
Marc Boyer a écrit :

Dans le langage de la norme C, le code que j'ai écrit (sans le __asm__)
est un Undefined Behavior. Est-ce le sens que tu donnes à "interdit" ?



Ben je sais plus trop ce qui est permis et ce qui ne l'est pas ;)



Quand à "engendrer un plantage", ben voilà, sous x86 tel que configuré
par défaut sur la plupart des OS, les erreurs d'alignement n'en provoquent
pas. Le code assembleur que j'ai introduit dans le C ne provoque pas
l'erreur d'alignement, il dit juste au processeur de provoquer un plantage
quand il y a une erreur d'alignement au lieu de tout rattrapper tout seul
comme un grand.




Ah, OK.



Je ne retrouve pas le message de Pierre où il dit cela.





C'était là mais non explicite :

Est-il certain qu'il s'agisse bien d'un problème d'alignement ? En quoi
ai-je bien le droit d'affecter 0 à l'adresse pi , et ce malgré le cast
en int * lors e l'initialisation?



?????
pi est un int*, initialisé par un int* (le cast d'un char* en int*). *pi
est donc un int. On n'affecte pas 0 à pi.
On peut d'ailleurs affecter 0 à un pointeur, et on le fait très souvent.
Même la norme indique qu'il s'agit d'une des initialisations valides
d'un pointeur...





mais probablement ai-je mal compris sa réponse ce qui n'est que la
continuation du fait qu'il a mal compris ma question qui fait suite à ma
mauvaise interprétation d'une de ses phrases laquelle est due selon moi
à l'utilisation par lui d'une formulation inappropriée et non d'une
fausse candeur. Donc autant en rester là.
Avatar
Marc Boyer
On 2008-11-26, candide wrote:
Marc Boyer a écrit :
C'était là mais non explicite :

Est-il certain qu'il s'agisse bien d'un problème d'alignement ? En quoi
ai-je bien le droit d'affecter 0 à l'adresse pi , et ce malgré le cast
en int * lors e l'initialisation?



?????
pi est un int*, initialisé par un int* (le cast d'un char* en int*). *pi
est donc un int. On n'affecte pas 0 à pi.
On peut d'ailleurs affecter 0 à un pointeur, et on le fait très souvent.
Même la norme indique qu'il s'agit d'une des initialisations valides
d'un pointeur...




mais probablement ai-je mal compris sa réponse ce qui n'est que la
continuation du fait qu'il a mal compris ma question qui fait suite à ma
mauvaise interprétation d'une de ses phrases laquelle est due selon moi
à l'utilisation par lui d'une formulation inappropriée et non d'une
fausse candeur. Donc autant en rester là.



Je pense qu'il y a en effet incompréhension sur "affecter 0 à pi".
AMHA, il traduit ce bout de français par
pi= 0
ce qui est valide, si pi est un pointeur.
int* pi;
Quand à "affecter 0 à l'adresse pi", c'est plus ambigue. Je l'ai
traduit par
*pi= 0;
ce qui n'est valide qui si on a le droit de déréférencer pi.

Marc Boyer
--
En France, un habitant sur 1000 est en prison.
Aux USA, 7 habitants sur 1000 sont en prison.
Est-ce que les USA sont 7 fois plus sûrs ?
Avatar
-ed-
On 25 nov, 12:03, candide wrote:


[Si quelqu'un a un exemple du même genre sur x86, qu'il le donne.]



Il a pourtatnt été bien expliqué que ça ne se produisait pas sur x8 6,
mais ça ça ralentissait le bazar...


Est-il certain qu'il s'agisse bien d'un problème d'alignement ?



Oui.

En quoi
ai-je bien le droit d'affecter 0 à l'adresse pi ,



Ce n'est pas ce que je fais. J'écris 0 à l'adresse pointée par pi. Tu
ne sais plus lire le C ?

et ce malgré le cast
en int * lors e l'initialisation? Et si c'est un problème d'alignement,
quel § de la norme n'est pas respecté ?



Bah, codage de goret (3 cast, quand même pour y arriver)->
comportement indéfini. Ça te suffit ?
4 5 6 7 8