Bonjour,
je me pose actuellement une question : lorsque l'on programme en C l'affectation d'un tableau t1=T2 se fait par ou une recopie ?
merci
Version imprimable
Bonjour,
je me pose actuellement une question : lorsque l'on programme en C l'affectation d'un tableau t1=T2 se fait par ou une recopie ?
merci
La recopie case à case est impossible en C et sera rejetée par le compilateur. Il faut utiliser memcpy pour recopier case à case (ou une boucle pour parcourir le tableau).
Maintenant, si le compilateur autorise l'affectation, c'est que c'est une copie de pointeur qui a été faite.
La règle est très simple, un tableau est toujours passé par adresse à une fonction
Pour compléter la réponse de ram-0000 :
La question des tableaux est relativement simple bien qu'étant une exception dans le traitement des objets en C :
1- Dans tous les cas excepté lorsqu'il est associé aux opérateurs unaires & (adresse de) et sizeof, l'identificateur d'un tableau est une valeur représentant l'adresse du premier élément du tableau. Son type est donc "adresse d'un élément du tableau" et elle peut être affectée à un objet pointeur du type adéquat : "pointeur = tableau".
Comme l'adresse d'un objet n'est pas modifiable et a été fixée à sa création, cette valeur (représentée par l'identificateur du tableau) n'est pas modifiable et on n'a jamais le droit d'écrire "tableau = .......".
2- La question
ne se pose pas : il est interdit d'écrire ce code (si t1 est un tableau). Il faut que t1 soit un pointeur.Citation:
lorsque l'on programme en C l'affectation d'un tableau t1=T2 se fait par une recopie de pointeur ou une recopie case a case?
3-
En C, les paramètres des fonctions sont sans exception aucune passés par valeur.Citation:
De plus je voudrais savoir comment son réalisésles passages de paramètres par valeur et par adresse d'un tableau?
Si dans le cas de l'identificateur d'un tableau, c'est l'adresse de son premier élément qui est passé en argument, c'est en vertu de ce qui est exposé en (1)
Les "cases", c'est toi qui les a fixées.
Tu passe en paramètres un tableaux de type SHORT tu peut très bien faire le tour du tableau avec un pointeur de type CHAR, sauf que tu parcourra ton tableau deux fois moins vite.
Chaque "cases" équivaut à la taille du type du tableau (ou plutôt du TYPE qu'il est sensé contenir).
On connait l'adresse du premier élément : tab
On connait la taille en byte d'un élément du tableau puisque on connait le type T des éléments du tableau
On sait que les éléments d'un tableau sont consécutifs en mémoire
Il est donc facile de calculer l'adresse du nieme élément du tableau.
D'un point de vue du C, si T est le type des éléments du tableau tab, le type de tab (hors & et sizeof) est T*. C'est une adresse et l'arithmétique associée à tab est l'arithmétique des adresses. Par conséquent, l'adresse de l'élément d'indice n est tab+n. L'élément lui-même est donc *(tab+n). En fait, lorsque tu écris tab[n], le compilateur l'interprète comme *(tab+n) et la notation [] est là pour nous donner un simplification syntaxique (très avantageuse surtout dans les tableaux à plusieurs dimensions).
merci pour vos reponses. Une autre question me trotte dans la tête. D'un
Que veux-tu dire ici par "pile" ?
Ceci est plutôt le genre de chose qui se fait avec les registres. Ici, au minimum, deux registres sont nécessaires:
Code:
1
2
3
4
5
6
7 R1 := y; R2 := x; R1 := *R1; R1 *= R2; R2 := R1; R1 := y; *R1 := R2;
J'ai une derniere question a laquelle je souhaiterais bien pourvoir repondre...Code:
1
2
3
4 int y; void h(int d, int *r) { int y;
merci
La variable locale y dans g() masque la variable globale y dans la fonction, celle-ci n'y accède donc pas en tant que telle.
Ici, seules les fonctions f() et main() accèdent directement à y. Par contre, g() reçoit un pointeur vers y, et la modifie donc indirectement.
Voici un code avec les variables renommées:
Au moins, je peux te décrire le déroulement (= signifie ici équivalence, j'utilise := pour l'affectation):Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 int glob_y; void g(int const gd, int * const gr) { int gy; gy = *gr; *gr = gd+1; } void f(int const fd, int * const fr) { int fx; fx = fd+1; glob_y = 1; *fr = fx; g(*fr, fr); } int main() { int mx; mx = 3; f(mx, &glob_y); return 0; }
- appel de main() : glob_y non initialisé (ou initialisé à zéro, je ne sais plus)
- variable locale mx := 3
- appel de f(fd := mx = 3, fr := &glob_y)
- variable locale fx := fd + 1 = 4
- glob_y := 1
- *fr = glob_y := fx = 4
- appel de g(gd := *fr = glob_y = 4, gr := fr = &glob_y)
- variable locale gy := *gr = glob_y = 4
- *gr = glob_y := gd + 1 = 5
- retour de g à f
- retour de f à main
- Si je ne me suis pas trompé, glob_y vaut 5 ici.
- retour de 0
merci mais donc par rapport a un programme qui ne possede pas de variables globales ex :
quelle nouvelle information figure dans la pile quand les fonctions (du programme contenant la variable globale) f et g s'exécutent?Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14 void f(int x, int *y) { if (x == 0) (*y) = 1; else { f(x-1,y); (*y) = (*y) * x; } } int main(void) { int x; x = 4; f(x,&x); return 0; }
merci d'avance
Là, c'est plus difficile, car il y a récursivité (et non-terminale, en plus).
Ce qui est sûr, c'est que le pointeur ne change pas, donc y pointe toujours sur la même variable: mx.Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 void f(int const x, int * const y) { if (x == 0) { *y = 1; } else { f(x-1, y); (*y) *= x; } } int main(void) { int mx; mx = 4; f(mx, &mx); return 0; }
f() est rappelée avec à chaque fois une valeur inférieure (qui est une copie à chaque fois, donc ne modifie rien), jusqu'à ce qu'une fonction soit appelée avec x==0. Après, on initialise la variable pointée (ici, mx) à 1, puis au fur et à mesure qu'on remonte on multiplie par la valeur de x dans la fonction. Ceci est juste un exemple plus compliqué que la normale de calcul récursif d'une factorielle, un cas d'école sur la récursivité (mais inutile en soi, car un calcul itératif de la factorielle est plus simple et plus efficace).
En gros, sur la pile, si on mettait un breakpoint juste avant le *y = 1, on aurait ça:
- main()
- f(4, &mx)
- f(3, &mx)
- f(2, &mx)
- f(1, &mx)
- f(0, &mx)
Ce qui donne, avec convention d'appel C (en noir, les variables locales et registres sauvegardés; en rouge, les appels de fonction) :
- adresse de retour hors du programme
- registres sauvegardés par main()
- mx = 4
- adresse de mx
- 4
- adresse de retour dans main
- registres sauvegardés par f
- adresse de mx
- 3
- adresse de retour dans f
- registres sauvegardés par f
- adresse de mx
- 2
- adresse de retour dans f
- registres sauvegardés par f
- adresse de mx
- 1
- adresse de retour dans f
- registres sauvegardés par f
- adresse de mx
- 0
- adresse de retour dans f
- registres sauvegardés par f
donc en fait dans le programme contenant la variable globale lorsque f s'exécute il y a seulement l'adresse de la variable locale qui est stockée en plus que lorsque la fonction f s'execute dans l'autre programme?
C'est pareil pour g?
Redis-ça lentement, car je n'arrive pas à comprendre ce que tu penses avoir compris.
Un dessin peut aider.