[Powered by Google Translate] [Section 4 - Plus confortable] [Rob Bowden - Université de Harvard] [C'est CS50. - CS50.TV] Nous avons un quizz demain, au cas où vous les gars ne savaient pas que. Il s'agit essentiellement de tout ce que vous avez vu en classe ou aurait dû voir en classe. Qui inclut des pointeurs, même si elles sont un sujet très récent. Vous devriez au moins comprendre les niveaux élevés d'entre eux. Tout ce qui a été dépassé dans la classe que vous devez comprendre pour le quiz. Donc, si vous avez des questions à leur sujet, vous pouvez les poser maintenant. Mais cela va être une séance très dirigée par des étudiants où vous les gars poser des questions, alors j'espère que les gens ont des questions. Quelqu'un at-il des questions? Oui. >> [L'élève] Pourriez-vous nous pointeurs à nouveau? Je vais aller sur les pointeurs. Toutes les variables nécessairement vivre dans la mémoire, mais en général, vous ne vous inquiétez pas à ce sujet et que vous venez de dire x + 2 et + 3 y et le compilateur de savoir où les choses vivent pour vous. Une fois que vous avez affaire avec des pointeurs, vous êtes maintenant explicitement l'utilisation de ces adresses mémoire. Donc, une seule variable ne jamais vivre à une adresse unique à un moment donné. Si nous voulons déclarer un pointeur, ce qui est le type va ressembler? Je tiens à déclarer un pointeur p. Qu'est-ce que le type de ressembler? [L'élève] int * p. Ouais >>. Donc, int * p. Et comment puis-je le faire pointer vers x? >> [L'élève] Ampersand. [Bowden] Donc esperluette est littéralement appelé l'adresse de l'opérateur. Donc, quand je dis et ça devient de x l'adresse mémoire de la variable x. Alors maintenant, j'ai le pointeur p, et partout dans mon code que je peux utiliser * p ou je pourrais utiliser x et ce sera exactement la même chose. (* P). Quel est ce fait? Qu'est-ce que cette étoile signifie? [L'élève] Cela signifie une valeur à ce point. Ouais >>. Donc, si on y regarde, il peut être très utile pour faire ressortir les diagrammes lorsqu'il s'agit d'une petite boîte de la mémoire pour x, qui arrive à avoir la valeur 4, alors nous avons une petite boîte de la mémoire pour p, et ainsi de p points de X, donc nous dessiner une flèche de p à x. Ainsi quand nous disons p * nous disons allez à la boîte qui est p. Star est de suivre la flèche, puis faites ce que vous voulez avec cette boîte là. Donc je peux dire * p = 7, et qui ira à la boîte qui est x et que le changement à 7. Ou je pourrais dire int z = * p * 2; C'est déroutant parce que son étoile, étoile. L'étoile est le déréférencement p, l'autre star est multiplié par 2. Remarquez que j'ai pu tout aussi bien remplacer le p * avec x. Vous pouvez les utiliser de la même manière. Et puis, plus tard, je peux avoir un point p à quelque chose de complètement nouveau. Je peux juste dire p = &z; Alors maintenant, P Pas de points de plus à x, il pointe vers z. Et chaque fois que je fais p * c'est la même chose que faire z. Donc la seule chose utile à ce sujet est une fois que nous commencer à entrer dans les fonctions. C'est un peu inutile de déclarer un pointeur qui pointe vers quelque chose de et puis vous êtes juste à l'déréférencement quand vous auriez pu utiliser la variable d'origine pour commencer. Mais quand vous entrez dans les fonctions - alors disons que nous avons une certaine fonction, int foo, qui prend un pointeur et fait exactement * p = 6; Comme nous l'avons vu avec swap, vous ne pouvez pas faire un échange efficace et une fonction distincte simplement en passant parce que tout entiers dans C est toujours passage par valeur. Même si vous êtes de passage pointeurs vous êtes de passage par valeur. Il se trouve que ces valeurs sont des adresses mémoire. Donc, quand je dis foo (p), je suis de passage du pointeur dans la fonction foo puis foo fait * p = 6; Donc à l'intérieur de cette fonction, * p est toujours équivalent à x, mais je ne peux pas utiliser x à l'intérieur de cette fonction, car elle n'est pas portée au sein de cette fonction. Donc, * p = 6 est le seul moyen que je peux accéder à une variable locale d'une autre fonction. Ou bien, les pointeurs sont la seule façon que je peux accéder à une variable locale d'une autre fonction. [L'élève] Disons que vous vouliez retourner un pointeur. Comment voulez-vous faire? [Bowden] Renvoie un pointeur comme dans quelque chose comme int y = 3; retour et y? >> [L'élève] Ouais. [Bowden] D'accord. Vous ne devriez jamais faire cela. Cela est mauvais. Je crois que j'ai vu dans ces diapositives de cours vous commencé à voir ce schéma l'ensemble de la mémoire où ici vous avez l'adresse mémoire 0 et ici vous avez d'adresse mémoire 4 Go ou 2 à la 32. Alors vous avez des trucs et des trucs et puis vous avez votre pile et vous avez votre tas, que vous juste commencé à apprendre à propos, en grandissant. [L'élève] N'est-ce pas le tas au-dessus de la pile? Ouais. Le tas est sur le dessus, n'est-ce pas? >> [L'élève] Eh bien, il a mis 0 sur le dessus. [L'élève] Oh, il a mis 0 sur le dessus. >> [L'élève] Oh, d'accord. Avertissement: N'importe où avec CS50 vous allez voir de cette façon. >> [L'élève] D'accord. C'est juste que quand vous êtes le premier voyant des piles, comme quand vous pensez d'une pile vous pensez de l'empilement des choses l'un sur l'autre. Nous avons donc tendance à retourner cette sorte autour de la pile est de plus en plus comme une pile normalement au lieu de la pile qui pend. >> [L'élève] Ne pas techniquement tas grandir trop, si? Cela dépend de ce que vous entendez par grandir. La pile et le tas se développent toujours dans des directions opposées. Une pile est toujours grandir dans le sens où il grandit vers des adresses de mémoire élevées, et le tas est de plus en plus bas en ce qu'elle a de plus en plus vers des adresses de mémoire inférieures. Ainsi, la partie supérieure est 0 et le fond est élevé adresses mémoire. Ils sont de plus en plus à la fois, juste dans des directions opposées. [L'élève] Je voulais juste dire que, parce que vous avez dit que vous mettez la pile sur le fond car il semble plus intuitif, car pour la pile à commencer par le haut d'un tas, tas est au-dessus de lui-même aussi, donc C'est - >> Oui. Vous pouvez aussi penser à le tas comme grandissent et plus grande, mais la pile plus. Ainsi, la pile est celle qui nous sorte de vouloir montrer grandir. Mais partout où vous regardez ailleurs va montrer l'adresse 0 en haut et l'adresse mémoire la plus élevée au fond, si ceci est votre point de vue habituel de la mémoire. Avez-vous une question? [L'élève] Pouvez-vous nous en dire plus sur le tas? Ouais. Je vais y revenir dans un instant. Tout d'abord, revenir aux raisons pour lesquelles le retour et y est une mauvaise chose, sur la pile, vous avez un groupe de cadres de pile qui représentent toutes les fonctions qui ont été appelés. Donc ignorer les choses précédentes, le haut de votre pile va toujours être la principale fonction puisque c'est la première fonction qui est appelée. Et puis, lorsque vous appelez une autre fonction, la pile va se développer vers le bas. Donc, si je l'appelle une fonction, foo, et il obtient son cadre propre pile, il peut appeler une fonction, d'un bar, il obtient son cadre propre pile. Et le bar pourrait être récursive et il pourrait se faire appeler, et que second appel à la barre va obtenir son cadre propre pile. Et donc ce qui se passe dans ces cadres de pile sont tous des variables locales et tous les arguments de la fonction que - Toutes les choses qui sont localement portée à cette fonction, allez dans ces cadres de pile. Cela signifie donc que quand j'ai dit quelque chose comme bar est une fonction, Je vais juste de déclarer un entier, puis renvoyer un pointeur vers cet entier. Alors d'où vient y vivre? [L'élève] y vit dans un bar. >> [Bowden] Ouais. Quelque part dans ce petit carré de mémoire est un carré qui a Littler y en elle. Quand je reviens et y, je retourne un pointeur vers ce bloc peu de mémoire. Mais alors, quand un retour de la fonction, de son cadre de pile se extraites de la pile. Et c'est pourquoi il est appelé pile. C'est comme si la structure de données pile, si vous savez ce que c'est. Ou même comme une pile de plateaux est toujours l'exemple, principal va aller sur le fond, puis la première fonction que vous appelez va aller au-dessus de cela, et vous ne pouvez pas revenir au menu principal jusqu'à ce que vous revenez de toutes les fonctions qui ont été appelées qui ont été placés sur le dessus de celui-ci. [L'élève] Donc, si vous avez fait faire revenir l'y &, cette valeur est sujette à changement sans préavis. Oui, C'est - >> [l'élève] Il pourrait être écrasé. Ouais >>. C'est tout à fait - Si vous essayez de - Ce serait également un bar int * car il est retourne un pointeur, de sorte que son retour est de type int *. Si vous essayez d'utiliser la valeur de retour de cette fonction, c'est un comportement indéfini parce que ce pointeur pointe vers une mauvaise mémoire. >> [L'élève] D'accord. Alors que faire si, par exemple, vous avez déclaré int * y = malloc (sizeof (int))? Voilà qui est mieux. Oui. [L'élève] Nous avons parlé de la façon quand nous traîner les choses à notre corbeille ils ne sont pas réellement effacée; nous venons de perdre leurs aiguilles. Donc dans ce cas avons-nous réellement effacer la valeur ou est-il toujours là dans la mémoire? Pour la plupart, ça va être encore là. Mais disons que nous arrive d'appeler une autre fonction, baz. Baz va obtenir son cadre propre pile ici. Ça va être écrasant toutes ces choses, et puis si vous essayez plus tard et utilisez le pointeur que vous avez obtenu précédemment, ça ne va pas être la même valeur. Il va avoir changé juste parce que vous avez appelé la fonction baz. [L'élève] Mais si nous n'avions pas, aurions-nous encore obtenir 3? [Bowden] Selon toute vraisemblance, vous le feriez. Mais vous ne pouvez pas compter sur cela. C dit que le comportement est indéterminé. [L'élève] Oh, c'est vrai. D'accord. Alors, quand vous voulez retourner un pointeur, c'est là que malloc est en cours d'utilisation. Je vous écris en fait juste retour de malloc (3 * sizeof (int)). Nous allons passer en revue malloc plus en une seconde, mais l'idée est de malloc toutes vos variables locales toujours sur la pile. Tout ce qui est malloced va sur le tas, et il sera pour toujours et toujours sur le tas jusqu'à ce que vous explicitement le libérer. Donc, cela signifie que lorsque malloc vous quelque chose, il va survivre après le retour de la fonction. [L'élève]-t-il survivre après l'arrêt du programme en cours d'exécution? Non >> Ok, donc ça va être là jusqu'à ce que le programme est tout le chemin accompli en marche. Oui >>. On peut aller sur des détails de ce qui se passe quand le programme s'arrête. Vous pourriez avoir besoin de me le rappeler, mais c'est une chose séparée entièrement. [L'élève] Donc malloc crée un pointeur? Ouais >>. Malloc - >> [l'élève] Je pense que malloc désigne un bloc de mémoire qu'un pointeur peut utiliser. [Bowden] Je veux que le diagramme à nouveau. >> [L'élève] Donc, cette fonction fonctionne, cependant? [L'élève] Ouais, malloc désigne un bloc de mémoire que vous pouvez utiliser, puis il retourne l'adresse du premier bloc de cette mémoire. [Bowden] Ouais. Ainsi, lorsque vous malloc, vous attrapant un certain bloc de mémoire qui est actuellement dans le tas. Si le tas est trop petit, alors le tas va tout simplement de se développer, et il pousse dans cette direction. Donc, disons que le tas est trop petite. Ensuite, il est sur le point de grandir un peu et retourner un pointeur vers ce bloc qui a juste grandi. Lorsque vous des trucs gratuits, vous faites plus de place dans le tas, oui, alors un appel ultérieur à malloc pouvez réutiliser cette mémoire que vous aviez libéré. La chose importante à propos de malloc et free, c'est qu'il vous donne un contrôle complet au cours de la durée de vie de ces blocs de mémoire. Les variables globales sont toujours en vie. Les variables locales sont vivants dans leur champ. Dès que vous passez une accolade, les variables locales sont morts. Malloced mémoire est vivante lorsque vous voulez qu'elle soit vivante et est ensuite libérée lorsque vous lui demandez d'être libéré. Ce sont en fait les seuls 3 types de mémoire, vraiment. Il s'agit de gestion automatique de la mémoire, qui est la pile. Les choses se passent automatiquement pour vous. Quand vous dites int x, la mémoire est allouée pour x int. Lorsque x est hors de portée, la mémoire est récupérée pour x. Ensuite, il ya la gestion dynamique de la mémoire, qui est ce que malloc, ce qui est quand vous avez le contrôle. Vous dynamiquement décider quand la mémoire doit et ne doit pas être affecté. Et puis il ya statique, ce qui signifie simplement qu'il vit pour toujours, qui est ce que les variables globales sont. Ils sont tout simplement toujours en mémoire. Des questions? [L'élève] Pouvez-vous définir un bloc simplement en utilisant des accolades mais n'ayant pas d'avoir une? instruction if ou d'une instruction while ou quelque chose comme ça Vous pouvez définir un bloc comme dans une fonction, mais qui a aussi des accolades. [L'élève] Donc, vous ne pouvez pas avoir comme une paire d'accolades aléatoire dans votre code qui ont des variables locales? >> Oui, vous le pouvez. A l'intérieur de la barre int nous pourrions avoir {int y = 3;}. C'est censé être ici. Mais qui définit complètement la portée de int y. Après cette deuxième entretoise bouclés, y ne peut plus être utilisée. Vous avez failli ne jamais le faire, cependant. Pour en revenir à ce qui se passe quand un programme se termine, il ya une sorte de mensonge méprise / demi que nous donnons dans le but de faire juste les choses plus faciles. Nous vous disons que lorsque vous allouer de la mémoire vous allouons une bonne partie de la RAM pour cette variable. Mais vous n'êtes pas vraiment en contact direct avec RAM jamais dans vos programmes. Si vous y pensez, comment j'ai dessiné - Et en fait, si vous passez par chez GDB, vous verrez la même chose. Peu importe combien de fois vous exécutez votre programme ou le programme que vous utilisez, la pile va toujours commencer - vous allez toujours à voir les variables autour de quelque chose oxbffff adresse. C'est généralement quelque part dans cette région. Mais comment pouvons-2 programmes peut avoir des pointeurs vers la même mémoire? [L'élève] Il ya une désignation arbitraire de oxbfff où est censé être sur la RAM qui peut réellement être dans des endroits différents selon le moment où la fonction a été appelée. Ouais. Le terme est la mémoire virtuelle. L'idée est que chaque processus, chaque programme qui s'exécute sur votre ordinateur dispose de son propre - supposons 32 bits - espace d'adressage complètement indépendant. C'est l'espace d'adressage. Elle a ses propres complètement indépendants de 4 giga-octets à utiliser. Donc, si vous exécutez 2 programmes simultanément, ce programme voit 4 gigaoctets à lui-même, ce programme voit 4 gigaoctets à lui-même, et il est impossible pour ce programme de déréférencer un pointeur et se retrouver avec la mémoire de ce programme. Et que la mémoire virtuelle est une application d'un espace d'adresses des processus à des choses réelles sur la RAM. Donc, c'est à votre système d'exploitation pour savoir que, hey, quand ce gars oxbfff pointeur déréférence, que cela signifie vraiment qu'il veut octets de RAM 1000, tandis que si le programme de déréférence ce oxbfff, il veut vraiment octets de RAM 10000. Ils peuvent être arbitrairement éloignés. Cela est vrai même des choses dans un espace d'adressage des processus unique. Donc, comme il voit les 4 giga-octets de lui-même, mais disons - [L'élève] Est-ce que tous les processus - Disons que vous avez un ordinateur avec seulement 4 Go de RAM. Est-ce que tous les processus unique voir les 4 giga-octets entiers? Oui >>. Mais les 4 gigaoctets qu'il voit est un mensonge. C'est juste qu'il pense qu'il a tout ce mémoire, car il ne sait pas tout autre processus existe. Il utilisera uniquement la mémoire autant qu'il a réellement besoin. Le système d'exploitation ne va pas donner de RAM pour ce processus si ce n'est pas en utilisant toute la mémoire dans toute cette région. Il ne va pas lui donner la mémoire pour cette région. Mais l'idée est que - je suis en train de penser - je ne peux pas penser à une analogie. Les analogies sont difficiles. L'un des problèmes de mémoire virtuelle ou l'une des choses c'est de résolution est que les processus doivent être complètement inconscient de l'autre. Et si vous pouvez écrire un programme qui vient tout déréférence de pointeur, comme il suffit d'écrire un programme qui dit * (ox1234), et que l'adresse mémoire déréférencement 1234. Mais il appartient au système d'exploitation pour ensuite traduire ce moyen 1234. Donc, si 1234 se trouve être une adresse mémoire valide pour ce processus, comme il est sur la pile ou quelque chose, alors ce sera de retour la valeur de cette adresse mémoire dans la mesure où le processus courant. Mais si 1234 est pas une adresse valide, comme il arrive à la terre dans certains petit morceau de la mémoire qui est ici au-delà de la pile et le tas au-delà et vous n'avez pas vraiment utilisé que, alors que c'est quand vous obtenez des choses comme des erreurs de segmentation parce que vous touchez mémoire que vous ne doit pas toucher. Cela est également vrai - Un système 32 bits, 32 bits signifie que vous avez 32 bits pour définir une adresse mémoire. C'est pourquoi les pointeurs sont de 8 octets, car 32 bits ya 8 octets - ou 4 octets. Les pointeurs sont 4 octets. Alors, quand vous voyez un pointeur comme oxbfffff, c'est - Dans un programme donné, vous pouvez simplement construire un pointeur arbitraire, n'importe où à partir ox0 de bœuf 8 f's - ffffffff. [L'élève] N'avez-vous pas dire qu'ils sont 4 octets? Ouais >>. [L'élève] Ensuite, chaque octet aura - >> [Bowden] Hexadécimal. Hexadécimal - 5, 6, 7, 8. Donc pointeurs vous allez toujours voir en hexadécimal. C'est juste la façon dont nous classons les pointeurs. Tous les 2 chiffres du hexadécimale est de 1 octet. Donc, il va y avoir 8 chiffres hexadécimaux de 4 octets. Ainsi, chaque pointeur unique sur un système 32-bit va être 4 octets, ce qui signifie que dans votre processus, vous pouvez construire tout 4 octets arbitraires et faire un pointeur sur elle, ce qui signifie que dans la mesure où il est courant, il peut traiter un ensemble de 2 à 32 octets de la mémoire. Même si elle n'a pas vraiment d'avoir accès à cela, même si votre ordinateur ne dispose que de 512 mégaoctets, il pense qu'il a autant de mémoire. Et le système d'exploitation est assez intelligent pour qu'il n'attribuera ce que vous avez réellement besoin. Il ne se contente pas y aller, oh, un nouveau procédé: 4 concerts. Ouais. >> [L'élève] Qu'est-ce que le bœuf veut dire? Pourquoi pensez-vous écrire? C'est juste le symbole hexadécimal. Quand vous voyez un numéro de départ avec le bœuf, les choses successives sont en hexadécimal. [L'élève] Vous avez expliqué ce qui se passe quand un programme se termine. Oui >>. Qu'est-ce qui se passe quand un programme se termine est le système d'exploitation seulement efface les mappages qu'il a pour ces adresses, et c'est tout. Le système d'exploitation peut maintenant vous donner que la mémoire à un autre programme à utiliser. [L'élève] D'accord. Donc, lorsque vous affectez quelque chose sur le tas ou les variables de la pile ou mondial ou quelque chose, ils ont tous simplement disparaître dès la fin du programme parce que le système d'exploitation est maintenant libre de donner ce mémoire à un autre procédé. [L'élève] Même s'il ya probablement encore des valeurs écrites en? Ouais >>. Les valeurs sont probablement encore là. C'est juste que ça va être difficile de les atteindre. Il est beaucoup plus difficile de les atteindre que de l'obtenir à un fichier supprimé parce que le type de fichier supprimé est assis là pendant une longue période et le disque dur est beaucoup plus grand. Donc, il va écraser des parties différentes de la mémoire avant qu'il arrive à écraser le morceau de mémoire que ce fichier sert à être à. Mais la mémoire principale, la mémoire vive, vous faites défiler beaucoup plus vite, il va très vite être remplacé. Questions sur ceci ou autre chose? [L'élève] J'ai des questions sur un sujet différent. Ok >>. Quelqu'un at-il des questions à ce sujet? D'accord. Sujet différent. >> [L'élève] D'accord. Je passais par quelques-uns des tests pratiques, et dans l'un d'eux parlait du sizeof et la valeur qu'il renvoie ou différents types de variables. Oui >>. Et il a dit que les deux int et long à la fois le retour 4, de sorte qu'ils sont tous les deux de 4 octets. Y at-il une différence entre un int et un long, ou est-ce la même chose? Oui, il ya une différence. Le standard C - Je vais probablement mettre en désordre. Le standard C est juste comme ce que C est à la documentation officielle de C. C'est ce qu'il dit. Ainsi, le standard C indique juste que un char sera pour toujours et toujours être à 1 octet. Tout ce qui suit - une courte est toujours défini comme étant juste supérieure ou égale à un produit de carbonisation. Cela pourrait être strictement supérieur, mais pas positive. Un int est simplement définie comme étant supérieure ou égale à une courte. Et une longue est simplement définie comme étant supérieure ou égale à un entier. Et un long long est supérieure ou égale à une longueur. Donc, la seule chose que la norme C définit l'ordre relatif est de tout. La quantité réelle de mémoire que les choses prennent place est généralement à la mise en œuvre, mais il est assez bien définie à ce stade. >> [L'élève] D'accord. Ainsi, les shorts sont presque toujours va être de 2 octets. Ints sont presque toujours va être de 4 octets. Longs longs sont presque toujours va être de 8 octets. Et aspire, cela dépend si vous utilisez une version 32 bits ou un système 64-bit. Si une longue va correspondre au type de système. Si vous utilisez un système 32-bit, comme l'appareil, il va y avoir 4 octets. Si vous utilisez une version 64-bit comme un grand nombre d'ordinateurs récents, il va être de 8 octets. Ints sont presque toujours 4 octets à ce stade. Longs longs sont presque toujours de 8 octets. Dans le passé, ints utilisé pour être que 2 octets. Mais remarquez que cette satisfait complètement tous ces rapports de plus grande et égale à. Donc, est depuis longtemps parfaitement le droit d'être de la même taille comme un entier, et il est également autorisé à être la même taille qu'un long long. Et il se trouve être celle de 99,999% des systèmes, il va être égal à soit un int ou un long long. Tout dépend de la 32-bit ou 64-bit. >> [L'élève] D'accord. En flotteurs, comment est le point décimal désigné en termes de bits? Comme en binaire? Ouais >>. Vous n'avez pas besoin de savoir que pour CS50. Vous n'avez même pas savoir que dans 61. On n'apprend pas vraiment que dans n'importe quel cours. C'est juste une représentation. J'ai oublié les attributions exactes bits. L'idée de la virgule flottante, c'est que vous allouez un certain nombre de bits pour représenter - Fondamentalement, tout ce qui est en notation scientifique. Donc, vous allouez un nombre spécifique de bits pour représenter le nombre lui-même, comme 1,2345. Je ne pourrai jamais représenter un nombre avec plus de chiffres que 5. Alors vous aussi allouer un nombre spécifique de bits de sorte qu'il a tendance à être comme vous ne pouvez aller à un certain nombre, comme c'est le plus grand exposant, vous pouvez avoir, et vous ne pouvez descendre à un exposant donné, comme c'est le plus petit exposant vous pouvez avoir. Je ne me souviens pas de façon exacte les bits sont affectés à l'ensemble de ces valeurs, mais un certain nombre de bits sont consacrés à 1,2345, un autre certain nombre de bits sont consacrés à l'exposant, et il est seulement possible de représenter un exposant d'une certaine taille. [L'élève] Et un double? Est-ce comme un flotteur extra long? Ouais >>. C'est la même chose que d'un flotteur sauf que maintenant vous utilisez 8 octets au lieu de 4 octets. Maintenant, vous serez en mesure d'utiliser 9 chiffres ou 10 chiffres, et cela pourra aller jusqu'à 300 au lieu de 100. >> [L'élève] D'accord. Et flotteurs sont aussi 4 octets. Oui >>. Eh bien, encore une fois, cela dépend probablement d'ensemble sur l'application générale, mais flotteurs sont 4 octets, les doubles sont 8. Doubles sont appelés double, car ils sont le double de la taille des flotteurs. [L'élève] D'accord. Et y at-il double double? >> Il n'y a pas. Je pense - >> [l'élève] Comme longs longs? Ouais >>. Je ne le pense pas. Oui. [L'élève] Le test de l'année dernière, il y avait une question sur la fonction principale avoir à faire partie de votre programme. La réponse a été qu'il n'a pas à faire partie de votre programme. Dans quelle situation? C'est ce que j'ai vu. [Bowden] Il semble - >> [l'élève] Quelle est la situation? Avez-vous le problème? >> [L'élève] Oui, je peux le tirer vers le haut. Il ne doit pas être, techniquement, mais fondamentalement, il va être. [L'élève] J'ai vu un sur un autre de l'année. C'était comme Vrai ou faux: Un valables - >> Oh, un fichier c.? . [L'élève] Tout fichier doit avoir c - [les deux parlent en même temps - inintelligible] D'accord. C'est donc séparé. Un fichier. C doit juste contenir fonctions. Vous pouvez compiler un fichier en code machine, binaire, que ce soit, sans qu'il soit exécutable pour le moment. Un exécutable valide doit avoir une fonction principale. Vous pouvez écrire 100 fonctions dans 1 fichier, mais pas de grandes et ensuite compiler que jusqu'à binaire, alors vous écrire un autre fichier qui n'a principale mais il demande un tas de ces fonctions dans ce fichier binaire ici. Et donc quand vous faites de l'exécutable, c'est ce que l'éditeur de liens ne est il combine ces 2 fichiers binaires dans un fichier exécutable. Ainsi, un fichier. C n'a pas besoin d'avoir une fonction principale à tous. Et sur les bases de code, vous verrez de grands milliers de fichiers et c. 1 fichier principal. Plus de questions? [L'élève] Il y avait une autre question. Il a dit faire est un compilateur. Vrai ou Faux? Et la réponse était fausse, et j'ai compris pourquoi ce n'est pas comme Clang. Mais qu'est-ce que nous appelons faire si ce n'est pas? Assurez est fondamentalement juste - je peux voir exactement ce qu'il l'appelle. Mais il fonctionne très commandes. Faire. Je peux tirer de cette place. Ouais. Oh, ouais. Assurez fait aussi cela. Cela indique que le but de l'utilitaire make est de déterminer automatiquement quelles pièces d'un vaste programme devront être recompilés et exécutez les commandes pour les recompiler. Vous pouvez faire faire des fichiers qui sont absolument énormes. Assurez regarde les horodatages des fichiers et, comme nous l'avons dit, vous pouvez compiler des fichiers individuels vers le bas, et il n'est pas jusqu'à ce que vous arrivez à l'éditeur de liens qu'ils sont mis ensemble dans un fichier exécutable. Donc, si vous avez 10 fichiers différents et vous apportez une modification à 1 d'entre eux, alors qu'est-ce marque va faire, c'est juste que recompiler 1 fichier puis la relier le tout. Mais c'est beaucoup plus bête que cela. C'est à vous de définir complètement que c'est ce qu'il devrait faire. Il a par défaut la capacité de reconnaître ce genre de choses horodatage, mais vous pouvez écrire un fichier make rien faire. Vous pouvez écrire un fichier make sorte que lorsque vous tapez le rendre juste cd dans un autre répertoire. J'étais frustré parce que tout amure Je intérieur de mon appareil puis-je consulter le PDF à partir du Mac. Donc je vais Finder et que je peux faire Allez, Se connecter au serveur, et le serveur je me connecte à mon Appliance est, puis-je ouvrir le PDF qui est compilée par LaTeX. Mais j'ai été frustré parce que chaque fois que j'avais besoin de rafraîchir le PDF, J'ai eu à le copier dans un répertoire spécifique qu'elle pourrait accéder à et il devenait gênant. Ainsi, au lieu que j'ai écrit un fichier de marque, que vous devez définir la façon dont il rend les choses. Comment vous faites dans ce PDF LaTeX. Tout comme n'importe quel fichier autre marque - ou je suppose que vous n'avez pas vu les fichiers make, mais nous avons dans l'appareil un fichier marque mondiale qui dit simplement, si vous compilez un fichier C, utilisez Clang. Et alors voici dans mon dossier que je fais make-je dire, ce fichier que vous allez avoir à compiler avec LaTeX PDF. Et il est donc LaTeX PDF qui a fait la compilation. Assurez n'est pas la compilation. C'est juste exécutant ces commandes dans la séquence I spécifié. Donc, il va LaTeX PDF, il le copie dans le répertoire que je veux qu'il soit copié, il cd dans le répertoire et fait d'autres choses, mais tout ce qu'il fait est de reconnaître quand un fichier est modifié, et si elle change, alors il exécute les commandes qu'il est censé fonctionner lorsque le fichier est modifié. >> [L'élève] D'accord. Je ne sais pas où les fichiers make mondiaux sont pour moi de le vérifier. D'autres questions? Quelque chose de passé quiz? Toutes choses pointeur? Il ya des choses subtiles avec des pointeurs comme - Je ne vais pas être en mesure de trouver une question quiz sur elle - mais comme ce genre de chose. Assurez-vous que vous comprenez que quand je dis int * x * y - Ce n'est pas exactement quelque chose ici, je suppose. Mais comme * x * y, ce sont 2 variables qui sont sur la pile. Quand je dis x = malloc (sizeof (int)), x est toujours une variable sur la pile, malloc est une rue plus loin dans le tas, et nous allons avoir point x sur le tas. Donc, quelque chose sur les points de pile au tas. Chaque fois que vous malloc quoi que ce soit, vous êtes inévitablement le stocker à l'intérieur d'un pointeur. De sorte que le pointeur est sur la pile, le bloc est malloced sur le tas. Beaucoup de gens se confondre et dire int * x = malloc, x est sur le tas. Non, ce que x pointe vers est sur le tas. x lui-même est sur la pile, sauf si pour une raison quelconque vous avez x est une variable globale, Dans ce cas, il arrive à être dans une autre région de la mémoire. Donc garder la trace, ces diagrammes boîte et la flèche sont assez commun pour le quiz. Ou si ce n'est pas le quiz 0, ce sera le quiz 1. Vous devriez savoir tout cela, les étapes de la compilation depuis que vous avez eu à répondre à des questions sur ceux-ci. Oui. [L'élève] Pourrait-on passer en revue ces étapes - >> Bien sûr. Avant étapes et la compilation que nous avons pré-traitement, la compilation, l'assemblage et la liaison. Le pré-traitement. Qu'est-ce que ça fait? C'est l'étape la plus facile à - bien, pas comme - cela ne signifie pas qu'il devrait être évident, mais ce n'est étape la plus facile. Les gars, vous pourriez mettre en œuvre vous-mêmes. Ouais. [L'élève] Prenez ce que vous avez dans votre comprend comme ceci et le copie puis définit également. Il cherche des choses comme # include et # define, et que des copies et des pâtes ce que ceux qui veulent réellement dire. Donc quand vous dites # include cs50.h, le préprocesseur est un copier-coller cs50.h dans cette ligne. Quand vous dites # define x est 4, le préprocesseur passe par l'ensemble du programme et remplace toutes les occurrences de x par 4. Ainsi, le préprocesseur prend un fichier C valide et génère un fichier valide C où les choses ont été copiées et collées. Alors maintenant compiler. Qu'est-ce que ça fait? [L'élève] Il va de C en binaire. [Bowden] Il ne va pas tout le chemin vers le binaire. [L'élève] Pour le code machine alors? >> Il ne s'agit pas du code machine. [L'élève] Assemblée? >> Assemblée. Il va à l'Assemblée avant qu'il va tout le chemin en code C, et la plupart des langues faire quelque chose comme ça. Choisissez n'importe quel langage de haut niveau, et si vous allez pour le compiler, il est probable que de compiler en plusieurs étapes. D'abord, il va compiler Python à C, alors il va compiler C à l'Assemblée, puis l'Assemblée va se traduit en binaire. Ainsi, la compilation se passe pour le porter de C à l'Assemblée. Le mot compilation signifie généralement qu'il porte à partir d'un niveau plus élevé à un langage de programmation de niveau inférieur. Donc, c'est la seule étape dans la compilation où vous commencez avec un langage de haut niveau et se retrouvent dans un langage de bas niveau, et c'est pourquoi l'étape est appelée la compilation. [L'élève] Au cours de la compilation, disons que vous avez fait # include cs50.h. Est-ce que le compilateur recompiler le cs50.h, que les fonctions qui sont là-dedans, et les traduire en code Assembleur ainsi, ou seront-ils copier et coller quelque chose qui a été pré-assemblée? cs50.h sera à peu près jamais se retrouver à l'Assemblée. Des trucs comme les prototypes de fonctions et les choses sont pour vous d'être prudent. Elle garantit que le compilateur peut vérifier des choses comme vous appelez des fonctions avec les bons types de retour et les arguments bons et des trucs. Donc cs50.h seront prétraitées dans le fichier, puis quand il est la compilation c'est essentiellement jetés après il permet de s'assurer que tout a été appelée correctement. Mais les fonctions définies dans la bibliothèque CS50, qui sont séparés de cs50.h, ceux qui ne seront pas compilés séparément. Cela fait descendre dans l'étape de liaison, donc nous allons y revenir dans un instant. Mais d'abord, qu'est-ce montage? [L'élève] Assemblée en binaire? Ouais >>. Montage. Nous n'appelons pas à le compiler, car l'Assemblée est à peu près une traduction pure du binaire. Il ya très peu de logique pour aller de l'Assemblée en binaire. C'est comme regarder dans une table, oh, nous avons cette instruction; qui correspond à la valeur binaire 01110. Et si les fichiers que l'assemblage sorties sont généralement des fichiers. O. Et les fichiers. O sont ce que nous disions auparavant, comment un fichier n'a pas besoin d'avoir une fonction principale. Tout fichier peut être compilé en un fichier. O tant qu'il s'agit d'un fichier valide C. Il peut être compilé en. O. Maintenant, ce qui apporte de liaison est en fait un tas de fichiers. O et les amène à un exécutable. Et si ce lien ne sont que vous pouvez penser à la bibliothèque CS50 sous forme de fichier. O. Il s'agit d'un fichier binaire déjà compilé. Et donc quand vous compilez votre fichier, votre hello.c, qui appelle GetString, hello.c est compilée vers le bas pour hello.o, hello.o est maintenant en binaire. Il utilise GetString, donc il doit passer à cs50.o, et l'éditeur de liens entre eux smooshes ensemble et copie GetString dans ce fichier et est livré avec un exécutable qui a toutes les fonctions dont il a besoin. Donc cs50.o n'est pas réellement un fichier joint, mais il est assez proche qu'il n'y a pas de différence fondamentale. Donc, reliant simplement apporte un tas de fichiers ainsi que que séparément contenir toutes les fonctions dont j'ai besoin pour utiliser et crée l'exécutable qui sera effectivement parcourue. Et donc c'est aussi ce que nous disions avant où vous pouvez avoir 1000. fichiers c, vous compilez tous à. fichiers o, ce qui prendra probablement un certain temps, puis vous changez 1. fichier c. Vous avez seulement besoin de recompiler que: 1. Fichier c et puis tout le reste relink, relier le tout nouveau ensemble. [L'élève] Lorsque nous lier nous écrivons lcs50? Ouais, soi-lcs50. Que les signaux de drapeau à l'éditeur de liens que vous devriez reliant dans cette bibliothèque. Des questions? Avons-nous dépassé binaire autre que de 5 secondes à la première conférence? Je ne le pense pas. Vous devez connaître tous les grands Os que nous avons passé en revue, et vous devriez être en mesure d', si nous vous avons donné une fonction, vous devriez être en mesure de dire que c'est grand O, à peu près. Ou bien, grand O est rugueuse. Donc, si vous voyez des boucles imbriquées en boucle sur le même nombre de choses, comme int i, i > [l'élève] n carré. >> Il tend à être n carré. Si vous avez triple imbriquées, il a tendance à être n cubes. Donc, ce genre de chose, vous devriez être en mesure de signaler immédiatement. Vous devez connaître le tri par insertion et tri à bulles et tri par fusion et l'ensemble de ceux-ci. Il est plus facile de comprendre pourquoi ils sont ceux carré et n n log n et tout cela parce que je pense qu'il y avait un quiz sur une année où nous avons essentiellement que vous avez donné une implémentation de tri à bulles et dit: «Qu'est-ce que le temps d'exécution de cette fonction?" Donc, si vous le reconnaissez comme une sorte de bulle, alors vous pouvez dire tout de suite n carré. Mais si vous regardez, vous n'avez même pas besoin de réaliser tri à bulles qu'il est; vous pouvez juste dire ce que fait ceci et cela. Ceci est n au carré. [L'élève] Y at-il des exemples difficiles que vous pouvez venir avec, comme une idée similaire de figurer dehors? Je ne pense pas que nous vous donner des exemples difficiles. La chose tri à bulles est à peu près aussi difficile que nous allions, et même que, aussi longtemps que vous comprenez que vous êtes l'itération sur le tableau pour chaque élément du tableau, ce qui va être quelque chose qui n au carré. Il ya des questions générales, comme ici nous avons - Oh. Juste l'autre jour, Doug a affirmé: «J'ai inventé un algorithme qui peut trier un tableau "De n nombres en O (log n)!" Alors, comment savons-nous que c'est impossible? [Réponse de l'élève inaudible] >> Oui. À tout le moins, vous avez de toucher chaque élément du tableau, il est donc impossible de trier un tableau de - Si tout est en ordre non triés, alors vous allez être en contact avec tout ce qui dans le tableau, il est donc impossible de le faire en moins de O de n. [L'élève] Vous nous avez montré que l'exemple de pouvoir le faire en temps O n si vous utilisez beaucoup de mémoire. Ouais >>. Et C'est - je ne sais plus ce que C'est - Est-il compter sorte? Hmm. C'est un algorithme de tri entier. Je cherchais le nom spécial pour cela que je ne me souvenais semaine dernière. Ouais. Ce sont les types de tri qui peuvent accomplir des choses en grand O de n. Mais il ya des limites, comme vous ne pouvez utiliser que des entiers jusqu'à un certain seuil. De plus, si vous essayez de trier that's quelque chose - Si votre tableau est 012, -12, 151, 4 millions, alors que seul élément qui va complètement ruiner le tri ensemble. Des questions? [L'élève] Si vous avez une fonction récursive et il est tout simplement les appels récursifs dans une instruction de retour, c'est récursive, et donc serait-ce pas utiliser plus de mémoire lors de l'exécution ou il serait au moins comparable utiliser la mémoire comme une solution itérative? [Bowden] Oui. Il serait sans doute un peu plus lent, mais pas vraiment. Récursive est très bonne. En regardant de nouveau les cadres de pile, disons que nous avons principal et nous avons bar (int x) ou quelque chose. Ce n'est pas une fonction récursive parfaite, mais la barre return (x - 1). Alors, évidemment, cela est erronée. Vous devez scénarios de référence et d'autres choses. Mais l'idée ici est que ce n'est récursive, ce qui signifie que lorsque la barre principale des appels qu'il va obtenir son stack frame. Dans ce cadre de pile il va y avoir un petit bloc de mémoire qui correspond à son argument x. Et alors disons principale arrive à appeler bar (100); Donc, x va commencer à 100. Si le compilateur reconnaît qu'il s'agit d'une fonction récursive, puis lorsque la barre fait son appel récursif à la barre, au lieu de faire un nouveau frame de pile, qui est l'endroit où la pile commence à se développer largement, elle finira par tomber sur le tas, puis vous obtenez une erreur de segmentation parce que la mémoire commence à entrer en collision. Ainsi, au lieu de faire de son propre cadre de pile, il peut réaliser, hey, je n'ai jamais vraiment besoin de revenir à ce frame de pile, de sorte qu'au lieu que je vais remplacer cet argument avec 99 puis démarrez barre de partout. Et puis, il le fera à nouveau et il atteindra la barre return (x - 1), et au lieu de faire un nouveau frame de pile, il suffit de remplacer son argument actuel à 98 puis revenir au début de la barre. Ces opérations, en remplacement de la valeur 1 sur la pile et le saut en arrière au début, sont assez efficaces. Ainsi, non seulement est-ce la même utilisation de la mémoire comme une fonction distincte qui est itératif parce que vous êtes seulement en utilisant 1 frame de pile, mais vous ne souffrez pas les inconvénients d'avoir à appeler des fonctions. Appel de fonctions peut être un peu cher, car il a tout à faire cette configuration et le démontage et tout ce genre de choses. Donc, cette récursivité terminale est bon. [L'élève] Pourquoi ne pas créer de nouvelles étapes? Parce qu'il se rend compte qu'il n'a pas besoin d'. L'appel à la barre revient tout juste l'appel récursif. Donc, il n'a pas besoin de faire quoi que ce soit avec la valeur de retour. Il va juste retourner immédiatement. Donc, il va tout simplement de remplacer son propre argument et recommencer. Et aussi, si vous n'avez pas la version récursive terminale, alors vous obtenez tous ces bars où quand cette barre retourne il doit retourner sa valeur à celle-ci, alors que la barre revient immédiatement et il renvoie sa valeur à celle-ci, alors il va juste revenir immédiatement et renvoie sa valeur à celle-ci. Ainsi, vous économisez de cette éclater toutes ces choses hors de la pile puisque la valeur de retour va tout simplement être passé tout le chemin du retour quand même. Alors pourquoi ne pas remplacer notre argument avec l'argument mis à jour et repartir à zéro? Si la fonction n'est pas récursive, si vous faites quelque chose comme - [L'élève] si la barre (x + 1). Ouais >>. Donc, si vous le mettez dans un état, puis vous faites quelque chose avec la valeur de retour. Ou même si vous venez de faire retour 2 bar * (x - 1). Alors maintenant, bar (x - 1) doit retourner pour qu'il calculer 2 fois cette valeur, alors maintenant il ne faut son propre cadre pile séparée, et maintenant, peu importe comment dur vous essayez, vous allez avoir besoin de - Ce n'est pas récursive. [L'élève] Aurais-je essayer d'apporter une récurrence de viser une récursivité terminale - [Bowden] Dans un monde idéal, mais en CS50 vous n'avez pas à. Afin d'obtenir la récursivité terminale, en général, vous configurez un argument supplémentaire où la barre sera x int y en et y correspond à la chose ultime que vous voulez retourner. Alors ce que vous allez être de retour bar (x - 1), 2 * y. Donc, c'est juste un haut niveau de transformer la façon dont vous les choses soient récursive. Mais l'argument supplémentaire - Et puis à la fin lorsque vous atteignez votre scénario de base, il vous suffit revenir y parce que vous avez été accumulé tout le temps la valeur de retour que vous souhaitez. Vous sorte de l'aviez fait de manière itérative, mais en utilisant des appels récursifs. Des questions? [L'élève] Peut-être que sur l'arithmétique des pointeurs, comme lors de l'utilisation de chaînes. >> Bien sûr. L'arithmétique des pointeurs. Lorsque vous utilisez des chaînes, il est facile parce que les chaînes sont des étoiles de type char, les caractères sont toujours et toujours un seul octet, et si l'arithmétique des pointeurs est équivalent à l'arithmétique régulière quand vous avez affaire à cordes. Disons simplement char * s = "bonjour". Nous avons donc un bloc dans la mémoire. Il a besoin de 6 octets, car il faut toujours le terminateur null. Et char * s va pointer vers le début de ce tableau. Ainsi, il souligne s. Maintenant, c'est fondamentalement la façon dont fonctionne n'importe quel tableau, qu'il s'agisse d'un retour par malloc ou si elle est sur la pile. Tout réseau est essentiellement un pointeur vers le début de la matrice, puis toute opération de tableau, aucune indexation, va tout simplement dans ce tableau un certain décalage. Donc, quand je dis quelque chose comme s [3], ce qui va s et compter 3 caractères po Donc, s [3], on a 0, 1, 2, 3, si s [3] va se référer à cette l. [L'élève] Et nous pourrions atteindre la même valeur en faisant s + 3 et étoile entre parenthèses? Oui. Ceci est équivalent à * (s + 3); et c'est toujours et toujours équivalent, peu importe ce que vous faites. Vous n'avez pas besoin d'utiliser la syntaxe support. Vous pouvez toujours utiliser le caractère * (s + 3) syntaxe. Les gens ont tendance à aimer la syntaxe support, cependant. [L'élève] Ainsi, tous les tableaux sont en fait que des pointeurs. Il ya une légère différence quand je dis int x [4]; >> [l'élève] Est-ce que créer la mémoire? [Bowden] Cela va créer 4 ints sur la pile, donc 16 octets globale. Cela va créer 16 octets sur la pile. x n'est enregistré nulle part. Il est juste un symbole faisant référence au début de la chose. Parce que vous avez déclaré le tableau à l'intérieur de cette fonction, ce que le compilateur va faire est de simplement remplacer toutes les occurrences de la variable x avec lequel il est arrivé à choisir de mettre ces 16 octets. Il ne peut pas le faire avec char * s parce que l'art est un pointeur réel. Il est gratuit pour ensuite pointer vers d'autres choses. x est une constante. Vous ne pouvez pas pointer vers un tableau différent. >> [L'élève] D'accord. Mais cette idée, cette indexation est la même indépendamment du fait que c'est un régime traditionnel ou si c'est un pointeur vers quelque chose ou si c'est un pointeur vers un tableau malloced. Et en fait, il est tellement équivalent que c'est aussi la même chose. En fait, il traduit seulement ce qu'il ya dedans des supports et de ce qui reste des crochets, les additionne, et déréférence. Donc, c'est tout aussi valable que * (s + 3) ou s [3]. [L'élève] Pouvez-vous avoir des pointeurs pointant vers 2-dimensionnels? Il est plus difficile. Traditionnellement, non. Un tableau à 2 dimensions est juste un tableau à 1 dimension avec une syntaxe pratique parce que quand je dis int x [3] [3], c'est vraiment juste 1 tableau avec 9 valeurs. Et donc quand je l'index, le compilateur sait ce que je veux dire. Si je dis que x [1] [2], il sait que je veux aller à la deuxième rangée, donc ça va sauter les 3 premiers, et puis il veut que la deuxième chose à cela, alors il va obtenir celui-ci. Mais il est encore juste un tableau unidimensionnel. Et si je voulais un pointeur vers ce tableau, Je dirais int * p = x; Le type de x est juste - C'est dur de type dit de x puisque c'est juste un symbole et ce n'est pas une variable réelle, mais c'est juste un int *. x est simplement un pointeur vers le début de la présente. >> [L'élève] D'accord. Et je ne vais pas être en mesure d'accéder à [1] [2]. Je pense qu'il ya une syntaxe spéciale pour déclarer un pointeur, quelque chose de ridicule comme int (* p [-. quelque chose d'absolument ridicule, je ne sais même pas. Mais il ya une syntaxe de déclaration des pointeurs comme des parenthèses et des choses. Il peut même ne pas vous laisser faire cela. Je pourrais revenir sur quelque chose qui pourrait me dire la vérité. Je vais le chercher plus tard, s'il ya une syntaxe pour le point. Mais vous ne le verrez jamais. Et même la syntaxe est si archaïque que si vous l'utilisez, les gens vont être déroutés. Les tableaux multidimensionnels sont assez rares car il est. On est quasiment - Eh bien, si vous faites les choses matrice ça ne va pas être rare, mais en C rarement vous allez utiliser des tableaux multidimensionnels. Ouais. >> [L'élève] Disons que vous avez un tableau très long. Ainsi, dans la mémoire virtuelle, il semble être tout de suite, comme les éléments de droite à côté de l'autre, mais dans la mémoire physique, serait-il possible que cela soit divisé? Oui >>. Comment fonctionne la mémoire virtuelle est-elle sépare juste - L'unité de répartition est une page, qui tend à être 4 kilo-octets, et donc quand un processus dit, hey, je veux utiliser cette mémoire, le système d'exploitation va affecter le 4 kilo-octets pour ce bloc de mémoire peu. Même si vous utilisez uniquement un seul octet peu l'ensemble du bloc de mémoire, le système d'exploitation va lui donner les 4 kilo-octets complets. Alors qu'est-ce que cela signifie est que je pourrais avoir - disons que c'est mon tapis. Cette pile peut être séparé. Ma pile pourrait être méga-octets et méga-octets. Ma pile pourrait être énorme. Mais la pile elle-même doit être divisé en différentes pages, qui, si on regarde par ici disons que c'est notre mémoire, si j'ai 2 Go de RAM, il s'agit de 0 adresse réelle comme l'octet zéro de ma RAM, et cela est de 2 gigaoctets tout le chemin vers le bas ici. Donc, cette page pourrait correspondre à ce bloc ici. Cette page pourrait correspondre à ce bloc ici. Celui-ci pourrait correspondre à celui-ci ici. Ainsi, le système d'exploitation est libre d'attribuer la mémoire physique à n'importe quelle page individu arbitraire. Et cela signifie que si cette frontière se trouve à cheval sur un tableau, un tableau se trouve être à gauche de la droite et de l'ordre d'une page, alors que matrice va être divisée en mémoire physique. Et puis, quand vous quittez le programme, lorsque le processus se termine, ces correspondances obtenir effacé et il est alors libre d'utiliser ces petits blocs pour d'autres choses. Plus de questions? [L'élève] Le pointeur arithmétique. Ah oui >>. Cordes étaient plus faciles, mais en regardant quelque chose comme ints, donc retour à int x [4]; Que ce soit un tableau ou si c'est un pointeur vers un tableau de 4 entiers malloced, ça va être traités de la même manière. [L'élève] Ainsi les tableaux sont sur le tas? [] Bowden tableaux ne sont pas sur le tas. >> [L'élève] Oh. [Bowden] Ce type de tableau a tendance à être sur la pile sauf si vous le déclare à - ignorant les variables globales. Ne pas utiliser des variables globales. A l'intérieur d'une fonction que je dis int x [4]; Cela va créer un bloc de 4-entier sur la pile pour ce tableau. Mais cette malloc (4 * sizeof (int)); va aller sur le tas. Mais après ce point je peux utiliser x et p dans à peu près les mêmes moyens, autres que les exceptions que j'ai dit avant de vous pouvez réaffecter p. Techniquement, leurs tailles sont un peu différentes, mais c'est complètement hors de propos. Vous n'avez jamais fait d'utiliser leurs tailles. Le p je pourrais dire p [3] = 2; ou x [3] = 2; Vous pouvez les utiliser dans exactement de la même façon. Alors maintenant, l'arithmétique des pointeurs - Oui. [L'élève] N'avez-vous pas faire p * si vous avez les parenthèses? Les supports sont un déréférencement implicite. Ok >>. En fait, aussi ce que vous dites avec le pouvez-vous obtenir des tableaux multidimensionnels avec des pointeurs, ce que vous pouvez faire quelque chose comme, disons, int ** pp = malloc (sizeof (int *) * 5); Je vais écrire tout d'abord. Je ne voulais pas celui-là. D'accord. Ce que j'ai fait ici, c'est - Cela devrait être pp [i]. Donc pp est un pointeur vers un pointeur. Vous n'êtes allouer de pp pour pointer vers un tableau de 5 étoiles int. Ainsi, dans la mémoire que vous avez sur la pile pp Il va pointer vers un tableau de 5 blocs qui sont tous eux-mêmes des pointeurs. Et puis quand je malloc ici-bas, je malloc que chacun de ces pointeurs individuels doit pointer vers un bloc séparé de 4 octets sur le tas. Donc, ce point à 4 octets. Et celle-ci évoque de 4 octets différents. Et tous les faire pointer vers leurs propres 4 octets. Cela me donne une façon de faire les choses multidimensionnelles. Je pourrais dire pp [3] [4], mais maintenant, ce n'est pas la même chose que les tableaux multidimensionnels parce que les tableaux multidimensionnels traduit [3] [4] en une seule position dans le tableau x. Ce p déréférence, accède à la troisième indice, alors que déréférence et des accès - 4 serait invalide - le second indice. Considérant que, lorsque nous avons eu l'int x [3] [4] avant comme un tableau multidimensionnel et lorsque vous double étrier c'est vraiment que le déréférencement d'un seul, vous suivez un seul pointeur, puis un décalage, c'est vraiment des références 2D. Vous suivez 2 pointeurs distincts. Ainsi, cela permet aussi la technique que vous ayez des tableaux multidimensionnels où chaque ensemble individuel est de différentes tailles. Je pense donc que déchiquetées tableaux multidimensionnels est ce qu'il a appelé depuis vraiment la première chose pourrait pointer vers quelque chose qui a 10 éléments, la deuxième chose pourrait pointer vers quelque chose qui a 100 éléments. [L'élève] Y at-il une limite au nombre de pointeurs, vous pouvez avoir pointant vers d'autres pointeurs? Non >> Vous pouvez avoir int ***** p. Retour à l'arithmétique de pointeur - >> [l'élève] Oh. Ouais >>. [L'élève] Si j'ai int *** p puis-je faire un déréférencement et je dis * p est égal à cette valeur, est-ce que va faire 1 niveau de déréférencement? Oui >>. Donc, si je veux accéder à la chose que le pointeur de la dernière pointe du doigt - Ensuite, vous faites p ***. Ok >>. Donc, ce n'est p points à 1 bloc, pointe vers un autre bloc, pointe vers un autre bloc. Ensuite, si vous le faites * p = autre chose, alors vous changez ce maintenant pointer vers un autre bloc. Ok >>. [Bowden] Et si ceux-ci ont été malloced, alors vous avez maintenant une fuite de mémoire à moins que vous arrive d'avoir des références différentes de ces puisque vous ne pouvez pas revenir à ceux-là que vous venez jeté. L'arithmétique des pointeurs. int x [4]; va allouer un tableau de 4 entiers où x va pointer vers le début de la matrice. Donc, quand je dis quelque chose comme x [1], je veux qu'il signifie aller à la deuxième entier dans le tableau, qui serait celui-ci. Mais vraiment, c'est 4 octets dans le tableau puisque cet entier occupe 4 octets. Ainsi, un décalage de 1 signifie réellement un décalage de 1 fois la taille de quel que soit le type du tableau est. Il s'agit d'un tableau d'entiers, de sorte qu'il sait faire 1 fois la taille de int quand il veut compenser. L'autre syntaxe. Rappelez-vous que ceci est équivalent à * (x + 1); Quand je dis pointeur + 1, ce qui revient est l'adresse que le pointeur est le stockage ainsi que 1 fois la taille du type de l'aiguille. Donc, si x = OX100, alors x + 1 = ox104. Et vous pouvez abuser de cela et dire quelque chose comme char * c = (char *) x; et maintenant c est va être la même adresse que x. c va être égal à OX100, mais c + 1 sera égal à ox101 depuis l'arithmétique des pointeurs dépend du type du pointeur que vous ajoutez à. Donc c + 1, il regarde c, c'est un pointeur de char, il va ajouter 1 fois la taille du char, qui va toujours être de 1, de sorte que vous obtenez 101, alors que si je fais x, qui est aussi toujours 100, x + 1 va être 104. [L'élève] Pouvez-vous utiliser c + + afin de faire avancer le pointeur de 1? Oui, vous le pouvez. Vous ne pouvez pas faire cela avec x car x n'est qu'un symbole, c'est une constante, vous ne pouvez pas changer x. Mais c arrive d'être juste un pointeur, donc c + + est parfaitement valable et il sera incrémenté de 1. Si c était juste un int *, alors c + + serait de 104. + + Pointeur arithmétique fait juste que c + 1 aurait fait l'arithmétique des pointeurs. C'est en fait la façon dont beaucoup de choses comme le tri par fusion - Au lieu de créer des copies de choses, vous pouvez passer à la place - Comme si je voulais faire passer cette moitié de l'ensemble - nous allons effacer une partie de cela. Disons que je voulais faire passer ce côté de la baie dans une fonction. Que ferais-je passer à cette fonction? Si je réussis x, je passe cette adresse. Mais je tiens à transmettre cette adresse particulière. Alors, que dois-je passer? [L'élève] Pointeur + 2? [Bowden] Donc x + 2. Oui. Cela va être cette adresse. Vous serez également très souvent le voir comme x [2], puis l'adresse de cela. Alors vous avez besoin de prendre l'adresse de celui-ci parce que le support est un déréférencement implicite. x [2] correspond à la valeur qui se trouve dans cette zone, et que vous voulez l'adresse de cette boîte, ce que vous dites et x [2]. C'est comme ça que quelque chose sorte de fusion où vous voulez passer la moitié de la liste pour quelque chose vous avez vraiment passer et x [2], et maintenant aussi loin que l'appel récursif est concerné, mon nouveau tableau commence là. Questions de dernière minute. [L'élève] Si nous ne mettons pas l'esperluette ou un - qu'est-ce que s'appelle? >> Star? [L'élève] Star. Techniquement >>, opérateur de déréférencement, mais - >> [l'élève] déréférencement. Si nous ne mettons pas une étoile ou une esperluette, ce qui se passe si je viens de dire y = x et x est un pointeur? Quel est le type de y? >> [L'élève] Je vais juste dire que c'est le pointeur 2. Donc, si vous venez de dire y = x, x et le point maintenant y à la même chose. >> [L'élève] Point à la même chose. Et si x est un pointeur int? >> Il se plaindre parce que vous ne pouvez pas affecter des pointeurs. [L'élève] D'accord. Rappelez-vous que les pointeurs, même si nous les attirer comme des flèches, vraiment tout magasin, ils - int * x - x est vraiment tout ce stockage est quelque chose comme OX100, ce qui nous arrive à représenter comme pointant vers le bloc stocké à 100. Donc, quand je dis int * y = x, je suis simplement copier OX100 en y, que nous allons juste y représenter, en soulignant particulièrement à OX100. Et si je dis int i = (int) x, puis i va stocker tout ce que la valeur de OX100 est à l'intérieur de celui-ci, mais maintenant ça va être interprété comme un entier au lieu d'un pointeur. Mais vous devez le casting ou bien il va se plaindre. [L'élève] Alors voulez-vous dire de jeter - Est-ce que ça va être coulée int int x ou de y casting? [Bowden] Quoi? [L'élève] D'accord. Après ces parenthèses est qu'il va y avoir un x ou ay il? [Bowden] Soit. x et y sont équivalents. >> [L'élève] D'accord. Parce qu'ils sont les deux pointeurs. Ouais >>. [L'élève] Donc, il serait de stocker les 100 hexadécimal sous forme entière? >> [Bowden] Ouais. Mais ce n'est pas la valeur de ce qu'il désigne. [Bowden] Ouais. >> [L'élève] Il suffit donc de l'adresse sous forme de nombre entier. D'accord. [Bowden] Si vous vouliez, pour une raison bizarre, vous pourriez traiter exclusivement avec des pointeurs et ne jamais traiter avec des nombres entiers et juste comme int * x = 0. Ensuite, vous allez être vraiment confus une fois l'arithmétique des pointeurs commence à se produire. Alors que les chiffres qu'ils contiennent n'ont pas de sens. C'est juste la façon dont vous vous retrouvez à les interpréter. Je suis donc libre de copier OX100 d'un * int en int, et je suis libre de céder - vous êtes probablement va se faire engueuler pour ne pas couler - Je suis libre de céder quelque chose comme (int *) ox1234 dans ce * int arbitraire. Donc ox123 est tout aussi valable une adresse mémoire est aussi et y. Et y arrive de retourner quelque chose qui est à peu près ox123. [L'élève] Serait-ce une façon vraiment cool d'aller du format hexadécimal au format décimal, comme si vous avez un pointeur et vous le lancez comme un int? [Bowden] Vous pouvez vraiment juste imprimer en utilisant comme printf. Disons que j'ai int y = 100. Donc printf (% d \ n - que vous devriez déjà savoir - imprimer ce que un entier x%,. Nous allons l'imprimer en hexadécimal. Ainsi, un pointeur n'est pas stocké en hexadécimal, et un entier n'est pas stockée sous forme décimale. Tout est stocké sous forme binaire. C'est juste que nous avons tendance à montrer des pointeurs sous forme hexadécimale parce que nous pensons des choses dans ces blocs de 4 octets, et les adresses mémoire ont tendance à être familier. Nous sommes comme si elle commence par bf, puis il arrive à être sur la pile. Donc, c'est juste notre interprétation des pointeurs sous forme hexadécimale. D'accord. Toutes les dernières questions? Je serai ici pour un peu plus tard si vous avez quoi que ce soit d'autre. Et c'est la fin de l'. [L'élève] Yay! [Applaudissements] [CS50.TV]