OVH Cloud OVH Cloud

trouver la taille d'un tableau

12 réponses
Avatar
Driss HANIB
Bonjour ,
je suis à la recherche d'une méthode "robuste" pour déterminer la taille
d'un tableau (type Array) de données quelqu'en soit le format.
La méthode Ubound (je n'ai pas besoin de LBound, mes tableaux commençant
systématiquement à 0) se met en erreur si le tableau n'a jamais été
dimensionné !

j'ai construit une fonction qui gère alors l'erreur liée à Ubound sur un
tableau non dimensionné et renvoie alors -1
le problème que je suis oblgé dans cette focntion de définir le type du
tableau passé en paramètre.
ce qui implique une focntion pas type de données..
en effet déclarer un tableau de type variant ne passe pas.. surtout que le
type de données du tableau est en général un type défini par moi même..

Quelqu'un a-t-il donc une méthode, indépendante du type de donnée du tableau
étudié ?

merci

Driss

10 réponses

1 2
Avatar
Patrice Henrio
Je n'ai peut-être pas compris ta question, mais à priori la réponse est non.
En effet un tableau n'est qu'une partie de la mémoire (ou du disque dur) et
c'est la donnée type qui permet de l'utiliser. Pour un tableau de Ubound=N,
commençant à 0 de type de donnée type_donnée, la taille est de (N+1) élément
mais la taille en mémoire (en octets) est de (N+1)*len(type_donnée)
len(type_donnée) renvoie le nombre d'octets nécessaires pour stocker
type_données en mémoire.
La seule chose dont l'on soit sûre si l'on connaît la taille en octets du
tableau c'est que la taille des éléments est un diviseur de ce nombre.

La question se poserait différemment pour d'autres langages comme php
(faiblement typé) dans lequel un tableau peut contenir des éléments de
divers type, l'organisation du tableau est alors nécessairement différente.
Mais en VB les éléments sont contigus ce qui permet d'ailleurs de lire un
tableau dans un fichier en une seule lecture.
Exemple

Soit un fichier contenant les octets représentant un tableau T d'éléments de
type t_donnée, chaque élément de t_donnée occupant 45 octets, alors le
fichier (binaire) a une taille en octet multiple de 45

On peut lire le tableau déclaré en dynamique de la façon suivante
dim P as t_données
Open Fichier For Binary As #1
ReDim T(1 To LOF(1) / Len(P))
Get 1, , T
Close #1

Si maintenant on définit un type de 5 octets (type5)
dim P as type5
Open Fichier For Binary As #1
ReDim T(1 To LOF(1) / Len(P))
Get 1, , T
Close #1
Dans ce dernier cas le tableau comporte 9 fois plus d'éléments, par contre
le résultat de la lecture risque d'être aléatoire puisque les découpage de
t_données en 9 fois 5 octets n'a peut-être pas de sens.

Il serait d'ailleurs intéressant de savoir comment VB stocke les valeurs
d'un type donné
style FirstInFirstOut
Les données du début sont inscrites dans les octets du début de la zone
mémoire du type
ou FirstInLastOut
Les données sont écrites dans l'ordre inverse.

Désolé mais j'espère pour toi que je me trompe et que tu arriveras à
résoudre ton problème.


"Driss HANIB" a écrit dans le message de news:
%
Bonjour ,
je suis à la recherche d'une méthode "robuste" pour déterminer la taille
d'un tableau (type Array) de données quelqu'en soit le format.
La méthode Ubound (je n'ai pas besoin de LBound, mes tableaux commençant
systématiquement à 0) se met en erreur si le tableau n'a jamais été
dimensionné !

j'ai construit une fonction qui gère alors l'erreur liée à Ubound sur un
tableau non dimensionné et renvoie alors -1
le problème que je suis oblgé dans cette focntion de définir le type du
tableau passé en paramètre.
ce qui implique une focntion pas type de données..
en effet déclarer un tableau de type variant ne passe pas.. surtout que le
type de données du tableau est en général un type défini par moi même..

Quelqu'un a-t-il donc une méthode, indépendante du type de donnée du
tableau
étudié ?

merci

Driss




Avatar
Patrice Henrio
J'ai peut-être répondu un peu vite car en relisant mieux ton message je
m'aperçois qur ton problème consiste peut-être seulement à créer une
fonction prenant un tableau de n'importe quel type et renvoyant -1 si
celui-ci n'est pas initialisé (affecté)
En tout cas le problème m'intéresse je regarde ce que je peux faire (mes
premiers essais ne sont pas encourageant, j'arrive seulement à passer un
tableau en paramètre variant et à retrouver que ce variant est au départ un
array en utilisant varType)
Avatar
X
Bonjour,

Moi aussi, je n'ai peur être pas tout compris, voir rien, à tout hasard:

Dim t() As Integer
Dim n As Long
Dim maxi As Long
maxi = 14
ReDim t(1 To maxi)
On Error GoTo erreur
n = UBound(t)
MsgBox n
Exit Sub
erreur:
If Err = 9 Then MsgBox "0" ' indice en dehors de la plage
If Err <> 9 Then MsgBox Err
On Error Resume Next


--
Merci beaucoup, au revoir et à bientôt :o)
------
Site logiciels
http://irolog.free.fr
Mail
http://irolog.free.fr/ecrire/index.htm
Site perso
http://irolog.free.fr/joe/index.htm
Principe d'utilisation des news Groups
http://support.microsoft.com/directory/worldwide/fr/newsgroup/regles.htm
------------------------------------------------------------------------------------
"Driss HANIB" a écrit dans le message de news:
%
Bonjour ,
je suis à la recherche d'une méthode "robuste" pour déterminer la taille
d'un tableau (type Array) de données quelqu'en soit le format.
La méthode Ubound (je n'ai pas besoin de LBound, mes tableaux commençant
systématiquement à 0) se met en erreur si le tableau n'a jamais été
dimensionné !

j'ai construit une fonction qui gère alors l'erreur liée à Ubound sur un
tableau non dimensionné et renvoie alors -1
le problème que je suis oblgé dans cette focntion de définir le type du
tableau passé en paramètre.
ce qui implique une focntion pas type de données..
en effet déclarer un tableau de type variant ne passe pas.. surtout que le
type de données du tableau est en général un type défini par moi même..

Quelqu'un a-t-il donc une méthode, indépendante du type de donnée du
tableau
étudié ?

merci

Driss




Avatar
Driss HANIB
Merci Patrice
c'est effectivement cette version plus basique de ma recherche qui est la
bonne.
je ne suis malheureusement pas encore au point (de ma connaissance) pour
explorer directement la mémoire.
Pour revenir à mon problème je viens d'essayer également de mettre comme
paramètre une variable de type variant mais il y a un message d'erreur car
en fait mon ou mes tableaux sont de type 'personnalisé' et apparemment cela
ne fonctionne pas..

Driss

"Patrice Henrio" a écrit dans le message de
news:
J'ai peut-être répondu un peu vite car en relisant mieux ton message je
m'aperçois qur ton problème consiste peut-être seulement à créer une
fonction prenant un tableau de n'importe quel type et renvoyant -1 si
celui-ci n'est pas initialisé (affecté)
En tout cas le problème m'intéresse je regarde ce que je peux faire (mes
premiers essais ne sont pas encourageant, j'arrive seulement à passer un
tableau en paramètre variant et à retrouver que ce variant est au départ


un
array en utilisant varType)




Avatar
Driss HANIB
merci X
c'est effetcivement ce que je cherche, dans le principe car j'ai
effectivement déjà construit une fonction sur ce modèle, mais mon problème
est que j'utilise des tableaux de type 'personnalisés' et que je dois
apparemment faire une focntion pour chaque type de tableaux.
et je voulais savoir si il y avait moyen d'avoir une focntion globale .
peut être qu'avec les types classiques : string, long.. cela est possible..

a suivre..

Driss
"X" a écrit dans le message de
news:
Bonjour,

Moi aussi, je n'ai peur être pas tout compris, voir rien, à tout


hasard:

Dim t() As Integer
Dim n As Long
Dim maxi As Long
maxi = 14
ReDim t(1 To maxi)
On Error GoTo erreur
n = UBound(t)
MsgBox n
Exit Sub
erreur:
If Err = 9 Then MsgBox "0" ' indice en dehors de la plage
If Err <> 9 Then MsgBox Err
On Error Resume Next


--
Merci beaucoup, au revoir et à bientôt :o)
------
Site logiciels
http://irolog.free.fr
Mail
http://irolog.free.fr/ecrire/index.htm
Site perso
http://irolog.free.fr/joe/index.htm
Principe d'utilisation des news Groups
http://support.microsoft.com/directory/worldwide/fr/newsgroup/regles.htm
--------------------------------------------------------------------------


----------
"Driss HANIB" a écrit dans le message de news:
%
> Bonjour ,
> je suis à la recherche d'une méthode "robuste" pour déterminer la taille
> d'un tableau (type Array) de données quelqu'en soit le format.
> La méthode Ubound (je n'ai pas besoin de LBound, mes tableaux


commençant
> systématiquement à 0) se met en erreur si le tableau n'a jamais été
> dimensionné !
>
> j'ai construit une fonction qui gère alors l'erreur liée à Ubound sur un
> tableau non dimensionné et renvoie alors -1
> le problème que je suis oblgé dans cette focntion de définir le type du
> tableau passé en paramètre.
> ce qui implique une focntion pas type de données..
> en effet déclarer un tableau de type variant ne passe pas.. surtout que


le
> type de données du tableau est en général un type défini par moi même..
>
> Quelqu'un a-t-il donc une méthode, indépendante du type de donnée du
> tableau
> étudié ?
>
> merci
>
> Driss
>
>




Avatar
Picalausa François
"Driss HANIB" a écrit dans le message de news:
%
Bonjour ,
je suis à la recherche d'une méthode "robuste" pour déterminer la taille
d'un tableau (type Array) de données quelqu'en soit le format.
La méthode Ubound (je n'ai pas besoin de LBound, mes tableaux commençant
systématiquement à 0) se met en erreur si le tableau n'a jamais été
dimensionné !

j'ai construit une fonction qui gère alors l'erreur liée à Ubound sur un
tableau non dimensionné et renvoie alors -1
le problème que je suis oblgé dans cette focntion de définir le type du
tableau passé en paramètre.
ce qui implique une focntion pas type de données..
en effet déclarer un tableau de type variant ne passe pas.. surtout que le
type de données du tableau est en général un type défini par moi même..





Hello,

Si tu n'est pas contre une dll en C, il suffit d'avoir de son côté un void*
et du côt de VB un Tableau() As Any.
Il est aussi possible, sans ajouter de dll, d'imiter ce comportement à
l'aide de l'API VarPtr de vbvm60 appliquée à un () As Any:

Option Explicit

Private Type SAFEARRAYBOUND
cElements As Long
lLbound As Long
End Type

Private Type SAFEARRAY
cDims As Integer
fFeatures As Integer
cbElements As Long
cLocks As Long
pvData As Long
End Type

Private Declare Function VarPtrArray _
Lib "msvbvm60.dll" _
Alias "VarPtr" _
( _
ByRef ArrayVar() As Any _
) _
As Long
Private Declare Sub CopyMemory _
Lib "kernel32" _
Alias "RtlMoveMemory" _
( _
ByRef pDst As Any, _
ByRef pSrc As Any, _
ByVal ByteLen As Long _
)

Private Function GetSAUbound(ppsa As Long, Index As Long) As Long
Dim psa As Long 'SAFEARRAY*
Dim sa As SAFEARRAY
Dim sab As SAFEARRAYBOUND
' Déférence le SAFEARRAY**
CopyMemory psa, ByVal ppsa, LenB(psa)

'Vérifie que les deux paramètres passés sont valides
If (psa <> 0 And Index >= 0) Then
' Déférence le SAFEARRAY*
CopyMemory sa, ByVal psa, LenB(sa)

'Vérifie que le borne demandée est permise
If (Index < sa.cDims) Then
'Trouve la borne
CopyMemory sab, ByVal (psa + LenB(sa) + Index * LenB(sab)),
LenB(sab)
'Retourne la valeur maximale pour cette borne
GetSAUbound = sab.lLbound + sab.cElements - 1
End If
Else
GetSAUbound = -1
End If
End Function

Private Sub Form_Load()
Dim Array1(3) As Long
Dim Array2() As Byte
Dim Array3(5) As String

Debug.Print GetSAUbound(VarPtrArray(Array1), 0) 'Note le double appel.
' C'est le premier qui permet de ne faire aucune différence sur le type
de tableau
Debug.Print GetSAUbound(VarPtrArray(Array2), 0)
Debug.Print GetSAUbound(VarPtrArray(Array3), 0)
End Sub

--
Picalausa François
Avatar
Jean-marc
"Picalausa François" a écrit dans le message de news:
%
"Driss HANIB" a écrit dans le message de news:
%
Bonjour ,
je suis à la recherche d'une méthode "robuste" pour déterminer la taille
d'un tableau (type Array) de données quelqu'en soit le format.
La méthode Ubound (je n'ai pas besoin de LBound, mes tableaux commençant
systématiquement à 0) se met en erreur si le tableau n'a jamais été
dimensionné !

j'ai construit une fonction qui gère alors l'erreur liée à Ubound sur un
tableau non dimensionné et renvoie alors -1
le problème que je suis oblgé dans cette focntion de définir le type du
tableau passé en paramètre.
ce qui implique une focntion pas type de données..
en effet déclarer un tableau de type variant ne passe pas.. surtout que
le
type de données du tableau est en général un type défini par moi même..





Hello,

Si tu n'est pas contre une dll en C, il suffit d'avoir de son côté un
void* et du côt de VB un Tableau() As Any.



Hello François,

en dehors de la très jolie utilisation d'un SafeArray (héhé), j'ai une
question
Quelque chose doit m'échapper dans ce que tu veux faire en C.
Je vois bien comment faire une fonction qui attend un void* et comment la
déclarer en VB:

Coté C:
export long __stdcall sizeArray (void *t);
long __stdcall sizeArray (void *t)
{
long ret;
/* quid ?? */
return ret;
}

Coté VB
Private Declare Function sizeArray Lib "testvb.dll" Alias "" (t
As Any) As Long


Ce que je vois moins bien, c'est:
- l'appel de la fonction. Quelque chose comme cela:

Dim lRet As Long
Dim t(20) As Long

lret = sizeArray(VarPtr(t(0))) ' comme ça ??


- Ce qu'on peut en faire dans la dll, car tel que je le comprends,
la fonction en C reçoit un pointeur non typé, ok, mais ensuite?

Que faire avec mon void *t ? Ce n'est qu'un pointeur, non typé qui
plus est, donc quoi??

Merci par avance de tes éclaircissements, j'ai surement loupé un truc
simple... La fatigue sans doute!

--
Jean-marc
"There are only 10 kind of people
those who understand binary and those who don't."
mailto: remove '_no_spam_' ;
Avatar
Picalausa François
"Jean-marc" a écrit dans le message
de news: 44fad7bd$0$1203$
"Picalausa François" a écrit dans le message de
news: %
"Driss HANIB" a écrit dans le message de news:
%





Hello,

Hello,

Si tu n'est pas contre une dll en C, il suffit d'avoir de son côté un
void* et du côt de VB un Tableau() As Any.



Hello François,

en dehors de la très jolie utilisation d'un SafeArray (héhé), j'ai une
question
Quelque chose doit m'échapper dans ce que tu veux faire en C.



Simplement: la même chose qu'en VB, sauf qu'il ne faudra pas effectuer un
premier appel à VarPtr de vbvm, puisque la valeur du pointeur pourra être
retrouvée de suite.
Cela étant, mea culpa, je m'étais dit que je devrais expliciter ma réponse,
chose que j'ai oubliée... ce qui me fait d'ailleurs penser qu'il ne
s'agisait pas d'un void* mais d'un SAFEARRAY**. (pour l'excuse qui semble
justifiée, j'invoque moi aussi la fatigue!)

Je vois bien comment faire une fonction qui attend un void* et comment la
déclarer en VB:
<snip>
Coté VB
Private Declare Function sizeArray Lib "testvb.dll" Alias ""
(t As Any) As Long



t() As Any en fait

Ce que je vois moins bien, c'est:
- l'appel de la fonction. Quelque chose comme cela:
lret = sizeArray(VarPtr(t(0))) ' comme ça ??



Le but étant de se passer de VarPtr (qui a mon avis ne fait que retourner le
pointeur qui lui est passé), il s'agirait directement de
sizeArray(t)
Et c'est bien là l'astuce, qui est de passer non pas la donnée mais bien le
SAFEARRAY.

- Ce qu'on peut en faire dans la dll, car tel que je le comprends,
la fonction en C reçoit un pointeur non typé, ok, mais ensuite?



Comme dit plus haut, même cose qu'en VB soit:
déférencer une ou deux fois le pointeur (en fonction de la valeur du
premier)
trouver la borne en se basant sur le SAFEARRAY (et l'écriture de cette
partie là devrait être plus naturelle puisqu'il ne faudra pas jouer à
recalculer le pointeur, le compilateur C s'en chargeant tout seul)
De plus, cette technique évite(rais... si je ne me suis pas trompé sur
varptr) les (lents) copymemory...

Donc, en résumé, ça devrait donner (non testé):

long __stdcall sizeArray (SAFEARRAY **t)
{
SAFEARRAY* lpsa = *t;
SAFEARRAYBOUND* psab;

if ((lpsa) && (lpsa->cDims >= 1))
{
//Retrouve le premier élément
psab = lpsa->rgsabound;
return psab->lLbound + psab->cElements - 1;
}

return -1;
}

--
François Picalausa
Avatar
Jean-marc
"Picalausa François" a écrit dans le message de news:

"Jean-marc" a écrit dans le
message de news: 44fad7bd$0$1203$
"Picalausa François" a écrit dans le message de
news: %
"Driss HANIB" a écrit dans le message de news:
%





Hello,



Re!

Hello,

Si tu n'est pas contre une dll en C, il suffit d'avoir de son côté un
void* et du côt de VB un Tableau() As Any.



Hello François,

en dehors de la très jolie utilisation d'un SafeArray (héhé), j'ai une
question
Quelque chose doit m'échapper dans ce que tu veux faire en C.



Simplement: la même chose qu'en VB, sauf qu'il ne faudra pas effectuer un
premier appel à VarPtr de vbvm, puisque la valeur du pointeur pourra être
retrouvée de suite.
Cela étant, mea culpa, je m'étais dit que je devrais expliciter ma
réponse, chose que j'ai oubliée... ce qui me fait d'ailleurs penser qu'il
ne s'agisait pas d'un void* mais d'un SAFEARRAY**. (pour l'excuse qui
semble justifiée, j'invoque moi aussi la fatigue!)



Tu est tout excusé!


Je vois bien comment faire une fonction qui attend un void* et comment la
déclarer en VB:
<snip>
Coté VB
Private Declare Function sizeArray Lib "testvb.dll" Alias ""
(t As Any) As Long



t() As Any en fait



oui bien sur.


Ce que je vois moins bien, c'est:
- l'appel de la fonction. Quelque chose comme cela:
lret = sizeArray(VarPtr(t(0))) ' comme ça ??



Le but étant de se passer de VarPtr (qui a mon avis ne fait que retourner
le pointeur qui lui est passé), il s'agirait directement de
sizeArray(t)



Ok, je vois maintenant :-)


Et c'est bien là l'astuce, qui est de passer non pas la donnée mais bien
le SAFEARRAY.

- Ce qu'on peut en faire dans la dll, car tel que je le comprends,
la fonction en C reçoit un pointeur non typé, ok, mais ensuite?



Comme dit plus haut, même cose qu'en VB soit:
déférencer une ou deux fois le pointeur (en fonction de la valeur du
premier)
trouver la borne en se basant sur le SAFEARRAY (et l'écriture de cette
partie là devrait être plus naturelle puisqu'il ne faudra pas jouer à
recalculer le pointeur, le compilateur C s'en chargeant tout seul)
De plus, cette technique évite(rais... si je ne me suis pas trompé sur
varptr) les (lents) copymemory...

Donc, en résumé, ça devrait donner (non testé):

long __stdcall sizeArray (SAFEARRAY **t)
{
SAFEARRAY* lpsa = *t;
SAFEARRAYBOUND* psab;

if ((lpsa) && (lpsa->cDims >= 1))
{
//Retrouve le premier élément
psab = lpsa->rgsabound;
return psab->lLbound + psab->cElements - 1;
}

return -1;
}




Ok, tout s'éclaire :-)

Avec un SAFEARRAY **, ça le fait tout à fait bien effectivement,
de façon plus naturelle qu'en VB/VarPtr etc.
Le void * me laissait preplexe!

--
Jean-marc
"There are only 10 kind of people
those who understand binary and those who don't."
mailto: remove '_no_spam_' ;
Avatar
Jean-marc
"Jean-marc" a écrit dans le message
de news: 44faf502$0$10458$
"Picalausa François" a écrit dans le message de
news:
"Jean-marc" a écrit dans le
message de news: 44fad7bd$0$1203$
"Picalausa François" a écrit dans le message de
news: %
"Driss HANIB" a écrit dans le message de
news: %










Donc, en résumé, ça devrait donner (non testé):

long __stdcall sizeArray (SAFEARRAY **t)
{
SAFEARRAY* lpsa = *t;
SAFEARRAYBOUND* psab;

if ((lpsa) && (lpsa->cDims >= 1))
{
//Retrouve le premier élément
psab = lpsa->rgsabound;
return psab->lLbound + psab->cElements - 1;
}

return -1;
}





J'ai testé et c'est tout à fait correct (comme d'hab).
A noter: la fonction retourne la borne sup, donc penser
à ajouter 1 si Option Base 0.

j'ai testé avec dim t(20) as long,
sizeArray(t) retourne 20, que l'on ai déclaré Option Base 0 ou
Option Base 1.

--
Jean-marc
"There are only 10 kind of people
those who understand binary and those who don't."
mailto: remove '_no_spam_' ;
1 2