Je tombe des nues. Je savais pas que ce genre de chose était compilable :
Bizarre, toutes ces syntaxes. J'ai jamais vu ça dans la norme C.Code:
1
2
3
4
5
6
7
8
9 int main () { int a [5] ; int b = 2 ; 3[a] = 3 ; b[a] = 1 ; a[2][a] = 5 ; return 0 ; }
Version imprimable
Je tombe des nues. Je savais pas que ce genre de chose était compilable :
Bizarre, toutes ces syntaxes. J'ai jamais vu ça dans la norme C.Code:
1
2
3
4
5
6
7
8
9 int main () { int a [5] ; int b = 2 ; 3[a] = 3 ; b[a] = 1 ; a[2][a] = 5 ; return 0 ; }
C'est une conséquence de l'équivalence entre : a[i] et *((a) + (i)) donc avec *((i) + (a)) d'où i[a].
J'ai toujours été sceptique quant à ça, mais malheureusement pour moi je ne suis pas encore tombé sur un compilateur qui rejette ça.
D'ailleurs il serait intéressant de savoir dans quel cas cela planterait, si quelqu'un veut bien partager son savoir à ce sujet :mrgreen: .
On ne peut jamais savoir quand ça plantera, quoi qu'on écrive.
La règle est simple: si ce qu'on écrit est correct (qu'on ne dépasse pas la zone allouée, qu'on n'utilise pas un pointeur non initialisé, etc etc etc) alors le programme se déroule correctement.
En revanche si ce qu'on écrit n'est pas correct, alors (en dehors des cas bateau comme division par 0) on a un comportement indéterminé => non prévisible (dans la plus large acceptation de ce que ce terme signifie) => ça peut éventuellement planter mais rien ne le garantit.
C'est évident, mais l'expression 3[a] ne cache-t-elle pas derrière elle un comportement indéterminé ou dépendant de l'implémentation ?
Surtout qu'on n'a pas la certitude qu'un pointeur a une valeur numérique, et que a[3] sera automatiquement convertie en *((a) + (3)) (on a la certitude que les deux expressions sont équivalentes par la norme mais pas que l'une est ramenée automatiquement à l'autre).
Autrement dit, si a[3] est garantie être ramenée à *((a) + (3)) alors oui effectivement il n'y a pas de soucies à se faire pour 3[a] (grâce à l'arithmétique des pointeurs).
J'aime beaucoup! :mrgreen:Citation:
a[2][a] = 5;
Tout comme l'exemple du swap, c'est original. Bien que je trouve que celui-ci n'ait rien d'étrange, ou d'inconnu. Vu qu'il s'agit simplement d'opération binaire répétée ^^
@uknow
Il me semble bien que la norme spécifie aussi le fait que:
tab[x] <=> x[tab]
Il n'y a donc pour moi pas de comportement indéterminé ici.
EDIT:
1) Je sais pas qui a voté pour la conversation, mais c'est chouette.
En espérant avoir plein d'autres exemples!
2) Sinon, oui le but de ma conversation est d'obtenir des syntaxes étranges, inconnues...
Et pas d'avoir du code illisible, ca c'est pas trop dur! :)
@uknow :
C'est absolument garanti : la norme définit l'opérateur [] par la propriété "a[b] est identique à (*((a)+(b)))"Citation:
Autrement dit, si a[3] est garantie être ramenée à *((a) + (3)) alors oui effectivement il n'y a pas de soucies à se faire pour 3[a] (grâce à l'arithmétique des pointeurs).
Peut-on classer ceci comme une étrangeté ?
Dans certains cas, l'expression a+b-c est rejetée par le compilateur alors que a-c+b esr acceptée.
Pourquoi ?
(Experts du langage, abstenez-vous de répondre, ce ne serait pas du jeu)
Peux tu préciser, les certains cas dont tu parles? Ou en donner un?Citation:
Dans certains cas, l'expression a+b-c est rejetée par le compilateur alors que a-c+b esr acceptée.
Car normalement les opérateurs '+' et '-', ont la même priorité.
Je ne vois donc pas de raison pour lesquels ceci serait incorrect.
A mon avis, s'il donne un exemple alors on trouvera de suite donc c'est pour ça qu'il n'en donne pas.
C'est vrai que les opérateurs ont même priorité mais au niveau exécution, ils sont exécutés l'un après l'autre, avec cast implicite lors du calcul si les deux opérandes ne sont pas de même type. Et peut-être justement que a+b ne peut pas se faire cause cast impossible. Mais bon, c'est mon idée mais j'ai pas d'exemple qui s'y rapporte.
En fait, cette idée est tirée de cet exemple
On pourrait croire que l'opération se passera dans le type le plus large des opérandes, (ici 10.0 de type double) donc on pourrait croire que le résultat sera comme 3.0 / 2.0 = 1.5 * 10.0 = 15.0Code:double res=3 / 2 * 10.0
Or, la première opération 3 / 2 se passant entre deux int, l'opération se fera en int => résultat 1. Puis, 1 multiplié par 10.0 donnera 10.0.
Ptet que le a+b-c et a-c+b de Diogène c'est un peu le même principe...
@Sve@r :
Effectivement.Citation:
A mon avis, s'il donne un exemple alors on trouvera de suite donc c'est pour ça qu'il n'en donne pas.
Effectivement.Citation:
Et peut-être justement que a+b ne peut pas se faire
Un dernier indice :
Dans certains cas, l'expression a+b-c est rejetée par le compilateur alors que a-c+b ou a+(b-c) sont acceptées.
Pourquoi ?
@gl : :ccool:
:applo:
Pourrait-on avoir un exemple s'il te plaît?Citation:
@gl :ccool:
Si je ne dis pas de bêtises, l'arithmétique des pointeurs autorise :
- l'addition/soustraction d'une valeur entière à un pointeur
- la soustraction de deux pointeurs de même type (qui donne une valeur entière de type ptrdiff_t)
L'addition de deux pointeurs n'est pas autorisé (et n'a pas beaucoup de sens finalement) ce qui fait que l'expression a+b-c ne peut être compilée (a+b étant évaluée en premier).
Code:
1
2
3
4
5
6
7 int a[1]; int *b=a; int *c=a; printf("%p\n", a - b + c); // ok printf("%p\n", a + (c - b)); // ok printf("%p\n", a + c - b); // Pb
Accessoirement, je ne sais pas si on peut classer ça dans les bizarreries, mais ça me fait penser à ceci
Code:
1
2
3
4
5
6
7 int *a=NULL; int *b=malloc(sizeof(int)); int *c; printf("%p\n", a); // ok, NULL est une valeur valide printf("%p\n", b); // ok, malloc a renvoyé une valeur valide (une adresse allouée ou NULL) printf("%p\n", c); // Comportement indéterminé - c ne contient pas de valeur valide (même s'il contient forcément quelque chose) et aller voir, donc ne serait-ce que sa valeur, est interdit
Citation:
Citation:
Code c :Citation:
Envoyé par ugo188 Voir le message
Pourrait-on avoir un exemple s'il te plaît?
Code:
1
2
3
4
5
6
7
8 int a[1]; int *b=a; int *c=a; printf("%p\n", a - b + c); // ok printf("%p\n", a + (c - b)); // ok printf("%p\n", a + c - b); // Pb
Merci pour cet exemple!Citation:
Si je ne dis pas de bêtises, l'arithmétique des pointeurs autorise :
* l'addition/soustraction d'une valeur entière à un pointeur
* la soustraction de deux pointeurs de même type (qui donne une valeur entière de type ptrdiff_t)
L'addition de deux pointeurs n'est pas autorisé (et n'a pas beaucoup de sens finalement) ce qui fait que l'expression a+b-c ne peut être compilée (a+b étant évaluée en premier).
Car en effet, ca prend tout son sens! :mouarf:
Je pense que l'une des raisons est que, contrairement en mathématique, chez les ordinateurs l'addition, la soustraction (et les autres opérations arithmétiques d'ailleurs) ne sont pas des lois de compositions internes dans l'ensemble limité des entiers qu'ils (les ordis) peuvent manipuler. L'addition et la soustraction ne sont pas commutatives non plus.
Les types de données (int, char, ...) ayant une taille fixe et donc un nombre maximal qu'il peuvent stocker, il se pourrait que dans a + b - c, (a + b) provoque inévitablement un dépassement de capacité détecté par le compilateur, qui jugera alors inutile de réaliser toute l'opération a + b - c (considérant que son résultat sera erroné). en revanche (a - c) peut être valide et son résultat additionné à b peut rester valide aussi. Le compilateur se taira.
bref en mathématique, 255 + 1 - 1 = 255 - 1 + 1 (commutativité)
mais en langage C (et en général avec les ordi) si vos types sont des unsigned char,
255 + 1 - 1 != 255 - 1 + 1
car 255 + 1 provoquera un dépassement....
En général, le compilateur ne peut pas déterminer si il y a dépassement de capacité ou non. Celui-ci n'est évident qu'à l'exécution.Citation:
(a + b) provoque inévitablement un dépassement de capacité détecté par le compilateur
Les possibilités de dépassement de capacité sont les mêmes que pour a+bCitation:
en revanche (a - c) peut être valide
En C, si a, b et c sont unsigned char, ils seront automatiquement converti en int avant de faire l'opération (et le résultat sera un int). Il n'y aura pas de dépassement.Citation:
mais en langage C (et en général avec les ordi) si vos types sont des unsigned char,
255 + 1 - 1 != 255 - 1 + 1
car 255 + 1 provoquera un dépassement....
Merci pour toutes ces précisions, j'aurai quand même pu tester tout ce que j'ai débité avant d'écrire...:aie:
Et puis, même si on prend n le plus grand unsigned possible (2^32 dans la plupart des cas), alors n + 1 dépasse la taille maximale utilisée pour le calcul mais ce qui dépasse est tronqué et on ne garde que ce qui ne dépasse pas => dans ce cas 0.
Et ensuite, 0 - 1 donnera (en hexa) 0xffffffff (si on reste dans l'exemple initial du long à 32 bits) mais comme on est en unsigned, cela sera retraduit en n
Bref en fait, qu'il y ait dépassement ou pas, ici comme en maths, n + 1 - 1 = n - 1 + 1 :mouarf:
La soustraction n'a jamais été commutative :mouarf2::lahola: