Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

Tableau de chaînes à triple entrées

4 réponses
Avatar
Mirandole
Salut. Gros problème en C avec les tableaux à plusieurs entrée :

#include <math.h>
#include <stdio.h>
#define maxt 12
#define maxh 50

Void main(int narg, char ** argv)
{
char tabl[maxt][maxh][1][1024]; // tableaux de chaînes
1024 à triple entrée
unsigned int i, k, j;

for (i=0;i<9;i++){
k=i/3;
j=fmod(i,3);
sprintf(tabl[k][j][0],"bla%d",i);
sprintf(tabl[k][j][1],"bla%d",i); // la même chose
}

for (i=0 ; i<9 ; i++ ) {
k=i/3;
j=fmod(i,3);
fprintf(stderr,"%s\n%s\n\n" , tabl[k][j][0] , tabl[k][j][1] )
}
}

Bon, hé bien au lieu d'écrire comme espéré :
bla0
bla0

bla1
bla1

bla2
bla2
etc...


j'ai :
bla0
bla1

bla1
bla2

bla2
bla2

bla3
bla4




bref, j'y comprend rien...

4 réponses

Avatar
kanze
Mirandole wrote:
Salut. Gros problème en C


Alors pourquoi poster dans fr.comp.lang.c++, où il est question
plutôt de C++, et non de C ? fr.comp.lang.c me semblerait plus
indiqué, si c'est vraiment du C (et il en a l'air).

avec les tableaux à plusieurs entrée :


Juste un détail, mais est-ce que tu ne veux pas dire plutôt « à
plusieurs dimensions » ? Un tableau qui n'a qu'une seule entrée
n'est pas très intéressant.

#include <math.h>
#include <stdio.h>
#define maxt 12
#define maxh 50

Void main(int narg, char ** argv)
{
char tabl[maxt][maxh][1][1024]; // tableaux de chaînes
1024 à triple entrée


Alors, je comprends de moins en moins ton vocabulaire. Le
tableau que tu viens de déclarer a 614400 entrées. Et en
supposant que tu veux dire dimensions, il en a quatre, non
trois.

Mais surtout, je ne comprends pas l'intérêt de la troisième
dimension. Une dimension de 1, c'est un scalaire. On ne peut se
servir que de l'indice 0. Alors, autant que faire, on la laisse
tomber. (Des dimensions de 1, voire même de 0, peuvent
apparaître, et avoir un sens, quand la dimension est calculée
dynamiquement. Mais ce n'est pas le cas ici.)

Évidemment, en C++, le tout serait beaucoup plus simple avec
std::vector et std::string. Aussi, ça marchera à peu près
partout -- il y a des systèmes où une declaration comme la
tienne provoquera un débordement de la pile.

unsigned int i, k, j;

for (i=0;i<9;i++){
k=i/3;
j=fmod(i,3);


Pourquoi fmod ? Le résultat ici doit être le même qu'avec i % 3.
Sauf que je ne suis pas sûr qu'avec toutes les conversions, et
la division flottante, tu n'y introduis pas une risque d'erreur.
(Je ne le crois pas dans ce cas-ci, mais en général, il vaut
mieux éviter le flottant quand on peut.)

sprintf(tabl[k][j][0],"bla%d",i);
sprintf(tabl[k][j][1],"bla%d",i); // la même chose


Pas vraiment. Que ce soit du C ou du C++, cette ligne donne un
comportement indéfini. Il n'y a pas de tabl[k][j][1].

Je ne sais pas exactement ce que tu essaies de faire. (Je
suppose que le code posté est une simplification du problème
réel.) Mais a priori, en C++, je verrais quelque chose du genre :

typedef std::pair< std::string, std::string >
Pair ;
std::vector< std::vector< Pair > >
tabl ;
for ( int j = 0 ; j < 3 ; ++ i ) {
tabl.push_back( std::vector< Pair >() ) ;
std::vector< Pair >&col = tabl.back() ;
for ( int k = 0 ; k < 3 ; ++ k ) {
std::ostringstream tmp ;
tmp << "bla" << 3 * j + k ;
col.push_back( Pair( tmp.str(), tmp.str() ) ) ;
}
}

Ça a l'avantage qu'il utilise beaucoup moins de mémoire (parce
que vector et string gèrent la mémoire dynamique, et qu'il n'y a
donc pas besoin de prévoir les cas extrèmes).

Si on tient à l'utilisation des indices, on peut aussi
prédimensionner tabl :

std::vector< std::vector< Pair > >
tabl( maxt, maxh ) ;
for ( int i = 0 ; i < 9 ; ++ i ) {
std::ostringstream tmp ;
tmp << "bla" << i ;
tabl[ i / 3 ][ i % 3 ] = Pair( tmp.str(), tmp.str() ) ;
}

La différence ici, c'est que le tableau a tout de suite 600
éléments, tandis qu'on ne se sert que de 9.

Pour les sorties, évidemment, dans les deux cas, c'est :

for ( int j = 0 ; j < 3 ; ++ j ) {
for ( int k = 0 ; k < 3 ; ++ k ) {
std::cout << tabl[ j ][ k ].first << 'n'
<< tabl[ j ][ k ].second << 'n' ;
}
std::cout << 'n' ;
}

Dans le cas avec les push_back, du fait que tous les entrées
soient valid, on pourrait même utiliser les itérateurs, ou
paramètrer les boucles des indices avec tabl.size() et
tabl[j].size().

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

Avatar
Sylvain
kanze wrote on 20/03/2006 11:10:
Mirandole wrote:

Mais surtout, je ne comprends pas l'intérêt de la troisième
dimension. Une dimension de 1, c'est un scalaire. [...]


on peut comprendre que c'était simplement là son bug (confusion entre la
taille et l'indice max.; erreur courante par exemple pour quelqu'un
qui viendra du VB où les array sont 1 based).

Évidemment, en C++, le tout serait beaucoup plus simple avec
std::vector et std::string. Aussi, ça marchera à peu près
partout -- il y a des systèmes où une declaration comme la
tienne provoquera un débordement de la pile.


comme tu dirais, cela dépend du point de vue et de l'application, si
l'appli traite de données brutes (taille non variante, lue en bloc, etc)
un multi-array de dimension fixe est plus simple; bon maintenant un
tableau de chaines (recettes de cuisine, chansons, ?...) de 1024 carac.
croisés en 50 sous-dialectes de 12 langues !?! ça doit être étonnement
spécifique.


unsigned int i, k, j;

for (i=0;i<9;i++){
k=i/3;
j=fmod(i,3);


Pourquoi fmod ? Le résultat ici doit être le même qu'avec i % 3.
Sauf que je ne suis pas sûr qu'avec toutes les conversions, et
la division flottante, tu n'y introduis pas une risque d'erreur.


les 2 arguments sont entiers, s'il a bcp de chance peut être que la
librairie bypass la conversion en flottant, ici c'est de toute façon
nuisible et inutile -- surement une autre méconnaissance des opérateurs
C/C++ (dont %).

(Je ne le crois pas dans ce cas-ci, mais en général, il vaut
mieux éviter le flottant quand on peut.)

sprintf(tabl[k][j][0],"bla%d",i);
sprintf(tabl[k][j][1],"bla%d",i); // la même chose


Pas vraiment. Que ce soit du C ou du C++, cette ligne donne un
comportement indéfini. Il n'y a pas de tabl[k][j][1].


en C, comme en C++, tabl[k][j][1] est résolu en:

&((char*) tabl)[1 * 1024 + j * 1024 * 1 + k * 1024 * 12 * 1]

si "indéfini" veut dire "non prévisible, non reproductif, non
déterministe", alors non, c'est défini! ça ne donne pas le bon élément
et ça provoque une exception quand on déborde du bloc total.

Sylvain.


Avatar
Serge Paccalin
Le lundi 20 mars 2006 à 22:25:39, Sylvain a écrit dans
fr.comp.lang.c++ :

Mais surtout, je ne comprends pas l'intérêt de la troisième
dimension. Une dimension de 1, c'est un scalaire. [...]


on peut comprendre que c'était simplement là son bug (confusion entre la
taille et l'indice max.; erreur courante par exemple pour quelqu'un
qui viendra du VB où les array sont 1 based).


En VB, les tableaux sont 0-based.

Dim A(5)

déclare un tableau à 6 éléments, de A(0) à A(5).

[coupe]

sprintf(tabl[k][j][1],"bla%d",i); // la même chose


Pas vraiment. Que ce soit du C ou du C++, cette ligne donne un
comportement indéfini. Il n'y a pas de tabl[k][j][1].


en C, comme en C++, tabl[k][j][1] est résolu en:

&((char*) tabl)[1 * 1024 + j * 1024 * 1 + k * 1024 * 12 * 1]

si "indéfini" veut dire "non prévisible, non reproductif, non
déterministe", alors non, c'est défini! ça ne donne pas le bon élément
et ça provoque une exception quand on déborde du bloc total.


Ça ne provoque pas forcément une exception, sinon les attaques par
débordement de tampon n'existeraient pas. Le comportement est...
indéfini.

--
___________ 21/03/2006 09:56:45
_/ _ _`_`_`_) Serge PACCALIN -- sp ad mailclub.net
_L_) Il faut donc que les hommes commencent
-'(__) par n'être pas fanatiques pour mériter
_/___(_) la tolérance. -- Voltaire, 1763



Avatar
kanze
Sylvain wrote:
kanze wrote on 20/03/2006 11:10:
Mirandole wrote:

Mais surtout, je ne comprends pas l'intérêt de la troisième
dimension. Une dimension de 1, c'est un scalaire. [...]


on peut comprendre que c'était simplement là son bug
(confusion entre la taille et l'indice max.; erreur courante
par exemple pour quelqu'un qui viendra du VB où les array sont
1 based).


Je m'en doute un peu aussi que c'est la cause de son erreur.

Évidemment, en C++, le tout serait beaucoup plus simple avec
std::vector et std::string. Aussi, ça marchera à peu près
partout -- il y a des systèmes où une declaration comme la
tienne provoquera un débordement de la pile.


comme tu dirais, cela dépend du point de vue et de
l'application,


Oui et non. Ça serait plus simple avec std::vector et
std::string. Ce qui ne veut pas dire qu'il peut y avoir des
raisons valable pour adopter une solution plus complexe dans
certains cas.

Dans le code posté, il avait des dimensions de 12, 50, 1, 1024.
Dont il utilisait 3, 3, 2, 5. Si l'application a besoin des
tailles fixes une fois pour tout, sa déclaration serait :
char tabl[3][3][2][5] ;
En fait, déjà pour la dernière, une taille fixe pour une chaîne
de caractères me semble douteuses au maximum. Pour
l'avant-dernière, en revanche, d'après la façon qu'il avait
écrit le code, j'ai supposé qu'il s'agissait bien de deux
quantités, toujours, et je me suis servi de std::pair. (C'est
peut-être les formes singulière et plurielle ?) Pour les deux
premières, j'ai supposé en revanche que de la variation était
possible, mais ce n'est qu'une supposition. Il n'y a rien de
concret pour indiquer d'une façon ou de l'autre.

si l'appli traite de données brutes (taille non variante, lue
en bloc, etc) un multi-array de dimension fixe est plus
simple;


Ce n'est pas évident en général. Dans le cas des caractères,
comme ici, en revanche, ça peut être effectivement le plus
simple. Mais à quel prix : réserver systèmatiquement 1024
caractères pour chaque élément risque de gaspiller beaucoup de
mémoire (assez pour que le tableau ne tient pas sur la pile sur
certains systèmes), sans toute fois suffir pour certains cas
exceptionnels.

bon maintenant un tableau de chaines (recettes de
cuisine, chansons, ?...) de 1024 carac. croisés en 50
sous-dialectes de 12 langues !?! ça doit être étonnement
spécifique.


C'est 50 recettes dans 12 langues, peut-être. Mais pourquoi les
deux variants (un normal, un pour diabètes ?) ?

Et que chaque recettes, dans toutes ces traductions fassent
exactement 1023 caractères... Comme tu dis, c'est étonnement
spécifique.

unsigned int i, k, j;

for (i=0;i<9;i++){
k=i/3;
j=fmod(i,3);


Pourquoi fmod ? Le résultat ici doit être le même qu'avec i % 3.
Sauf que je ne suis pas sûr qu'avec toutes les conversions, et
la division flottante, tu n'y introduis pas une risque d'erreur.


les 2 arguments sont entiers, s'il a bcp de chance peut être
que la librairie bypass la conversion en flottant,


Il faudrait un sacré compilateur pour y arriver. C'est une
fonction dont on passe la paramètres par valeur.

ici c'est de toute façon nuisible et inutile -- surement une
autre méconnaissance des opérateurs C/C++ (dont %).

(Je ne le crois pas dans ce cas-ci, mais en général, il vaut
mieux éviter le flottant quand on peut.)

sprintf(tabl[k][j][0],"bla%d",i);
sprintf(tabl[k][j][1],"bla%d",i); // la même chose


Pas vraiment. Que ce soit du C ou du C++, cette ligne donne un
comportement indéfini. Il n'y a pas de tabl[k][j][1].


en C, comme en C++, tabl[k][j][1] est résolu en:

&((char*) tabl)[1 * 1024 + j * 1024 * 1 + k * 1024 * 12 * 1]


Ça serait le cas si le code avait un comportement défini. En C,
comme en C++, l'expression tabl[k][j][1] contient un débordement
d'indice, ce qui est un comportement indéfini. Le langage de la
norme a été formulé de façon exprès à permettre une
implémentation d'effectuer la vérification des bornes. Il y a
même eu une implémentation dans le temps qui le faisait.

si "indéfini" veut dire "non prévisible, non reproductif, non
déterministe", alors non, c'est défini!


« Indéfini », dans le C et le C++, a une signification bien
précise -- il veut dire que l'implémentation n'est soumise à
aucun contraint. Dans la pratique, c'est peu probable que cette
expression réformatte le disque dur, mais s'il le fait, ce n'est
pas une erreur dans le compilateur.

ça ne donne pas le bon élément et ça provoque une exception
quand on déborde du bloc total.


Pas du tout. Avec l'implémentation que j'ai actuellement, ça ne
provoque jamais d'exception. Avec l'implémentation CenterLine,
il y a quelques années, ça provoquait systèmatiquement une
exception, qu'on déborde le bloc total ou non. Le langage ne dit
pas ce que ça doit faire -- c'est un comportement indéfini.

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34