Comme tout le monde l'a dit, cela dépend de la cible et du complicateur, mais pour vous, ceux-ci restent constants et il n'y a pas d'autres choses dans ce code qui semblent introduire un caractère aléatoire dans la pile. parlant), donc il fera la même chose à chaque fois.
La pile système augmente généralement d'une adresse élevée à une adresse inférieure. Si le pointeur de la pile est 0x1234 et que vous appuyez sur une valeur (sur un système de 32 bits {4 octets}), le pointeur de pile devient 0x1230.
Les tableaux sont adressés de l'adresse la plus basse à l'adresse la plus élevée. Si vous avez
char a[2];
et [0] est à 0x0122 alors sera à 0x[1].
Votre tableau en doit2
est une variable automatique, ce qui signifie qu'il est créé à l'entrée de la fonction et supprimé à la sortie de la fonction. Les variables automatiques doivent soit vivre sur la pile, soit dans des registres. Comme il s'agit d'un tableau, il est beaucoup moins compliqué pour le compilateur de le placer dans la RAM plutôt que dans les registres (cela facilite l'indexation car il ajoute simplement la taille de l'index * à l'adresse du premier membre du tableau). Puisque la pile est en RAM, le compilateur place le tableau sur la pile. L'allocation d'espace sur la pile pour cette matrice signifie que le pointeur de pile est sizeof(int)*16
inférieur à ce qu'il serait si ce tableau n'était pas présent. Le pointeur de pile pointe le plus probablement vers overflowme[0]
tandis que dans doit2
.
Il y a d'autres choses qui pourraient être sur la pile et quelques choses qui devaient être sur la pile. Les choses qui devaient être sur la pile sont les pointeurs de retour qui ont été poussés là quand les fonctions ont été appelées.Sur un système 32 bits, ceux-ci devraient occuper 4 octets chacun. Les choses qui auraient pu être sur la pile (si le compilateur voulait l'utiliser) est le pointeur d'image précédent. (Explicit *) Les cadres empilés sur x86-32 ne sont que l'espace entre ESP et EBP, mais ils ne sont pas nécessaires si souvent qu'ils ne sont pas utilisés et EBP est simplement utilisé comme un registre généraliste (des registres plus généraux sont disponibles généralement bon). L'utilisation de cadres de pile est utile, car ils facilitent le débogage car ESP et EBP agissent comme des marqueurs pour les bords des variables locales. Les cadres de pile sont parfois nécessaires, comme lorsque vous utilisez alloca
ou les tableaux automatiques de taille variable C99 car ils permettent à l'espace variable local pour une fonction d'être supprimé par mov EPB, ESP
ou une instruction équivalente plutôt que sub size_of_local_variable, ESP
afin que le compilateur n'ait pas besoin de connaître la taille du cadre. Ils permettent également d'adresser les variables locales par rapport à l'EBP plutôt qu'à l'ESP qui, dans le cas de alloca
, change. EBP dans ce cas ne changera pas jusqu'à la fin de la fonction en cours, sauf si elle a été modifiée et restaurée par les fonctions d'appel.
Lors de la compilation sans optimisations, les compilateurs activés utilisent souvent toujours des trames de pile car elles facilitent le débogage. Il est également plus facile de modéliser le code avec des cadres de pile, puis de transformer le code pour ne pas les utiliser après avoir prouvé qu'ils ne sont pas nécessaires.
Ainsi, la valeur précédente de RASE peut ou ne peut pas résider sur la pile entre l'adresse de retour (quelque part dans problem2
) et le dernier élément de l » overflowme
doit2
. Le compilateur est également libre de mettre n'importe quoi d'autre sur la pile, donc qui sait quoi d'autre pourrait être là. La variable locale int x
peut être placée dans un registre ou dans la pile. Lors de la compilation sans optimisations activées, les variables locales vont souvent sur la pile même lorsqu'elles peuvent entrer dans les registres.
Alors, laisse supposer qu'il ya « tableau overflowme
s, un ancien pointeur de trame, une adresse de retour, et problem2
» doit2
s x
sur la pile (et un peu plus de choses sous {qui est vraiment à une adresse supérieure} elle).
Depuis &(overflowme[i])
est le même que l'ajout de l'adresse du premier élément de overflowme
(i * {la taille de int
}) et l'ancien RASE mensonges après le dernier élément de overflowme
et une adresse de retour se trouve après l'ancien EBP et int x
se trouve après l'adresse de retour, x
est définitivement debout dans le chemin d'être écrasé par un dépassement de tampon. Pourquoi cela se produit pour l'index de 37 n'est pas clair. Le pointeur mathématique (en supposant que seuls les éléments indiqués ci-dessus se trouvent sur la pile entre le tableau et x
) ne suggère pas qu'il devrait être basé sur des pointeurs de 4 octets (machine 32 bits), mais s'il s'agit d'un système de 8 octets (Machine 64 bits), puis le calcul est plus proche de l'adresse que je m'attendais x
à être à sizeof(int) == 8
. Le compilateur aurait également pu aller de l'avant et attribuer l'espace de pile pour les appels à printf
(les arguments variables après que la chaîne de format doit aller sur la pile) ce qui affecterait le calcul (et encouragerait le compilateur à placer x
sur la pile car il le ferait dois y pousser de toute façon).
Si vous voulez une réponse plus détaillée à votre question, regardez l'assemblage pour ce code et établissez l'adressage exact.
- Vous pouvez considérer que le cadre de pile est présent même si EBP n'est pas utilisé comme pointeur de base de trame, mais que le cadre ne sera pas encadré.
non, 37 fonctionne à chaque fois! – user133466
Est-ce que ce sont les devoirs? –
Ce ne sera pas nécessairement - cela ne se produira qu'avec un compilateur spécifique, CPU, drapeaux de compilateur, etc –