Contenu | Rechercher | Menus

Annonce

Si vous avez des soucis pour rester connecté, déconnectez-vous puis reconnectez-vous depuis ce lien en cochant la case
Me connecter automatiquement lors de mes prochaines visites.

À propos de l'équipe du forum.

#1 Le 30/04/2021, à 19:33

zephyre123

Taille d'un tableau deux manière de faire quelle différence ?

Bonjour;

Voici deux programmes :

- premier :

#include <stdio.h>
#include <stdlib.h>

#define TAILLE 5

int main(void)
{
	int premierTableau[TAILLE] = { 0 };

	for (int i = 0; i < TAILLE; i++)
		printf("PREMIER TABLEAU : premierTableau[i] = %d\n", premierTableau[i]);

	return 0;
}

- deuxième :

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
	int taille = 5;
	int deuxiemeTableau[taille];

	for (int i = 0; i < taille; i++)
	{
		deuxiemeTableau[i] = 0;
		printf("DEUXIEME TABLEAU : deuxiemeTableau[i] = %d\n", premierTableau[i]);
		
	}

	return 0;
}

Quelle différence y a t'il entre ces deux codes au niveau de la mémoire ?
Les deux font exactement la même chose pourtant.
Entre ces deux méthodes quelle est la meilleure ?

J'ai repéré au moins une différence c'est que je n'ai pas réussi à écrire cette ligne ci dessous :
int deuxiemeTableau[taille] = { 0 };

J'ai du passer par une boucle pour initialiser le tableau mais je ne comprends pas pourquoi je ne peux pas écrire cette ligne.

Pouvez vous m'aidez svp ?

Dernière modification par zephyre123 (Le 30/04/2021, à 19:36)

Hors ligne

#2 Le 30/04/2021, à 20:20

Zakhar

Re : Taille d'un tableau deux manière de faire quelle différence ?

Dans la toute première version de C tu n'aurais pas pu écrire la deuxième forme, c'est apparu bien plus tard.

La première forme est résolue à la compilation puisque les #define sont remplacés par leur valeur en dur par le préprocesseur, c'est comme si tu avais déclaré :

int main(void)
{
	int premierTableau[5] = { 0 };
}

Dans le deuxième cas, le tableau est alloué au "run" dans la pile.
En fait le code va être optimisé compte tenu de sa simplicité, mais c'est bien une allocation au moment du run, et donc pas possible d'initialiser puisque tu ne sais pas d'avance la taille.

Imagine par exemple que ce soit une fonction :

int foo(int size)
{
	int premierTableau[size];
}

Là tu comprends mieux, le tableau dans la fonction a une taille qui varie selon la variable passée en entrée.

Cela est très pratique, ça permet d'allouer dynamiquement des trucs dans la pile sans se préoccuper des concordances malloc/free !

Exemple qui sert à quelque chose en supposant un appel d'API avec un JSON:

static const char json_start[]="{\"file\":\"";
static const char json_end[]="\"}";

void concat_json(const char *filename)
{
    char str[sizeof(json_start) + sizeof(json_end) + strlen(filename) -1];

    memcpy(str, json_start, sizeof(json_start) - 1);
    memcpy(str + sizeof(json_start) - 1, filename, strlen(filename));
    strcpy(str + sizeof(json_start) - 1 + strlen(filename), json_end);

    /*** more code ***/

    api_call(str);

    /*** more code ***/
}

La fonction concat_json prend en entrée une chaîne et la concatène dans une structure JSON définie en variable externe.
Cette concaténation est faite dans la pile, sans avoir besoin de faire un "malloc/free", en calculant la longueur dont on a besoin.

Avant cette fonction magique, on était obligé d'utiliser alloca(), ce qui était assez moche, mais aboutissait au même résultat.

Note qu'ici j'utilise sizeof pour les variables statiques plutôt que strlen car sizeof est une constante qui est calculée à la compilation (il est cependant possible qu'un bon compilateur optimise de même un strlen dans ce cas !). Un -1 à la fin, car le sizeof va compter aussi le zéro binaire à la fin de chaine, est c'est donc 1 de plus que le strlen. On ne fait pas -2 (on a 2 sizeof) car on a quand même besoin d'un zéro binaire à la fin pour notre copie.

L'écriture non condensée serait donc :

sizeof(json_start) - 1 + sizeof(json_end) - 1 + strlen(filename) + 1

Il n'est pas grave non plus d'utiliser strlen(filename) plusieurs fois, le compilateur va s'apercevoir que ce n'est pas nécessaire puisque la chaîne ne change pas (de toute façon elle est définie en const pour la fonction) et ne va donc l'appeler qu'une fois  (sauf peut-être en mode "debug").
Sinon tu peux bien sûr commencer par mettre le résultat du strlen dans une variable et utiliser ensuite cette variable. Question de goût selon ce que trouves le plus lisible, le code généré devrait être identique (sauf en mode "debug").

L'intérêt de faire ainsi est si le code qui suit fait des tests et selon le cas va faire un return.
Si tu avais alloué par un malloc, il faudrait penser à faire un free avant chaque return au risque de générer une fuite mémoire...

Attention cependant en faisant ainsi, la zone allouée est dans la pile, laquelle n'est pas infinie. Par défaut sur Linux c'est 8Mo, donc si la chaîne dépassait la pile restante, le programme se planterait quelle que soit la mémoire disponible sur la machine, tandis qu'un malloc() fonctionnerait s'il reste de la mémoire.

P.S.: tu peux aussi faire le même principe avec un snprintf pour éviter d'avoir deux variables externes, et éviter plusieurs memcpy/strcpy.

Dernière modification par Zakhar (Le 30/04/2021, à 20:51)


"A computer is like air conditioning: it becomes useless when you open windows." (Linus Torvald)

Hors ligne