1
votes

Comment gérer différentes tailles de tableau comme arguments pour une fonction?

J'ai réussi à convertir manuellement plus de 5000 lignes de code du programme Fortran 77 en C ++, mais la conversion ne s'est pas déroulée comme prévu. J'essaye donc de déboguer le programme C ++ en utilisant mon programme Fortran 77. Dans fortran, j'ai développé un sous-programme qui prend un tableau et imprime l'index du tableau et sa valeur dans un fichier délimité par des virgules. J'essaye de faire la même chose en C ++. mais échapper à la déclaration "double temp1 [tempi]". Le tableau ne doit pas nécessairement être de la même taille dans tous les appels à la fonction. Donc, je ne peux pas le coder un dire "double temp1 [21]" parce que la prochaine fois il est 25. Fortran passe les tableaux par référence. Que proposez-vous que je fasse.

J'y suis parvenu pour le programme Fortran. L'idée est de prendre le vidage de la mémoire des variables du programme c ++ et de comparer les valeurs dans Excel en utilisant vba pour voir laquelle change le plus, puis de se concentrer sur cette variable dans le programme C ++ un point de débogage de départ.

Logique du code c ++:

 PROGRAM PRINT_MEMORY
 real*8 :: ASKIN(21)
 real*8 :: FM(25)
 CALL SINGLEARRAYD(ASKIN,21,"ASKIN")
 CALL SINGLEARRAYD(FM,25,"FM")
 END PRINT_MEMORY

 SUBROUTINE SINGLEARRAYD(TEMP1,TEMPI,STR1)
 IMPLICIT NONE
 CHARACTER(LEN=*) :: STR1
 INTEGER*4 MD_I,TEMPI
 REAL*8, DIMENSION(1:TEMPI) :: TEMP1
 DO MD_I = 1, TEMPI
 WRITE(51,'(ES25.16E3,A1,A25,A1,I5,A1)') TEMP1(MD_I),',',STR1,'(',
1 MD_I,')'
 ENDDO
 ENDSUBROUTINE SINGLEARRAYD

Logique du code Fortran 77:

 void singlearrayd(double temp1[tempi], int tempi, string str1){
   for (int md_i = 1; md_i <= tempi; md_i++){
   cout << temp1[md_i] << "," << str1 << "(" << md_i << ")";
   }
 }

 int main(){
   double askin[22];
   double fm[26];
   singlearrayd(askin,22,"askin");
   singlearrayd(fm,26,"fm");
   return 0;
 }


9 commentaires

Vous n'avez pas besoin de spécifier la taille du tableau. Les tableaux se désintègrent en pointeurs donc les informations de taille sont perdues de toute façon. double temp1 [] devrait vous aider.


son double * temp1 ou double temp1 []


Remarque: les tableaux en C et C ++ sont d'origine zéro. Cela signifie que for (int md_i = 1; md_i <= tempi; md_i ++) en démarre un trop tard et en exécute un au-delà de la fin du tableau. Utilisez pour (int md_i = 0; md_i


Légèrement sans rapport (programmation C ++): vous n'avez pas besoin (et ne devriez pas) déclarer md_i avant la boucle et envisagez d'utiliser un std :: vector au lieu d'un tableau - le vecteur a une méthode size ().


L'indexation des tableaux dans Fortran commence à 1. L'indexation des tableaux en C ++ commence à zéro. Vos boucles utilisent l'indexation Fortran en C ++ - une erreur classique "off by one" qui se traduira par l'accès à des éléments de tableau en dehors de la plage d'éléments valides. Vous devez corriger cela.


std :: array est également utile comme tableau intelligent si vous connaissez la taille du tableau au moment de la compilation. Vous devrez le passer par référence, car il ne se décomposera pas.


Si vous ne connaissez pas la taille au moment de la compilation, utilisez std :: vecteur .


De plus, votre tableau fm dans le programme C ++ est de la mauvaise taille.


d'accord si je devais essayer d'étendre ceci pour appeler doublearrayd (clomsk, 10,12, "clomsk") appeler doublearrayd (alpha, 12,21, "alpha") devrais-je définir deux nouvelles fonctions au lieu d'une parce que C ++ nécessite moi pour entrer le deuxième rang ie clomsk [] [12]; et alpha [] [21] seraient les arguments de deux nouvelles fonctions? Y a-t-il un moyen pour moi d'utiliser une seule fonction pour gérer les deux cas?


3 Réponses :


4
votes

Il y a plusieurs problèmes dans votre code.

En C ++, un tableau natif (comme votre askin dans main () ) est converti en pointeur lorsqu'il est passé à une fonction. Il n'est donc pas nécessaire de déclarer une dimension sur le tableau dans la liste d'arguments MAIS il est toujours nécessaire de passer un deuxième argument, car vous spécifiez la taille.

Cela signifie que la fonction C ++ doit avoir la forme

#include <vector>

void singlearrayd(const std::vector<double> &temp1, const std::string &str1)
{
    for (std::size_t md_i = 0; md_i < temp1.size(); ++md_i)
   {
        cout << temp1[md_i] << "," << str1 << "(" << md_i << ")";
   }
}

int main()
{
   std::vector<double> askin(21);   // askin has 21 elements, initialised to zero
   std::vector<double> fm(21);
   singlearrayd(askin, "askin");
   singlearrayd(fm, "fm");
}

ou (de manière équivalente)

int main()
{
   double askin[21] = {0.0};   // initialise the first element.  Other elements are initialised to zero
   double fm[21] = {0.0};
   singlearrayd(askin,21,"askin");
   singlearrayd(fm,25,"fm");
}

Notez dans ce qui précède que j'ai spécifié le type du troisième argument par son nom complet comme std :: string . Dans de nombreux cas, il vaut mieux éviter d'utiliser namespace std .

Le deuxième problème est que vous supposez que l'indexation des tableaux Fortran et l'indexation des tableaux C ++ sont identiques. En réalité, l'indexation des tableaux Fortran est basée sur 1 (le premier élément d'un tableau a l'index un, par défaut) et l'indexation des tableaux C ++ est basée sur 0 (le premier élément d'un tableau a un index zéro). L'utilisation de l'indexation de tableau Fortran en C ++ entraîne un comportement indéfini, car elle accédera aux éléments en dehors de la plage valide.

Le troisième problème (potentiel) est que votre fonction définit deux variables nommées md_i (une dans la fonction et une dans la boucle). Il vaut mieux éviter de faire cela.

Le fait de résoudre tout ce qui précède transformera votre fonction en (dans son intégralité)

void singlearrayd(double temp1[], int tempi, std::string str1)
{
    for (int md_i = 0; md_i < tempi; ++md_i)    // note the differences here carefully
    {
        cout << temp1[md_i] << "," << str1 << "(" << md_i << ")";
    }
 }

Le quatrième problème est que main () en C ++ renvoie int , pas void.

Le cinquième problème est que main () n'initialise pas les tableaux avant que singlearrayd () ne les imprime. Dans Fortran, les tableaux locaux à une fonction sont (souvent) initialisés à zéro. En C ++, ils ne sont pas initialisés par défaut, donc accéder à leurs valeurs (par exemple pour les imprimer) donne un comportement indéfini.

void singlearrayd(double *temp1, int tempi, std::string str1)

Cela fera fonctionner votre code. En pratique, cependant, des améliorations sont possibles. La première amélioration consiste à utiliser un conteneur standard plutôt qu'un tableau. Les conteneurs standard connaissent leur taille, ce qui permet de simplifier votre fonction. Deuxièmement, passez des arguments non triviaux (comme des conteneurs ou des chaînes) par référence - et de préférence une référence const si aucune modification n'est apportée à l'argument. Contrairement à Fortran, où les arguments de fonction sont souvent passés par référence PAR DEFAUT, il est nécessaire d'introduire DÉLIBÉRÉMENT des références en C ++.

void singlearrayd(double temp1[], int tempi, std::string str1)

Les conteneurs C ++ prennent également en charge les itérateurs - qui sont plus sûrs en pratique ET souvent plus efficace que l'utilisation de l'indexation de tableau. Je vais vous laisser un exercice pour apprendre à les utiliser.

Un message clé cependant: ne supposez pas qu'une simple traduction mécanique de Fortran vers C ++ fonctionnera. Vous avez déjà démontré les écueils d'une telle hypothèse. Prenez le temps d'apprendre le C ++ AVANT d'essayer de traduire trop de code de Fortran vers C ++. Cela est nécessaire à la fois pour que le code C ++ fonctionne correctement et pour le faire fonctionner efficacement.


1 commentaires

Peter juste un suivi, ma conversion de code a réussi à 100% après l'avoir déboguée un jour. J'ai fait quelques erreurs de conversion stupides lors de la traduction de Fortran vers C ++. trouvé un problème C ++ intéressant principalement pour les variables entières de boucle et en dehors de la boucle for utilisait le même nom de variable. Fortran autorise une déclaration qui peut être utilisée et réutilisée dans la boucle do ou en dehors de celles-ci. Mais le processus global était une promenade dans le parc. Merci pour vos conseils et suggestions. :)



2
votes

Une implémentation plus moderne serait

 #include <string>
 #include <array>
 #include <iostream>

 template <std::size_t size, class U>
 void singlearrayd(const std::array<U, size>& temp1, const std::string& str1){
   int i = 0;
   for (const auto& x : temp1)
      std::cout << x << "," << str1 << "(" << (i++) << ")";
   }

 int main(){
   std::array<double, 21> askin;
   std::array<double, 21> fm; 
   singlearrayd(askin, "askin");
   singlearrayd(fm, "fm");

   return 0;
 }

Veuillez noter que dans le code ci-dessus, les deux tableaux askin et fm ne sont pas initialisés . Vraisemblablement, dans le vrai code, vous les auriez déjà initialisés avant d'appeler singlarrayd . Souvenez-vous également que main doit renvoyer un int.


1 commentaires

francesco vous remercie pour votre aide. Le code ci-dessus est un module que je construis pour aider à déboguer le programme que j'ai converti à partir de Fortran. J'ai figuré le double temp1 [] par moi-même avant que les commentaires de tout le monde au-dessus de ma source soient stackoverflow.com/questions/45220976/... . L'exemple ci-dessus n'est pas le vrai code, juste quelque chose que j'étais en train de développer.



0
votes

Merci pour vos précieuses informations et commentaires. Je pense que la meilleure approche a été d'utiliser

    void doublearrayd(double *temp1, int tempi, int tempj, std::string str1){
        for (int md_j = 1; md_j<tempj; md_j++){
            for (int md_i = 1; md_i<tempi; md_i++){
                std::cout << *(temp1 + md_i*tempj + md_j) << "," << str1 << "(" << md_i << ";" << md_j << ")" << std::endl;
            }
        }
    }

    void triplearrayd(double *temp1, int tempi, int tempj, int tempk, std::string str1){
        for (int md_k = 1; md_k < tempk; md_k++){
            for (int md_j = 1; md_j<tempj; md_j++){
                for (int md_i = 1; md_i<tempi; md_i++){
                    std::cout << *(temp1 + md_i*tempj*tempk + md_j*tempk + md_k) << "," << str1 << "(" << md_i << ";" << md_j << ";" << md_k << ")" << std::endl;
                }
            }
        }
    }

En étendant cette idée et en faisant plus de recherches en utilisant Google, j'ai pu étendre cette idée pour gérer les tableaux 2D et 3D.

    void singlearrayd(double *temp1, int tempi, std::string str1)

https://en.wikipedia.org/wiki/Row -and_column-major_order

Comment puis-je transmettre un tableau multidimensionnel dynamique à une fonction?


0 commentaires