2013-04-05 15 views
15

mam prosty kod w następujący sposób:Jaki jest adres wydrukowany przez printf() z% p formacie wc?

#include<stdio.h> 

int glob; 

int main(void) 
{ 
    int a; 
    printf("&a is : %p \n", &a); 
    printf("glob is : %p \n", &glob); 
    return 0; 
} 

Wyjście z powyższego programu jest: Pierwszy bieg:

&a is : 0x7fff70de91ec 
glob is : 0x6008f4 

Drugi bieg:

&a is : 0x7fff38c4c7ac 
glob is : 0x6008f4 

uczę o wirtualne & adresy fizyczne. Mam następujące pytanie:

  1. Jaki jest wydrukowany adres (fizyczny/wirtualny) zmiennej "a"?
  2. Jeśli to jest wirtualne, to jak zmienia się w każdym uruchomieniu tego samego programu? Jak rozumieć kompilator zapewnia wirtualny adres do zmiennych w czasie kompilacji?
  3. Dlaczego adres zmiennej globalnej jest stały w każdym uruchomieniu programu?

W ten program wykonywany w systemie Linux: 2.6.18-308.el5 x86_64 GNU/Linux

Opracowano na podstawie: wersję gcc 4.1.2 20.080.704 (Red Hat 4.1.2-52)

+2

Twój program wywołuje ** niezdefiniowane zachowanie **. '% P' musi mieć wartość ptr-to-void, więc musisz rzutować na' (void *) 'w obu printfs. – Jens

+0

@Jens Czy argument nie byłby domyślnie rzutowany na 'void *'? –

+3

@VilhelmGray niejawne konwersje mają miejsce, gdy typ jest oczekiwany, ale nie ma typów w funkcjach variadic. – effeffe

Odpowiedz

6

Adresy widoczne w programie są zawsze wirtualne, a zachowanie opisane przez OP jest przeciwdziałaniem systemu Linux, aby uniknąć buffer overflow attacks.

Wystarczy spróbować, możesz wyłączyć go z

sysctl -w kernel.randomize_va_space=0 

następnie uruchom ponownie program i oglądać.

Globalny znajduje się w innej przestrzeni pamięci, która nie może być szkodliwa w hackish-wise punktu widzenia. To dlatego, że nie jest randomizowane za każdym razem.

15

Oba adresy: są wirtualne.

Nowoczesne systemy używają randomizacji stosów, aby zapobiec tak zwanym atakom polegającym na rozbijaniu stosów, dlatego zmienna lokalna może zmieniać swoje położenie w każdym biegu. Jednak zmienna globalna jest przechowywana w pliku wykonywalnym i jest ładowana za każdym razem przy tym samym przesunięciu.

+7

Warto zauważyć, że niektóre inne systemy operacyjne i niektóre bezpieczniejsze dystrybucje Linuksa mają PIE (niezależne od pozycji pliki wykonywalne) i na tych adresach zmienna globalna również się zmieni. – Art

+0

@ Joachim, tak, zgadzam się, że zmienne lokalne są przechowywane w stosie, ale dlaczego mówi się, że zmienne mają określony adres w czasie kompilacji przez kompilator? – BSalunke

+0

@Balunke Zmienne lokalne są umieszczane pod określonym _offset_ ze wskaźnika stosu, gdy wywoływana jest funkcja. Tak więc, podczas gdy kompilator nie nadaje mu stałego adresu, zmienne nadal mają adres podany przez kompilator. –

3

Twój program będzie zawsze zawsze zobaczyć tylko adres wirtualny.

Prawdziwe adresy są dostępne tylko w menedżerze pamięci wirtualnej w trybie jądra.

Globale ma ten sam adres (dopóki nie umieścisz przed nim innych zmiennych), ponieważ jest on tworzony w segmencie danych.

Zmienne lokalne są zawsze tworzone na stosie.

+1

Zmienne lokalne mogą być również "statyczne" ... :) – unwind

+0

@unwind: tak, masz rację. Po prostu opiszę kod, który OP zapewnia. – rkosegi

2

Wszystkie adresy widziane przez program są wirtualne. Jednak zmienne lokalne przechodzą na stos i globalne w specjalnym obszarze zwanym segmentem danych. Chociaż względne położenia zmiennych są ustalane podczas kompilacji, stos może się różnić w każdym przebiegu.