2015-05-18 4 views
3

Я только что достиг указателей в своем учебнике, но это не объясняет достаточно хорошо, поэтому мне нужна помощь.Попугался с указателями на C

Я знаю, что такое указатели и что они делают. например, я очень хорошо понимаю следующий пример:

#include <stdio.h> 

int main() 
{ 
    int num = 5; 
    int *point; 

    point = &num; 

    *point = 8; 

    printf("%d %d", num, *point); 

    return 0; 
} 

Точка указывает на число (сохраняя адрес num в качестве значения). а затем я разыскиваю точку, чтобы изменить исходное значение num.

Рассмотрим теперь красивый модифицированную версию того же примера:

#include <stdio.h> 

int main() 
{ 
    int num = 5; 
    int *point; 

    point = 8; 

    printf("point: %d\n", point); 

    printf("sum (%d) = num (%d)+ point (%d)", num+point, num, point); 

    return 0; 
} 

У меня есть несколько вопросов:

1- почему даже можно назначить нормальное значение (8) к указателю (точке)? не указатели должны хранить только адреса других вещей? что происходит в строке 8?

2- я скомпилировал код и для второго printf он показывает: sum (28) = num (5) + point (8) Почему сумма равна 28? 5 + 8 - 13. Что происходит?

+0

Разве ваш компилятор не жалуется на вторую программу? gcc дает 4 отдельных предупреждения при компиляции кода. –

+1

Я использую Code :: Blocks с mingw32 и компилируется с предупреждениями ... но он компилируется. В любом случае, я мог бы использовать некоторые ответы на мои вопросы. – ufosecret

+2

Дело в том, что код создает неопределенное поведение. Есть упрощенные ответы о том, как указатели похожи на ints, но такие ответы не совсем правильные, и правда многое зависит от подробного чтения языкового стандарта. –

ответ

6

Вы можете указать point по адресу памяти 8 (или 0x00000008). Это эквивалентно point = (int*)8; Я получаю сообщение об ошибке <Unable to read memory>, потому что я использую C++, и я предполагаю, что он защищает меня от моей глупости.

Почему 5 + 8 = 28? Если вы добавите число n в указатель типа T, вы переместите этот указатель на n элементов типа T. Ваша машина имеет дело с 32-битными ints, размер которых составляет 4 байта, поэтому 8 + (5 * 4) = 28 .

+1

Но я печатаю его на первом printf с% d, и он показывает 8. не является ли% d для целых чисел, а не для памяти? – ufosecret

+0

@ufosecret Ожидаете ли вы% d разыменовать указатель? Я думаю, что это просто показывает их внутреннее представление. –

+1

'% d' - это просто формат целочисленного числа. Если вы укажете значение указателя (адрес), оно просто напечатает значение этого адреса в десятичной форме. – Santa

3

Во втором примере вы назначаете 8 указателю. Надеюсь, вы получили хорошее предупреждение от своего компилятора. Но вы можете назначить 8. Просто не разыщите его. Вероятно, вы получите ошибку сегментации или хуже. Сумма num + point имеет смысл. Это называется арифметикой указателя. point - указатель на целое число. Int на вашем компьютере - 4 байта. Сумма num + point равна (5 * sizeof (int)) + 8, что равно 28 в вашей системе.

+0

'5 + (int *) 8' имеет смысл, но это неопределенное поведение. –

+0

@Anonymous: Лучше расскажите об этом ядру и встроенным ребятам. 40 лет работы по канализации. –

3

Поскольку практически все компиляторы C реализуют указатели в виде интегральных чисел для поддержки арифметики указателя.

int arr[10]; 
int *ip = arr; 

arr[2] = 42;  // this is equivalent to: 
*(ip + 2) = 42; // or 
*(2 + ip) = 42; 

Скажем, &arr[0] (адрес первого элемента массива целых чисел arr) является 0xcafe0000. Предположим, что значение int занимает 4 байта в памяти. Затем &arr[1] находится на 0xcafe0004, &arr[2] находится на 0xcafe0008 и т. Д. С шагом 4 байт.

Компилятор видит это:

int i = arr[5];  // which is equivalent to: 
int j = *(arr + 5); 

и взять адрес 0xcafee0000, и добавить (5 * sizeof(int)), который выходит на 0xcafee0028 перед тем разыменования его.

Вот почему, когда вы приводите любой ПР «целое число к типу указателя, скажем:

int *p = (int*) 8; 

А потом добавить:

p = p + 5; // p := 28 

компилятор примет p» значение s, и добавьте его в операнд 5раз размер int.

+0

'p = p + 5' - это неопределенное поведение. –

1

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

Причина, по которой указатель num + равен 28, заключается в том, что компьютер является причиной того, что ваши адреса являются 32-разрядными в используемом вами компьютере. Таким образом, C имеет эту функцию, называемую арифметикой указателя, что означает, что добавление/вычитание любого значения в указатель фактически добавит 4 * num, поскольку в каждом 32-битном слове, которое имеет ваша машина, есть 4 байта. В результате вы получаете точку + num == 8 + (4 * 5).

=== EDIT

Подобно тому, как дополнительный, когда вы делаете арифметические операции над указателями в C машину будет на самом деле добавить к указателю число байтов вашего типа данных. Например, если вы объявить:

int num = 5; 
char * point; 

тогда, когда вы

point = 8; 

результат точки + Num == 8 + 5 == 13, потому что в большинстве реализаций C полукокса является один байт.

+0

Указатели не обязательно должны быть целыми (хотя обычно это так) –

+0

Во всех реализациях char - это один байт –

+0

Думая о них как о целых, помогает мне рассуждать о них. В конце концов, они просто цифры. Я полагаю, что одна из причин, почему вы говорите это, состоит в том, что адреса памяти обычно начинаются с 0 (однако я знаю машины с адресными адресными пространствами). –

3

Указатель - это номер, как и номер int. Назначение жесткого закодированное значение указателя является законным, но выдается предупреждение

point = 8; 

но бессмысленно, если вы не работаете со встроенной системой и знать, что по этому адресу 8.

Хотя ваша программа выжила память ограничения доступа, которые вы не знаете, какое значение было в адресе памяти 8. Таким образом, вы не поймете результат вычисления. Это неопределенное поведение, так как программа не установила значение по этому адресу.

+0

'point = 8;' является незаконным, требуется литье –

+0

@MattMcNabb добавлено примечание при вводе. –

+0

так, чтобы присвоить значение указателю, как я, следует избегать, правильно? – ufosecret

2

1 - почему даже можно назначить нормальное значение (8) указателю (точке)? не указатели должны хранить только адреса других вещей? что происходит в строке 8?

Вы правы, что указатель хранит адрес памяти, но этот адрес памяти записывается просто числом. Как правило, значения указателей намного больше, но ничто не мешает вам назначить указатель на адрес памяти (8). Не ожидайте использовать этот адрес. Один из способов, который обычно используется, - NULL pointers.

int *point = NULL; 

такое же, как

int *point = 0; 

2- я составил код и для второго Printf он показывает: сумма (28) = Num (5) + точка (8), почему сумма равна до 28? 5 + 8 - 13. Что происходит?

Арифметика указателей не работает так, как вы можете ожидать изначально, а также хранящий ячейку памяти, указатель имеет тип памяти:

int *point; 

в этом случае point знает, что он указывает до int. Поэтому, когда указатель в инкрементах не увеличивает его позицию на один байт, он увеличивается на sizeof(int), который в вашем случае равен 4 байтам. Это означает, что point++ или point = point + 1 будет перемещать указатель вдоль любого размера, на что он думает, что он сейчас указывает на это, поэтому для этого легко перемещаться по массиву или блоку памяти объектов того же типа.

Чтобы получить голову вокруг этой концепции пытаются возиться с этим куском кода

#include <stdio.h> 

int main() 
{ 
    int x; 
    int * ptr = &x; 

    for(int i = 0; i < 5; i++) 
    { 
     printf("pointer address: %p\n", ptr + i); 
    } 
return 0; 
} 

В частности, попробуйте изменить тип x и ptr от int к char или long long и увидеть разницу он делает!

+0

Очень хорошо объяснено. Благодарю. – ufosecret

Смежные вопросы