2009-08-13 10 views
17

Si j'ai un prototype qui ressemble à ceci:Comment passer un littéral de tableau constant à une fonction qui prend un pointeur sans utiliser de variable C/C++?

function(float,float,float,float) 

je peux transmettre des valeurs comme ceci:

function(1,2,3,4); 

Donc, si mon prototype est le suivant:

function(float*); 

Y at-il chemin je peux réaliser quelque chose comme ça?

function({1,2,3,4}); 

Je cherche juste un moyen paresseux pour le faire sans créer une variable temporaire, mais je ne peux pas sembler clouer la syntaxe.

+0

Merci pour le conseil. Je suppose que je m'attendais à des réponses rapides et sales pour une question rapide et sale, mais votre réponse a exploré quelques styles alternatifs auxquels je n'avais pas pensé. Très appréciée! – sinoth

Répondre

27

Vous pouvez le faire en C99 (mais pas ANSI C (C90) ou tout curr variante de C++) avec littéraux composés. Voir la section 6.5.2.5 de la norme C99 pour les détails sanglants. Voici un exemple:

// f is a static array of at least 4 floats 
void foo(float f[static 4]) 
{ 
    ... 
} 

int main(void) 
{ 
    foo((float[4]){1.0f, 2.0f, 3.0f, 4.0f}); // OK 
    foo((float[5]){1.0f, 2.0f, 3.0f, 4.0f, 5.0f}); // also OK, fifth element is ignored 
    foo((float[3]){1.0f, 2.0f, 3.0f}); // error, although the GCC doesn't complain 
    return 0; 
} 

GCC fournit également ceci comme une extension de C90. Si vous compilez avec -std=gnu90 (la valeur par défaut), -std=c99, ou -std=gnu99, il compilera; Si vous compilez avec -std=c90, ce ne sera pas le cas.

+0

On pourrait évidemment aussi compiler avec '-std = gnu89' (mais pas' -std = c89'), car il est aussi '-std = gnu90' (et '-std = c90') (Je suis sûr que vous connaissez cet Adam, c'est plus pour les autres au cas où ils ne l'ont pas fait: P). – RastaJedi

2

La mauvaise nouvelle est qu'il n'y a pas de syntaxe pour cela. La bonne nouvelle est que cela va changer avec la prochaine version officielle de la norme C++ (prévue l'année prochaine ou deux). La nouvelle syntaxe ressemblera exactement à ce que vous décrivez.

+1

heh heh, l'année prochaine ou deux:] Bon à savoir ce sera en C++ 0x. – sinoth

+0

Je doute dans deux ans. La version bêta 10 de Visual Studio contient déjà des fonctionnalités C++ 0x et g ++ 4.4 possède déjà des listes d'initialisation (http://gcc.gnu.org/projects/cxx0x.html). Il a été repoussé à 2010, probablement au premier trimestre. – GManNickG

0

Non, vous ne pouvez pas faire cela. Je n'ai pas la norme ici, donc je ne peux pas donner une référence exacte, mais la chose la plus proche de ce que vous demander sont en constantes de chaîne, à savoir

function(char *); 
function("mystring"); 

est traité par le compilateur comme

char * some_pointer = "mystring"; 
function(char *); 
function(some_pointer); 

Il n'y a aucun moyen de traiter d'autres types de variables de cette manière.

4

Vous pouvez créer un composé littéral:

function ((float[2]){2.0, 4.0}); 

Bien que, je ne sais pas pourquoi vous voulez passer par la peine. Ceci n'est pas autorisé par l'ISO.

En règle générale, les raccourcis comme celui-ci doivent être évités en faveur de la lisibilité dans tous les cas; la paresse n'est pas une bonne habitude d'explorer (opinion personnelle, bien sûr)

+0

Je suis d'accord sur la lisibilité, et dans la pratique cette fonction particulière sera passé une adresse mémoire et être heureux. Cependant, pendant les tests, je veux souvent simplement ajouter des valeurs aléatoires. Votre extrait fait l'affaire, pour le meilleur ou pour le pire :) Merci! – sinoth

+2

cela est valide en c99, mais rien d'autre selon les normes ... –

0

Malheureusement, cela fonctionne uniquement avec des tableaux de caractères:

void func2(char arg[]) { 
} 

int main() 
{ 
    func2("hello"); 
    return 0; 
} 
17

Ceci est marqué à la fois C et C++, de sorte que vous allez obtenir radicalement différentes réponses.

Si vous attendez quatre paramètres, vous pouvez le faire:

void foo(float f[]) 
{ 
    float f0 = f[0]; 
    float f1 = f[1]; 
    float f2 = f[2]; 
    float f3 = f[3]; 
} 

int main(void) 
{ 
    float f[] = {1, 2, 3, 4}; 
    foo(f); 
} 

Mais qui est assez dangereux, comme vous pouvez le faire par accident:

void foo(float f[]) 
{ 
    float f0 = f[0]; 
    float f1 = f[1]; 
    float f2 = f[2]; 
    float f3 = f[3]; 
} 

int main(void) 
{ 
    float f[] = {1, 2}; // uh-oh 
    foo(f); 
} 

Il est généralement préférable de laisser les comme des paramètres individuels. Puisque vous ne devriez pas utiliser les tableaux bruts de toute façon, vous pouvez le faire:

#include <cassert> 
#include <vector> 

void foo(std::vector<float> f) 
{ 
    assert(f.size() == 4); 

    float f0 = f[0]; 
    float f1 = f[1]; 
    float f2 = f[2]; 
    float f3 = f[3]; 
} 

int main(void) 
{ 
    float f[] = {1, 2, 3, 4}; 
    foo(std::vector<float>(f, f + 4)); // be explicit about size 

    // assert says you cannot do this: 
    foo(std::vector<float>(f, f + 2)); 
} 

Une amélioration, mais pas beaucoup d'un.Vous pouvez utiliser boost::array, mais plutôt que d'une erreur de taille ne correspondent pas, ils sont initialisés à 0:

#include <boost/array.hpp> 

void foo(boost::array<float, 4> f) 
{ 
    float f0 = f[0]; 
    float f1 = f[1]; 
    float f2 = f[2]; 
    float f3 = f[3]; 
} 

int main(void) 
{ 
    boost::array<float, 4> f = {1, 2, 3, 4}; 
    foo(f); 

    boost::array<float, 4> f2 = {1, 2}; // same as = {1, 2, 0, 0} 
    foo(f2); 
} 

Tout cela sera fixé en C++ 0x, quand les constructeurs de liste initialiseur sont ajoutés:

#include <cassert> 
#include <vector> 

void foo(std::vector<float> f) 
{ 
    assert(f.size() == 4); 

    float f0 = f[0]; 
    float f1 = f[1]; 
    float f2 = f[2]; 
    float f3 = f[3]; 
} 

int main(void) 
{ 
    foo({1, 2, 3, 4}); // yay, construct vector from this 

    // assert says you cannot do this: 
    foo({1, 2}); 
} 

et probablement boost::array ainsi:

#include <boost/array.hpp> 

void foo(boost::array<float, 4> f) 
{ 
    float f0 = f[0]; 
    float f1 = f[1]; 
    float f2 = f[2]; 
    float f3 = f[3]; 
} 

int main(void) 
{ 
    foo({1, 2, 3, 4}); 

    foo({1, 2}); // same as = {1, 2, 0, 0} ..? I'm not sure, 
       // I don't know if they will do the check, if possible. 
} 
0

vous pouvez écrire une classe de constructeur qui permettrait de la même syntaxe

// roughly 
template <typename C> 
class Builder { 
public: 
    template <typename T> 
    Builder(const T & _data) { C.push_back(_data); } 
    template <typename T> 
    Builder& operator()(const T & _data) { 
     C.push_back(_data); 
     return *this; 
    } 
    operator const C &() const { return data; } 
private: 
    C data; 

}; 

cette façon, vous pouvez utiliser la classe comme

foo (const std :: vecteur & v);

foo (Builderstd :: vecteur> (1) (2) (3) (4));

2

Vous pouvez techniquement faire référence à la matrice, mais vous ne pouvez toujours pas créer une liste d'initialisation anonyme, je pense.

void func(int (&bla)[4]) 
{ 
    int count = sizeof(bla)/sizeof(bla[0]); 
    // count == 4 
} 

int bla[] = {1, 2, 3, 4}; 
func(bla); 

int bla1[] = {1, 2}; 
func(bla1); // <-- fails 

Pour C++ façon, regardez boost::assign. Façon intéressante de remplir les conteneurs STL.

0

Pour ajouter au plaisir, vous pouvez utiliser des modèles pour le rendre variable en longueur.

template<std::size_t N> 
int chars(const char(&r)[N]){ 
    std::cout << N << ": " << r << std::endl; 
    return 0; 
} 

template<std::size_t N> 
int floats(const float(&r)[N]){ 
    std::cout << N << ":"; 
    for(size_t i = 0; i < N; i++) 
     std::cout << " " << r[i]; 
    std::cout << std::endl; 
    return 0; 
} 

int main(int argc, char ** argv) { 
    chars("test"); 
    floats({1.0f, 2.0f, 3.0f, 4.0f}); 
    return 0; 
} 
Questions connexes