2015-09-10 3 views
-2

Я изучаю C и недавно имел свой класс по указателям и адресам памяти. Учитель сказал нам, что ячейки памяти, в основном без знака, поэтому мы можем отобразить их, используя следующий код:Как работать с адресами памяти в C? Являются ли они hexadexcimals или unsigned ints?

int a; 
printf("%u", &a); 

или

int a, *p=&a; 
printf("%u", p); 

В самом деле, это работает, но я также читал в некоторых форумах что для печати адресов следует использовать% p или% x. Итак, они должны быть шестнадцатеричными числами ... Являются ли целые числа, которые я вижу выше, фактически шестнадцатеричные числа, преобразованные в десятичные числа? И то, что адреса фактически в их базовой форме - шестнадцатеричные или целые или просто двоичные nos.

Пожалуйста, помогите мне в этом.

+3

Шестнадцатеричные и десятичные числа - это просто произвольные представления чисел. Вы не можете сказать, что число явно * hex или dec, это и то, и другое. – CoffeeandCode

+3

Ваш вопрос: «Является ли четверть 25 центов, или 1/4 доллара?». Hex и decimal - это всего лишь два способа взглянуть на одно и то же значение. – user657267

+0

Я понимаю это сейчас. Я не должен рассматривать их как два разных числа, но одно число представлено двумя способами. Большое спасибо. –

ответ

5

Адреса в их базовой форме - это просто значения. Hex, или двоичные, или восьмеричные изображения номера. Например, 42, 0x2a, 2a16, 052, 528, 1010102 и 1042 все разные представлений одного и того же значения .

С точки зрения того, что вы должны использовать в качестве строки формата, %p является правильным. Нет никакой гарантии, что целые числа без знака будут иметь такое же количество бит, что и указатель, поэтому вы можете потерять информацию.

Фактически, предоставление и аргумент printf, который не соответствует соответствующему спецификатору формата, на самом деле является неопределенным поведением.

Использование %u или %x вариантов может хорошо работать на большинстве систем, где указатели и целые числа без знака имеют совместимый размер, но действительно портативный код не будет полагаться на это. Например, реализация может иметь 16-разрядное целое число без знака (для удовлетворения минимальных требований к диапазону в ISO C11 Appendix E) и 1024-разрядного типа указателя (я должен получить один из тех машин).

+0

Хорошо. Спасибо, что освободил меня от сомнений. –

0

В компьютере, все это число, если вы достаточно близко :)

Указатель действительно число. Единственная проблема заключается в том, что размер этого числа зависит от архитектуры оборудования и модели памяти.

Например, некоторые процессоры используют 64-разрядные адреса, в то время как микроконтроллеры могут использовать только 16 бит.
Даже на 64-битном процессоре данный процесс может быть ограничен 32-разрядными адресами (обычно это архитектура WIN32, которая может работать на 64-битном процессоре).

Если вы попытаетесь напечатать указатель, используя% u, у вас нет гарантии, что размер целого будет равен размеру указателя. Как правило, на архитектуре WIN64% u отображает только первые 32 бита указателя.

С другой стороны, формат% p принимает фактический размер указателя и гарантирует, что его значение будет отображаться правильно.

+0

Я получаю это сейчас ... Большое спасибо. –

0

портативный способ напечатать адрес объекта является

int a, *p=&a; 
printf("%p", (void *) p); 

"%p" Спецификатор соответствует void*. C не предоставляет спецификатор printf() для int *, но все указатели объектов могут преобразовывать в void*.

Многие архитектуры используют линейный адрес, начинающийся с 0 до некоторого большого значения, который может превышать INT_MAX. Тем не менее, C предоставляет множество схем адресов. Выход из вышеперечисленного может быть любой из ниже, и не обязательно только из числовых символов:

123456789 
0x0001C800 
1000:0008 
int:0000 
+0

Почему (void *) p? Почему бы не просто printf ("% d", p); –

+0

@AnirudhKhanna Поскольку '% p' работает только с' void * ' –

+0

@Anirudh Khanna' printf() 'specifier' "% d" 'соответствует' int'. ''% d "' может не предоставлять правильный вывод указателем. Это неопределенное поведение. Ваш код может видеть ожидаемый результат на одной платформе, сбой на другом. – chux

1

Вот стандартный способ сделать это в C:

#include <inttypes.h> 
#include <stdio.h> 
#include <stdlib.h> 

int main(void) 
{ 
    int x = 0; 

    printf("The value of &x is %" PRIxPTR " in hex and %" PRIuPTR 
      " in decimal, and %%p displays %p.\n", 
      (uintptr_t)&x, 
      (uintptr_t)&x, 
      (void*)&x); 

    return EXIT_SUCCESS; 
} 

uintptr_t представляет собой целое число который способен безопасно хранить указатель на объект.

И решение C++:

#include <climits> 
#include <cstddef> 
#include <cstdint> 
#include <iomanip> 
#include <iostream> 

using std::cout; 
using std::endl; 
using std::resetiosflags; 
using std::setfill; 
using std::setiosflags; 
using std::setw; 
using std::streamsize; 

int main(void) { 
    int x = 0; 

    static const streamsize hex_width = static_cast<streamsize>(sizeof(int*)*CHAR_BIT/4); 
    static const streamsize dec_width = 0; // Don't pad. 

    cout << "The value of &x is " << resetiosflags(cout.basefield) << setw(hex_width) << setfill('0') 
     << setiosflags(cout.hex) << reinterpret_cast<uintptr_t>(&x) << " in hex, " 
     << resetiosflags(cout.basefield) << setw(dec_width) 
     << setiosflags(cout.dec) << reinterpret_cast<uintptr_t>(&x) << " in decimal, and " 
     << resetiosflags(cout.basefield) << static_cast<void*>(&x) << " as a pointer." << endl; 

    return EXIT_SUCCESS; 
-2

Оба права. Попробуйте этот код:

int a = 10; 
printf("%p\n%u", &a, &a); 

напечатает адрес указателя в шестнадцатеричном (% р) и в UINT (% U). Если вы конвертируете hex в uint, используйте calc of windows в режиме разработчика, вы будете иметь то же значение.

прочитать: https://en.wikibooks.org/wiki/C_Programming/Pointers_and_arrays

Пейте кофе и держать кодирования! =)

+0

Это приводит к неопределенному поведению с использованием неверного спецификатора формата (дважды). Вам нужно присвоить значение правильному типу. –

+0

Я не пользователь c, но я подумал, что «& a» идентифицирует (void *) указатель на? поэтому% p будет в порядке. Возможно, это разница между c и C++? –

+0

Код, компилировать и запускать. Обе работы, первое шестнадцатеричное значение show и второе показывают значение unsigned. После этого конвертируйте hex в unsigned (10 base) и получите одно и то же значение. Все адреса в C/C++ являются указателями void в памяти, тип используется для значения и размера шага при ходьбе в памяти. –

0

Изучение С, одно из препятствий, с которым все должны преодолевать, подружится с указателями. В C, в отличие от других языков, вы кодируете на очень низком уровне машины и что почти все, что вы делаете на C, - это операция с памятью. Ключ к обучению C означает «Что у вас на этом адресе памяти?», «Насколько большой блок памяти у меня есть?» и «Как мне работать с ним правильно?». К сожалению, много раз люди делают этот процесс намного сложнее, чем нужно.

Одна вещи, которая действительно помогает, и что имеет фундаментальное значение для понимания объемности программирования низкого уровня, просто, что все переменные являются не более чем этикеткой, или еще лучше, псевдонима адреса памяти ,

Единственное, что отличает обычные переменный и переменный указатель является то, что вместо того, чтобы быть псевдонимом, где некоторые прямых или непосредственное значения сохраняются в памяти (например, 5 или 21), указатель a псевдоним в ячейку памяти, где хранится адрес . Проще говоря, указатель сохраняет адрес памяти в качестве значения.

Придумайте основы таким образом:

int x = 5; 

Что такое x? Это метка для адреса памяти. Какой адрес памяти делает x ярлык (псевдоним)? &x. Что хранится по этому адресу? 5. До сих пор хорошо?

Ну, что такое указатели?

int *y;   /* what's so special about the '*' in the declaration? 
        Nothing, just basic syntax for pointer declaration. */ 

y = &x;   /* which stores the address of x as the value of y. 
        And what is x? -- a label to a memory location */ 

Что такое y? Это метка для адреса памяти. Какой адрес памяти делает y ярлык (псевдоним)? &y. Что хранится по этому адресу? &x (адрес x). Все еще хорош?

Теперь давайте рассмотрим короткий пример, который, надеюсь, поможет цементировать основы, чтобы вы могли по-настоящему использовать указатели, как предполагалось, для доступа к блокам памяти и манипуляциям с ними, таких как массивы, связанные списки, стеки и т. Д. .:

#include <stdio.h> 

void prn_values (int val, int *ptr); 

int main (void) { 

    int x = 5;   /* declare variable & pointer */ 
    int *y = &x;  /* y now holds the address of x */ 
    prn_values (x, y); 

    int z = 7; 
    y = &z;    /* y now holds the address of z */ 
    prn_values (z, y); 

    z = x;    /* y still holds the value of z, */ 
    prn_values (z, y); /* but the value of z has changed */ 

    *y = 9;    /* y still holds z, but we have changed z by  */ 
         /* changing the value at the address stored in y */ 
    printf ("\n z : %-14d (value)\n &z : %-14p (address)\n", z, &z); 

    /* actually using a pointer to an array */ 
    char array[] = "pointer arithmetic."; 
    char *p = array; /* p points to the start address of array */ 

    printf ("\n array : %p (address)\n array [0]: %p (address)\n\n", 
      array, &array[0]); 

    while (*p) { 
     printf ("  %c : %p\n", *p, p); 
     p++; 
    } 

    return 0; 
} 

void prn_values (int val, int *ptr) 
{ 
    printf ("\n x : %-14d (value)\n &x : %-14p (address)\n", val, &val); 

    printf (" y : %-14p (value)\n &y : %-14p (address)\n *y : %-14d " 
      "(dereference)\n", ptr, &ptr, *ptr); 
} 

Compile

gcc -Wall -Wextra -o pointers pointers.c 

Выход

Ниже указателю целого числа y присваивается значение x, затем z и выводятся значения, адреса и значения по адресу указателя. Примечание как значение указателя является адресом переменной, на которую указывает. Пока y указывает на z, значение z изменено. Так как y хранит (указывает на) адрес памяти, псевдоним которого z, *y отражает изменение. Затем обратите внимание, как изменение работает в обоих направлениях. Если значение по адресу, содержащемуся y, изменяется путем присвоения *y, это изменение также отражается в z.

Это все рудиментарные основы указателя, что очень мало, чтобы показать истинную силу и использование указателей. Это указатель arithmetic и возможность передавать указатель как аргумент функции, где вид использования и значения указателей. Обратите внимание, как конечный цикл while использует указатель p в массиве символов для доступа к каждому адресу (содержащему символ) в массиве не более чем приращением p на 1 (p++;) на каждой итерации.

$ ./pointers 

    x : 5    (value) 
&x : 0x7ffff2c72590 (address) 
    y : 0x7ffff2c72590 (value) 
&y : 0x7ffff2c725b0 (address) 
*y : 5    (dereference) 

    z : 7    (value) 
&z : 0x7ffff2c725a0 (address) 
    y : 0x7ffff2c725a0 (value) 
&y : 0x7ffff2c725b0 (address) 
*y : 7    (dereference) 

    z : 5    (value) 
&z : 0x7ffff2c725a0 (address) 
    y : 0x7ffff2c725a0 (value) 
&y : 0x7ffff2c725b0 (address) 
*y : 5    (dereference) 

    z : 9    (value) 
    z : 0x7ffff2c725a0 (address) 

array : 0x7ffff2c725c0 (address) 
array [0]: 0x7ffff2c725c0 (address) 

    p : 0x7ffff2c725c0 
    o : 0x7ffff2c725c1 
    i : 0x7ffff2c725c2 
    n : 0x7ffff2c725c3 
    t : 0x7ffff2c725c4 
    e : 0x7ffff2c725c5 
    r : 0x7ffff2c725c6 
      : 0x7ffff2c725c7 
    a : 0x7ffff2c725c8 
    r : 0x7ffff2c725c9 
    i : 0x7ffff2c725ca 
    t : 0x7ffff2c725cb 
    h : 0x7ffff2c725cc 
    m : 0x7ffff2c725cd 
    e : 0x7ffff2c725ce 
    t : 0x7ffff2c725cf 
    i : 0x7ffff2c725d0 
    c : 0x7ffff2c725d1 
    . : 0x7ffff2c725d2 

Арифметика указателей для всех типов массивов (char, int, struct foo, whatever). Объявив указатель type X, компилятор знает размер типа данных X. Через арифметику указателя доступ и итерация сложных типов данных ничем не отличается. Указатели на функции и указатели на указатели работают одинаково. По мере того, как вы приобретаете больше опыта, вы будете полагаться на указатели как на большую часть вашего инструментария C, так что стоит потратить время на тщательное понимание основ. Передача указателей в качестве аргументов функции - еще одна целая грань того, как указатели используются в C. Поскольку этот ответ рос намного дольше, чем первоначально предполагалось, мы оставим это на другой день.Дайте знать, если у вас появятся вопросы. Удачи с C.

+0

Это было удобное руководство для введения указателя! Большое спасибо. Это помогло мне понять многие новые концепции. –

+0

Рад, что я мог помочь. Истинный ключ к пониманию власти C - это реализация, в отличие от других языков, вы кодируете на очень низком уровне машины и что почти все, что вы делаете на C, - это операция с памятью. Вот почему ключ к тому, чтобы действительно свободно программировать на C, означает «Что у вас на этом адресе памяти?», «Насколько большой блок памяти у меня есть?» и «Как мне с этим работать?». Ключом к работе с памятью является указатель. Зачем? - Это единственный тип переменной, который имеет адрес памяти в качестве значения. –