Déclaration de tableau : *tab = {1, 2}
Fermé
cest_pas_faux
-
Modifié le 9 août 2019 à 11:52
mamiemando Messages postés 33446 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 20 décembre 2024 - 12 août 2019 à 11:06
mamiemando Messages postés 33446 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 20 décembre 2024 - 12 août 2019 à 11:06
A voir également:
- Déclaration de tableau : *tab = {1, 2}
- Tableau croisé dynamique - Guide
- Tableau ascii - Guide
- Tableau word - Guide
- Trier tableau excel - Guide
- Tableau de raccourcis clavier - Guide
2 réponses
mamiemando
Messages postés
33446
Date d'inscription
jeudi 12 mai 2005
Statut
Modérateur
Dernière intervention
20 décembre 2024
7 812
Modifié le 12 août 2019 à 11:11
Modifié le 12 août 2019 à 11:11
Bonjour
Explication du problème
En fait il y a une différence entre déclarer ton tableau comme étant de type
Avec
Avec
Et c'est ce que montre ce petit programme :
... pour lequel la compilation renvoie :
... et dont le résultat est :
Ici on voit en particulier que
On pourrait se dire que comme t2 peut emmagasiner 8 octets et qu'un int ne fait que 4 octets, l'intégralité de
Conclusion
Remarques complémentaires sur les tableaux et le pointeurs
Bonne chance
Explication du problème
En fait il y a une différence entre déclarer ton tableau comme étant de type
int[]ou
int *
Avec
int[] t1 = {1, 2}, tout se passe "comme tu le crois". Le bloc
{1,2}est créé en mémoire, et on prend l'adresse qui correspond au début de ce bloc pour l'assigner à
t1. On n'a donc pas initialisé
t1avec le bloc
{1, 2}mais avec son adresse. Par la suite,
t1peut être casté en
int *car c'est une adresse, et donc on ne fait que changer le type du pointeur de
int[]</code c> vers <code>int *.
Avec
int * t2 = {3, 4}, il n'en est rien. On initialise un pointeur au sens strict, donc il faut qu'à droite on ait un pointeur. Quand le type assigné à un pointeur n'est pas un pointeur, alors seuls les 32 premiers bits sont pris en compte. Tout se passe donc comme si
{3, 4}était casté en
int32. Comme le type de {3, 4} n'a pas été déclaré (tel que
t1), le compilateur lui donne par défaut le type
int32[]. Donc ici,
t2sera initialisé avec la première "case", donc à
3.
Et c'est ce que montre ce petit programme :
#include <stdio.h> int main() { printf("sizeof(int) = %zu sizeof(int *) = %zu\n", sizeof(int), sizeof(int *)); int t1[] = {1, 2}; printf("t1 = %x\n", t1); int * t2 = {3, 4}; printf("t2 = %x\n", t2); return 0; }
... pour lequel la compilation renvoie :
toto.c: In function ‘main’:
toto.c:7:17: warning: initialization of ‘int *’ from ‘int’ makes pointer from integer without a cast [-Wint-conversion]
int * t2 = {3, 4};
^
toto.c:7:17: note: (near initialization for ‘t2’)
toto.c:7:20: warning: excess elements in scalar initializer
int * t2 = {3, 4};
^
toto.c:7:20: note: (near initialization for ‘t2’)
... et dont le résultat est :
sizeof(int) = 4 sizeof(int *) = 8
t1 = 4df4baf0
t2 = 3
Ici on voit en particulier que
t2est initialisé avec la copie bit à bit de
{3, 4}qui sera vu comme un
int, puisque
{3, 4}n'est pas un pointeur. Comme c'est assez cavalier,
gcca eu le bon goût de nous prévenir avec le message warning: initialization of ‘int *’ from ‘int’ makes pointer from integer without a cast [-Wint-conversion]
On pourrait se dire que comme t2 peut emmagasiner 8 octets et qu'un int ne fait que 4 octets, l'intégralité de
{3, 4}peut être intégralement recopiée bit à bit dans
t2. On initialiserait donc
t2à
3 * 8^4 + 4. Il n'en est rien seul la première case,
3a été copiée. Ceci confirme que le bloc à droite de
=a été casté en un scalaire (une valeur numérique si tu préfères) de type
int32. On comprend alors ce que gcc voulait dire par warning: excess elements in scalar initializer.
Conclusion
- Quand tu alloues un tableau d'entiers avec
malloc
utilisesint *
. - Quand tu initialises un tableau d'entiers avec un bloc de données constant (e.g.
{1, 2}
) utilisesint[]
. - Par la suite un pointeur obtenu par
int[]
etint *
ont sémantiquement le même comportement, mais restent de type différents. Tu auras donc besoin de caster unint[]
enint *
si tu souhaites utiliser une fonction qui prend unint *
en paramètre.
Remarques complémentaires sur les tableaux et le pointeurs
- Si tu es sensible à ce qui se passe en mémoire, évite le type
int
car sa taille effective dépend de l'architecture du système. Tu peux par exemple utiliserint32 *
après avoir inclu#include<stdint.h>
). Typiquement dans une application réseau, où les tailles dans un paquet sont normalisés, tu dois savoir ce qui se passe au bit près. - Comme ni
int[]
niint *
ne portent la taille du tableau (ni dans le type, ni en mémoire), une fonction qui prend un tel pointeur en paramètre aura besoin du nombre de cases dans ton tableau. - On ne passe en général jamais un
int[N]
avec N une valeur entière en paramètre d'une fonction : cela recopie les N entiers en pile. C'est donc "coûteux" car ça engendre des recopies inutiles. De plus, on ne passe plus le tableau par pointeur mais par recopie. Donc si la fonction tente de modifier le tableau, ce changement n'aurait lieu que sur la recopie. - Afin d'éviter de devoir passer systématiquement la taille des chaînes aux fonctions de
<string.h>
la convention choisie en C a consisté à définir un caractère d'arrêt'\0'
. Mais comme'\0'
vaut0
(voir table ascii), ce genre d'astuce ne peut pas être utilisé pour un tableau de valeur numérique. - Pendant qu'on est dans les chaînes, la syntaxe
"toto"
n'est pas équivalente{'t', 'o', 't', 'o', '\0'}
. Dans la première écriture, chaque lettre correspond bien à unchar
soit 1 octet. Dans la seconde écriture,{'t', 'o', 't', 'o', '\0'}
sera vu comme unint32[]
par le compilateur. Si tu initialises une chaîne avec ça, tu auras donc le même problème qu'avect2
, qui sera alors initialisé à(int) 't'
soit74
(voir tableau ascii). - On peut tout à fait utiliser la syntaxe avec les guillemets pour initialiser un pointeur "étoilé" comme un
char *
(exemple :const char * s = "bonjour";
).
Bonne chance
Dalfab
Messages postés
706
Date d'inscription
dimanche 7 février 2016
Statut
Membre
Dernière intervention
2 novembre 2023
101
9 août 2019 à 23:51
9 août 2019 à 23:51
Bonjour,
La question devrait plutôt être : pourquoi la ligne suivante compile ?
Cette ligne n'a aucun sens. La syntaxe {1,2} n'a sens que pour initialiser un tableau ou une structure. Mais ici il n'y a ni tableau ni structure!
Le compilateur doit émettre un gros warning et la traiter comme s'il avait vu :
La question devrait plutôt être : pourquoi la ligne suivante compile ?
int *a = {1, 2};
Cette ligne n'a aucun sens. La syntaxe {1,2} n'a sens que pour initialiser un tableau ou une structure. Mais ici il n'y a ni tableau ni structure!
Le compilateur doit émettre un gros warning et la traiter comme s'il avait vu :
int *a = 2;
Bonjour,
Mon but à la base était de parcourir un tableau de int* comme un tableau de char* :
Et ici, pareil, quand je faisais :
Il me crachait un segfault. Il n'y a pas une histoire comme quoi l'un est en lecture seule et l'autre expression est en lecture écriture ?
Mon but à la base était de parcourir un tableau de int* comme un tableau de char* :
#include <stdio.h>
int main ()
{
char a[] = {'a', 'b'};
for(int i = 0; i < 2; i++)
{
printf("%c\n", *(a + i));
}
return 0;
}
Et ici, pareil, quand je faisais :
char *a = {'a', 'b'};
Il me crachait un segfault. Il n'y a pas une histoire comme quoi l'un est en lecture seule et l'autre expression est en lecture écriture ?
Dalfab
Messages postés
706
Date d'inscription
dimanche 7 février 2016
Statut
Membre
Dernière intervention
2 novembre 2023
101
10 août 2019 à 10:48
10 août 2019 à 10:48
Il n'y a pas de différence d'utilisation pour
Dans tous les cas,
charet
int. Sauf qu'il existe une notation pratique pour un table de char souvent appelée "chaîne de caractères'.
Dans tous les cas,
char*ane crée pas un tableau, il peut pointer dessus. Pour
int*a, même réponse.
char a[] = {'a','b','c','\0'};// ok, a est un tableau char b[] = "abc"; // ok, notation chaîne pour un tableau de char char *c = "abc"; // toléré car "abc" est un tableau de 4 char constants et c pointe dessus const char *d = "abc"; // Ok, d pointe sur un tableau constant, notation possible char *e = {'a','b','c','\0'}; // aucun sens char *f = a; // ok, pointe sur un tableau char g[] = a; // interdit, copie de tableaux int h[] = {1,2,3,4}; // ok, h est un tableau int i[] = "abc"; // aucun sens (tableau d'int et tableau de const char) int *j = {1,2,3,4}; // aucun sens int *k = h; // ok, pointe sur un tableau int l[] = h; // interdit, copie de tableaux
Dalfab
Messages postés
706
Date d'inscription
dimanche 7 février 2016
Statut
Membre
Dernière intervention
2 novembre 2023
101
10 août 2019 à 21:41
10 août 2019 à 21:41
Les accolades sont des accolades et les guillemets sont des guillemets, donc ça n'est pas équivalent.
Et comme je l'ai écris dans tes 2 expressions a est un pointeur car l'étoile est une étoile. On la voit bien les 2 fois.
Ce qu'il faut savoir c'est que "abc" est un tableau de 4 caractères constants.
La seule exception comme écrit avant est que la ligne suivante est acceptée. C'est elle l'exception qui n'est pas logique.
On n'a normalement pas le droit d'écrire tableau=tableau, mais il y a exception ici. C'est compris comme : recopie le tableau "abcde" pour initialiser le tableau b (taille par défaut = 5+1).
Et comme je l'ai écris dans tes 2 expressions a est un pointeur car l'étoile est une étoile. On la voit bien les 2 fois.
Ce qu'il faut savoir c'est que "abc" est un tableau de 4 caractères constants.
La seule exception comme écrit avant est que la ligne suivante est acceptée. C'est elle l'exception qui n'est pas logique.
char b[] = "abcde"; // ok, notation chaîne pour un tableau de char
On n'a normalement pas le droit d'écrire tableau=tableau, mais il y a exception ici. C'est compris comme : recopie le tableau "abcde" pour initialiser le tableau b (taille par défaut = 5+1).
char a[4] = "abcde"; // erreur char b[5] = "abcde"; // ok (mais erreur en C++) char c[6] = "abcde"; // ok vaut "abcde\0" char d[9] = "abcde"; // ok vaut "abcde\0\0\0\0" char e[] = "abcde"; // ok vaut "abcde\0" const char* f = "abcde";// f est un POINTEUR printf("%d", sizeof(e)); // => 6 printf("%d", sizeof("abcde"));// => 6 printf("%d", sizeof(d)); // => 9 printf("%d", sizeof(f)); // => 4 ou 8 (c'est un pointeur)