Écrire le prototype d'une fonction qui prend un tableau d'exactement 16 entiers
L'une des questions de l'entrevue m'a demandé "d'écrire le prototype d'une fonction C qui prend un tableau d'exactement 16 entiers" et je me demandais ce que cela pourrait être? Peut-être une déclaration de fonction comme ceci:
void foo(int a[], int len);
Ou autre chose?
Et si la langue était le C++ à la place?
Vous devez vous connecter pour publier un commentaire.
En C, cela nécessite un pointeur vers un tableau d'entiers 16:
Il serait appelé à:
En C++, vous pouvez utiliser une référence à un tableau, aussi, comme le montre Nawaz's réponse. (La question demande C dans le titre, et à l'origine uniquement mentionné en C++ dans les balises.)
Une version qui utilise une variante de:
finit par être équivalent à:
qui acceptera n'importe quelle taille de tableau, dans la pratique.
La question se pose - t
special_case()
vraiment empêcher une taille différente de la matrice d'être transmis. La réponse est "Oui".Le compilateur (GCC 4.5.2 sur mac os X 10.6.6, comme il arrive) se plaint (avertit):
Changement de GCC 4.2.1 - tel que fourni par Apple, et que l'avertissement est:
L'avertissement 4.5.2 est mieux, mais le fond est le même.
void foo(int a[static 16]);
mais je viens de réaliser que des garanties de au moins 16 membres qui ne sont pas exactement comme voulu par l'OP-Werror
.void f(int (*array)[16])
valide avec C90, ou s'il requiert C99 ou au-delà ?gcc -O3 -g -std=c90 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition -Werror -pedantic -c special.c
prouve quoi que ce soit, le code est OK sous C90 (oùspecial.c
contenait leanon()
fonction plus unextern void anon(void);
déclaration, et la compilation a échoué avec dépareillés types pour laarray18
définition/appel et a réussi quand ils ont été faits dans les commentaires).special_case(&array);
: est-ce à dire il y a un autre niveau d'indirection pour accéder à des données dansarray
?array
être un pointeur vers un tableau de contenu est, il semble que l'argument de fonction est un pointeur vers un pointeur.int[5]
est un peu brouillon. Dans le cas d'un argumentf(int[5])
, il est équivalent àf(int *)
et si vous avez un pointeur vers le premier int. Dans le cas d'un argumentf(int(*)[5])
, vous disposez d'un pointeur de 5 entiers consécutifs. Ces deux sont les mêmes (pour la mise en œuvre).array[0][i]
au lieu de l'habituellearray[i]
. Néanmoins, dans de tels cas, le pointeur de points au début, elle a donc la même valeur. Voir ideone.com/1qA7T9 .int read(const int (*array)[16])
de ne pas accepter un argument de typeint (*array)[16]
sans le déclenchement d'une alarme. À partir d'un compilateur point de vue, ils sont de types différents. Qui introduit une question : comment spécifierconst
opérations dans des conditions de compatibilité ?gcc -O2 -S input.c
). Ils sont exactement les mêmes.Il y a plusieurs façons de déclarer la matrice de paramètres de taille fixe:
accepte un pointeur-à-
int
, mais le tableau de taille sert de documentationaccepte un pointeur sur un tableau avec exactement 16 éléments
accepte un pointeur vers le premier élément d'un tableau avec au moins 16 éléments
accepte une structure de boxe un tableau avec exactement 16 éléments, le passage par valeur.
void foo(int values[static 16]);
cas, la norme n'exige pas que le compilateur de vérifier que l'argument donné est correct. Et, autant que je sache, aucun compilateur même essayer de le faire. Ainsi, alors qu'il est beau en théorie, à l'heure actuelle, il n'est pas réellement vous toute sécurité-il n'y aura aucun avertissement si vous l'appelez, avec un petit tableau. Espérons que les compilateurs va aller mieux.&local_int
, bien que, même si cela signifie que vous êtes en effet de donner un tableau de longueur 1.& qui est nécessaire en C++:
Remarque : & est nécessaire, sinon vous pouvez passer de tableau de n'importe quelle taille!
Pour C:
Démo : http://www.ideone.com/fWva6
a
de l'intérieurfoo()
oubar()
n'est pas aussi simple, comme si c'était un simple pointeur de table, en raison du niveau d'indirection. Il n'est pas possible de fairea[5]=1;
, mais le travail équivalent semble êtrea[0][5]=1;
. Pas une affaire énorme, mais encore un peu moins évident à lire. Plus important encore, je ne suis pas sûr de comprendre comment il est possible d'appliquerconst
de la propriété. Il semble que quelque chose comme cela ne fonctionne pas : ` void foo(const int (*un)[16]) { (...) } void bar (int (*a)[16]) { foo(a); }` Les pointeurs sont considérés comme de type différent.Je pense que le plus simple pour être typesafe serait de déclarer une structure qui contient le tableau, et le passer:
Vous déjà reçu quelques réponses pour C, et une réponse pour le C++, mais il y a une autre manière de le faire en C++.
Que Nawaz dit, pour passer un tableau de taille N, vous pouvez le faire en C++:
Toutefois, comme du C++11, vous pouvez également utiliser les std::array conteneur, qui peut être transmis avec la plus naturelle de la syntaxe (en supposant une certaine familiarité avec la syntaxe du modèle).
Comme un conteneur, std::array permet essentiellement la même fonctionnalité qu'un normal C-gamme de style, tout en ajoutant des fonctionnalités supplémentaires.
Mais, à ma connaissance (qui est certes limitée dans le temps), de l'arithmétique des pointeurs n'est pas en sécurité avec std::array, sauf si vous utilisez la fonction de membre de data() pour obtenir le tableau réel de l'adresse en premier. C'est à la fois de prévenir de futures modifications à la std::array classe à partir de la rupture de votre code, et parce que certains STL implémentations peuvent stocker des données supplémentaires en plus du tableau réel.
Noter que cette solution serait la plus utile pour le nouveau code, ou si vous convertissez votre pré-code existant pour utiliser std::tableaux au lieu de C-style de tableaux. Comme std::les tableaux sont les types d'agrégats, ils manquent personnalisée, les constructeurs, et donc vous ne pouvez pas passer directement à partir de C-gamme de style à std::array (court de l'aide d'un plâtre, mais c'est laid et peut potentiellement causer des problèmes dans l'avenir). Pour les convertir, vous devez utiliser quelque chose comme ceci:
Par conséquent, si votre code utilise de style C tableaux et il serait impossible de convertir de façon à utiliser std::tableaux au lieu de cela, vous feriez mieux de coller avec le style C tableaux.
(Note: j'ai spécifié les tailles de N de sorte que vous pouvez réutiliser plus facilement le code chaque fois que vous en avez besoin.)
Edit: Il y a quelques choses que j'ai oublié de mentionner:
1) La majorité de la norme C++ des fonctions de la bibliothèque conçue pour une utilisation sur les conteneurs sont de mise en œuvre-agnostique; au lieu d'être conçu pour des conteneurs spécifiques, ils fonctionnent sur des plages, en utilisant les itérateurs. (Cela signifie également qu'ils travaillent pour
std::basic_string
et les instanciations de celle-ci, commestd::string
.) Par exemple,std::copy
a le prototype suivant:Même si cela peut paraître imposant, en général, vous n'avez pas besoin de spécifier les paramètres du modèle, et peut tout simplement laisser le compilateur gérer pour vous.
En raison de compter sur les itérateurs, ces fonctions sont également compatibles avec le style C des tableaux (comme les itérateurs sont une généralisation des pointeurs, tous les pointeurs sont, par définition, les itérateurs (mais pas tous les itérateurs sont nécessairement des pointeurs)). Cela peut être utile lorsque vous travaillez avec le code existant, car il permet d'avoir le plein accès à la gamme des fonctions de la bibliothèque standard.
Vous l'avez remarqué dans cet exemple et la dernière que
std::array.begin()
etstd::begin()
peut être utilisé de façon interchangeable avecstd::array
. C'est parce questd::begin()
etstd::end()
sont mis en œuvre tels que, pour tout conteneur, ils ont le même type de retour et retourner la même valeur, que l'appel de labegin()
etend()
fonctions de membre d'une instance du conteneur.De style C les tableaux n'ont pas de membre de fonctions, ce qui nécessite l'usage de
std::begin()
etstd::end()
pour eux. Dans ce cas, les deux fonctions sont surchargés de fournir applicable pointeurs, selon le type de la matrice.Comme une règle générale, si vous n'êtes pas sûr de savoir si ou de ne pas tout particulier segment de code devrez utiliser le C-style des tableaux, c'est plus sûr d'utiliser
std::begin()
etstd::end()
.[Remarque que, bien que j'ai utilisé
std::copy()
comme un exemple, l'utilisation des plages et des itérateurs est très commun dans la bibliothèque standard. La plupart, si pas tous, fonctions conçu pour fonctionner sur les conteneurs (ou, plus précisément, toute la mise en œuvre de la Concept de conteneurs, commestd::array
,std::vector
, etstd::string
) utilisent des plages, de les rendre compatibles avec les actuels et futurs des conteneurs, ainsi qu'avec les C-style de tableaux. Il peut y avoir des exceptions à cette excellente compatibilité que je ne suis pas au courant, cependant.]2) Lors du passage d'un std::array, en valeur, il peut être fortement les frais généraux, en fonction de la taille de la matrice. En tant que tel, il est généralement préférable de le passer par référence, ou d'utiliser des itérateurs (comme la bibliothèque standard).
3) Tous ces exemples supposent que tous les tableaux dans votre code sera de la même taille, comme indiqué par la constante
N
. Pour faire plus de votre code plus de la mise en œuvre indépendant, vous pouvez soit utiliser les gammes & itérateurs vous-même, ou si vous voulez garder votre code axé sur les tableaux, l'utilisation basées sur des modèles de fonctions. [Bâtiment sur cette réponse à une autre question.]Si ce faisant, vous pouvez même aller aussi loin que la modèle de la matrice du type de membre, à condition que votre code peut fonctionner sur d'autres types de int.
Pour un exemple de cette action, voir ici.
Si quelqu'un voit toutes les erreurs que j'ai peut-être manqué, n'hésitez pas à les signaler pour moi de fixer, ou les corriger vous-même. Je pense que j'ai attrapé tous, mais je ne suis pas sûr à 100%.