Voici une question que j'ai eue dans un examen aujourd'hui: P>
en C, supposons que les pointeurs soient strictement dactylographiés (c'est-à-dire un pointeur à un INT ne peut pas être utilisé pour pointer vers un caractère). Est-ce que cela réduit son pouvoir expressif? Si non, pourquoi et comment compenseriez-vous cette limitation? Si oui, comment? Et ce que de plus constructions devriez-vous ajouter à "égaliser" la perte de pouvoir expressif de c? P>
Quelques détails supplémentaires: P>
int x = 5; int * p = & x; Char * temp = (char *) p; code> li>
- Ceci inclut
(void *) code> conversions li>
ul>
J'ai également inclus ma réponse ci-dessous. P>
8 Réponses :
(someType *)((void *)somePtr) that contruction allow you to convert any pointer to (someType *).
Vraisemblablement, cela serait non autorisé, sinon la question est quelque peu triviale.
-1: Ce n'est pas la question de la question. L'OP demande ce qui se passerait si cela était interdit.
Convertissement non autorisé de (un autretype *) à (Sentatype *), mais la convertie (anytype *) à (vide *) et (annuler *) à (anytype *) est garantie par Standart
Vous avez raison. Mais l'OP demande ce qui se passerait si des cases n'étaient pas possibles dans C, quel que soit leur standard actuel.
Eh bien c'est une question très subjective. Couple que, avec le fait, je ne sais pas ce que "puissance expressive" est;) p>
Toujours pas être capable de lancer entre les types de pointeur est une grande limitation dans ma tête. Il semble que lorsque vous utilisez Java mappant un tableau de caractères (en provenance de quelque chose comme une prise réseau par exemple) à une classe est incroyablement gênant. La possibilité de simplement le jeter et de ré-interpréter la mémoire est incroyablement utile et permet d'optimiser de manière significative dans le traitement des blocs de mémoire aléatoires. P>
Comment obtiendriez-vous ces limitations? Peut-être implémenter une fonction "casting" ou peut-être une fonction memcpy modélique pouvant réinterviser la mémoire serait un énorme bonus d'optimisation et, pour les personnes comme moi, la productivité. Il pourrait même s'agir d'un plan d'autorisation d'inclure une sorte de classe "ID" d'être incluse dans le flux d'octets afin que vous sachiez qu'il peut être réinterrété comme une classe spécifique. P>
L'inconvénient d'avoir ce pouvoir est qu'il vous permet d'interpréter complètement les données dans le mauvais sens. Cela peut causer des bugs très méchants. P>
Cela signifie-t-il aussi plus En réalité, c N'est même pas tiré - complet; Voir les commentaires ci-dessous. P> vide * code>? Si oui, alors oui: l'expressivité de C serait limitée, car malloc code> serait impossible à mettre en œuvre. Vous auriez besoin d'ajouter un nouveau mécanisme d'allocation de magasin gratuit, typé et gratuit dans l'esprit de C ++ Nouveau code>. P>
(ou non: C serait toujours Turing-complet. Mais je ne pense pas que c'est ce que l'on entend ici.) s> p>
Une note sur le Turing-Complétude de C: Une machine Turing est un contrôleur d'état fini avec une bande infinie. Strictement, uniquement avec une telle ruban adhésif et une interface avec des instructions de lecture à droite et de pas, C est Turing-complète. MALLOC CODE> Ne fait pas la différence, car aucun nombre d'appels à celui-ci ne peut attribuer une quantité infinie de mémoire ( tailleof (void *) code> est une constante).
Une implémentation unique C n'est pas complète - complète, mais une famille de m de mise en œuvre dans laquelle vous recompilez le programme dans une nouvelle implémentation avec une tailles de type accrue et la répercute lorsque le programme est sorti de la mémoire, est terminé. Cela est dû à la représentation des types.
Notez que "la mémoire de mémoire" a une signification i> formelle i> dans la langue due au fait qu'un tableau de tailleof (vide *) code> non signé caractère code > S peut contenir au plus (uchar_max + 1) ^ taille de taille (vide *) code> valeurs de pointeur possibles.
@R ..: Oui, le langage C est donc dans un certain sens Turing-complet, mais chaque implémentation est finie-État, sauf si vous ajoutez une interface à la mémoire infinie.
La langue n'est même pas complète; Il spécifie une implémentation qui définit les limites qui l'empêchent d'être terminées. Vous avez besoin d'une famille infinie de C d'implémentations de C avec des types de pointeur progressivement plus importants pour être terminé.
@R ..: Oui, c'est ce que je voulais dire par dans un certain sens i>. À la deuxième pensée, cela ne tire en effet pas une exhaustivité, car elle nécessite un nombre infini d'implémentations.
@R ..: Si vous lisez la spécification ISO de fseek code> soigneusement, il s'avère que c peut utiliser une bande non liée représentée sous forme de flux binaire ( fichier * code>).
@larsmans: avec des recherches relatives répétées? Peut-être qu'une implémentation pourrait supporter cela, mais les implémentations confidentielles de POSIX ne peuvent pas (du moins pas pour les "fichiers réguliers") car ils sont tenus de donner code> eoverflow code> lorsque la position résultante ne correspondrait pas à off_T code>.
@R ..: Mais une bande sans bordure ne serait pas un fichier régulier, ce serait un périphérique de caractère :)
Cette question est similaire à demander quelles utilisations utiles / valides des moules de pointeur sont similaires. Voici quelques-uns: p>
Sans le déclenchement du pointeur, vous devez avoir plusieurs versions de Dans une catégorie légèrement différente, vous ne pouvez pas implémenter un sur quel type de fonctionnalité permettrait de reprendre une puissance expressive, un système de type qui reconnaît le polymorphisme de sorte que vous ne devez pas le coder avec des moulages à pointeur dangereux serait une excellente étape. langues de la famille ML a eu ce type de système de type pendant une longue période, Et si vous connaissez Generics de Java , ils suivent la même ligne de pensée. P> memcpy code>, memmove code>, malloc code> car ils nécessitent toutes les conversions de pointeur pour être implémentées et utilisées . Dans le cas de MALLOC code>, la mémoire d'allocation de la mémoire définie par l'utilisateur est définie par l'utilisateur code> S devient impossible. P>
qsort code> (celui fourni par la bibliothèque standard trie un tableau de VOID * code> et peut être utilisé efficacement pour trier tableaux de divers types de pointeurs). p>
Je ne pense pas que cela réduit son pouvoir expressif - vous êtes toujours capable d'écrire un interprète de la machine Turing, ce qui signifie qu'il est terminé. Voir par exemple ce fil de golf . P>
Si vous voulez dire une puissance expressive en termes de commodité de l'utilisateur, il limite certainement C beaucoup parce que le mécanisme d'allocation de la mémoire (Malloc & Co.) devrait être modifié. P>
Turning Complétude n'est pas seulement une mesure inutile pour la manière dont une langue est utile, op signifie de manière proprement quelque chose de différent par "expressivité".
@delnan: vrai, mais il est difficile de dire ce qu'il veut dire exactement. Et la totalité de l'exhaustivité est la plus proche de la signification commune du pouvoir expressif.
Edité la question, j'ai ajouté ce que j'ai interprété comme un pouvoir expressif.
en C, supposons que les pointeurs soient strictement tapé (c'est-à-dire un pointeur à un int ne peut pas être utilisé pour pointer vers un Char). P> blockQuote>
Prévoquer un pointeur strict typing dans C perd complètement l'intrigue, c'est-à-dire qu'il s'agit simplement d'un raccourci portable pour la langue d'assemblage. Vous êtes supposé em> pour pouvoir vous tirer dessus dans le pied et un bon développeur sera suffisamment intelligent pour ne pas. P>
Vrai, mais c'est ce que la question était.
Cela pourrait effectivement augmenter l'expressivité de C. C est l'une des rares langues pour lesquelles une implémentation donnée est spécifiée Si vous avez retiré l'exigence selon laquelle les pointeurs sont représentés sous le nom Char code>, ce qui signifie tous les types et les données totales disponibles sur le programme (l'espace de tous les pointeurs possibles, tous les noms de fichiers possibles, et tous les décalages de fichiers possibles, etc.) sont finalement liés et, par conséquent, le modèle de calcul de C est une machine à étatite Char [TailleOf (Type de pointeur)] CODE>, la langue spécifiée formellement pourrait en principe traiter avec une quantité infinie de données, et il serait en train de concevoir -complete. p>
C n'est pas Turing complet ?! Par votre argument, chaque langue dans le monde n'est pas! Pouvez-vous me signaler à un article / livre qui traite de cela?
@Utkarsh: Vous pouvez vérifier cela pour vous-même. void * code> a une taille constante dans n'importe quelle implémentation de C, il y a donc un maximum à la quantité de mémoire qu'un programme C peut adresser; Chaque objet de données et chaque fonction a une adresse pouvant être convertie en void * code>. (Cela correspond au fait que les ordinateurs pratiques sont des machines à états finis. Ce n'est pas nécessairement vrai pour d'autres langues.)
En effet. Vous pouvez Imagine i> une machine avec une "bande à deux dimensions" qui pourrait, à chaque "emplacement de la mémoire" dans une dimension, contiennent une valeur de pointeur / référence non liée. La plupart des langues pourraient être implémentées sur une telle machine pour être complète. Cependant, en raison de la spécification i> de la représentation des types de la norme C, cela n'est pas possible pour C. Le nombre de pointeurs possibles est lié par le fait qu'ils sont représentés comme une éventail d'octets ( char code>). Il en va de même pour les noms de fichiers et les compensations de fichiers, les seuls autres moyens de résoudre les données dans C.
J'ai pensé à un moyen de contourner la limitation de ne pas pouvoir être capable de taper entre divers types. Vous pouvez utiliser un syndicat:
int variable = 0x2345; alltypepointers p; p.i = &variable; char *temp = p.c; p.c++; int newint=*p.i;
C Ne spécifie pas que la représentation de différents types de pointeur est la même. Ce code a UB. Il ne précise pas non plus que vous pouvez accéder aux données via le type de pointeur incorrect, à l'exception des types Char code>.
@R. Ub? Jusqu'à présent, j'ai supposé qu'un pointeur était juste un numéro de 32/64 bits. Devrait être la même, quel que soit le type.
Par exemple, de nombreuses machines historiques ne pouvaient aborder que les mots, pas les octets. Par conséquent, Char code> et Void Les pointeurs code> étaient plus grands et inclus un décalage d'octet supplémentaire dans le mot que le code généré utiliserait pour extraire l'octet souhaité.
@R: Je n'ai pas répondu à votre réponse (en fait, son +1). Pouvez-vous me signaler à certains articles / livre / spécification / quelque chose où je peux lire à ce sujet?
Plus récemment, X86-16 pourrait avoir des pointeurs de taille différente pour des fonctions que pour les données.
@ DAN04: C désindique déjà complètement la conversion entre la fonction et les pointeurs de données, même void * code>. void (*) () code> est la chose la plus proche d'un pointeur de fonction générique. C nécessite des moulages entre tous les types de pointeur de fonction pour préserver la valeur du pointeur. D'autre part, POSIX les oblige à tous avoir la même représentation.
Même en dehors de l'objection de R. ', vous ne pouvez jamais énumérer tous les pointeurs code> struct code>. Je ne suis toujours pas sûr du genre d'expressivité de votre professeur après, mais C sans struct code> Les pointeurs seraient très maladroits.
@larsmans Vous pouvez ajouter toutes les autres structures que vous utilisez. Et je ne sais pas si je suis d'accord avec le commentaire de R.. En fin de compte, chaque pointeur serait la même taille. Ils indiquaient un mot et le compilateur / instruction extraire l'octet requis de ce mot (basé sur le décalage spécifié). Donc, votre pointeur est la chose sans la partie "offset".
@Utkarsh: Vous confondez le concept C d'un pointeur avec le concept sous-jacent d'une adresse mémoire. De plus, si vous devez redéfinir manuellement cette structure pour chaque programme pour adapter son struct code> S, qui est toujours gênant.
@Larsmans: Comment sont les deux différentes? S'agit-il de cela: une certaine implémentation de C pourrait utiliser l'adresse mémoire standard comme pointeur de caractère. Mais une autre mise en œuvre pourrait échanger quelques bits pour le même pointeur? D'accord, la chose struct code> est gênante, mais au moins, il y a un moyen de faire les choses.
@Utkarsh: C'est une possibilité, mais la différence entre les pointeurs et les adresses est également apparente sur des systèmes typiques où char * code> et int * code> ont la même représentation. Ajouter 1 à l'adresse de la mémoire d'un int code> donnerait l'octet suivant dans le int code>, mais ajoute 1 à un int * code> donne un pointeur à Le prochain int code> (par exemple dans un tableau), il ajoute donc vraiment Tailleof (int) code> à l'adresse de la mémoire stockée dans le pointeur.
Ah c'est déroutant! La notion de pointeurs que j'ai eu pour des années est fausse! Et je ne peux pas comprendre pourquoi>. <
La question n'est pas claire. Dans la langue C Tous les types de pointeur sont déjà i> Strictement, et a toujours été depuis le premier C89 / 90. Vous ne pouvez pas initialiser / attribuer un point de pointeur
int * code> sur un objetchar code> (sauf si vous forcez cette opération via un type explicite de type). Le seul type de pointeur en C qui peut être apporté pour pointer vers d'autres types de données sans une distribution explicite estVoid * code>. La seule raison pour laquelle vous pouvez parfois le faire sans créer de code pratique est que certains compilateurs C par défaut suivent des politiques d'application très lâchées.Alors, quelle est la question? Indépendamment des moulages explicites? La personne qui demande la question était tout simplement ignorée du fait que c est déjà strictement typé (au sens ci-dessus)? Ou autre chose? Qu'est-ce que c'est exactement entendu par "strictement typée"?
Corrigé la question. Strictement tapé dans le sens où un pointeur de caractère ne peut que pointer un personnage. Même les couts de type explicites ne peuvent pas le faire indiquer Int * ou Double *.
Idéal, mais (se référant à la question mise à jour) Notez que des choses comme
int * p = 5 code> sont déjà i> illégales dans C. Ils ont été illégaux depuis C89 / 90. Vous avez besoin d'une distribution (comme dansint * p = (int *) 5 code>) Pour que cela devienne légal C. Si votre compilateur C permetint * p = 5 code>, signifie que votre compilateur C a une vérification d'erreur trop détendue. Cela se produit assez souvent (pour la compatibilité avec le code de pré-standard Legacy), mais néanmoinsint * p = 5 code> n'est pas autorisé dans la norme C déjà.@Andrey hmm. Changé.