2016-09-05 2 views
1

Я просматриваю свои навыки C, и я изо всех сил пытаюсь понять некоторые бит. Вот мое понимание С и указателей.Понимать указатели в C

Каждый раз, когда я объявляю переменную, C сохраняет значение в памяти.

int num = 12; // The 12 is stored somewhere in memory, let's say has address 0x54 

Чтобы получить адрес памяти переменной «NUM» мы делаем:

printf("%p", &num); // this returns the 0x54 

, если я хочу, чтобы создать указатель, который указывает на то же значение «Num», я:

int *ptr = # // create a pointer and point him to 0x54 

Если я проверить как адрес:

После этого ... Я не понимаю выход моей программы. Я читаю это book, и автор говорит, что мы можем рассматривать указатели как массивы и наоборот (за исключением некоторых случаев [если я правильно понял]).

int ages[] = { 23, 43, 12, 89, 2 }; 

printf("1-%d\n", ages[0]); 
printf("1-%d\n", ages[1]); 

printf("2-%p\n", &ages[0]); 
printf("2-%p\n", ages); 
printf("2-%p\n", &ages); 

printf("3-%d\n", *(&ages[1])); 
printf("4-%p\n", *(&ages)); 
printf("5-%p\n", &ages[1]); 
printf("6-%p\n", &ages+1); 
printf("7-%d\n", *(*(&ages)+1)); 
printf("8-%ld\n", sizeof(1)); 

Выходные вопросы в комментариях:

1-23 // value of position 0, OK 
1-43 // value of position 1, OK 
2-0x7fff1fd500f0 // adress of the beginning of array, OK 
2-0x7fff1fd500f0 // same as above, OK 
2-0x7fff1fd500f0 // because ages is not a pointer, same as above, OK 
3-43 // get address of ages, advance 4 bytes and then give me the value that is in that address, OK 
4-0x7fff1fd500f0 // give me the address of ages, and then give the value that is in that address. The address I have in the print number 2, why doesn't return the value and show me the address? **NOT OK** 
5-0x7fff1fd500f4 // give me the address of the position 1 of the array, the address that contains the number 43, OK 
6-0x7fff1fd50104 // why I don't get the same number of the print 5? How I jump from f0 to 104? **NOT OK** 
7-43 // why I need pointer of pointer, **NOT OK** 
8-4 // this was just trying to understand print 6, OK 

Может кто-нибудь объяснить мне печатает 4, 6 и 7, и дайте мне знать, если я правильно думать о других печатных изданий.

+4

Если кто-то говорит вам, что массивы и указатели одинаковы, убегайте. C11 draft standard n1570: * 6.3 Конверсии 6.3.2.1 Lvalues, массивы и указатели функций 3 За исключением случаев, когда это операнд оператора sizeof, оператор _Alignof или оператор унарного оператора или является строковым литералом, используемым для инициализации array, выражение, которое имеет type '' array of type '', преобразуется в выражение с типом '' указателем на тип '', которое указывает на начальный элемент объекта массива и не является значением lvalue. Если объект массива имеет класс хранения , поведение не определено. * – EOF

+0

Если вы понимаете 'printf (" 2-% p \ n ", &ages);', то я не совсем понимаю, что путает насчет остальных. –

+0

Просто Нет. Когда-то переменные массива/структуры рассматривались как указатели и наоборот. Это привело к множеству проблем. Смущение, которое у вас есть, - это то, что было у большинства других людей в то время и было причиной этих проблем. – Ouroborus

ответ

2

Ваши путаницы все сводятся к двум вещам: как работает приоритет оператора и что означает &ages.

Давайте посмотрим на первое первое. Очевидно, &ages является указателем. Но что это за указатель до? Это не указатель на int. Вместо этого это указатель на тип int[5].

Итак, давайте посмотрим на это:

printf("4-%p\n", *(&ages)); 

Если у вас есть указатель на int[5], и вы используете * на него, вы получите то, что он указывает: int[5]. Затем он переходит в указатель при переходе на printf. В частности, указатель на первый элемент массива.

Это:

printf("5-%p\n", &ages[1]); 

Является ли вопрос operator precedence. Используя явные скобки, это &(ages[1]); [] имеет более высокий приоритет, чем &. Ну, мы знаем, что ages[1] является вторым int в массиве. Используя &, он вернет указатель на второй элемент массива.

Подобным образом:

printf("6-%p\n", &ages+1); 

Оператор старшинство говорит нам, что это действительно (&ages) + 1. И помните, что такое &ages? Правильно, указатель на int[5].

Когда мы выполняем арифметику указателя, мы добавляем размер объекта, указывающего на адрес. Этот объект равен int[5], размер которого равен 20. Или в шестнадцатеричном формате 0x14. Таким образом, вы получаете адрес 0x14 байт от начала массива.

А:

printf("7-%d\n", *(*(&ages)+1)); 

Оператор старшинство говорит нам, что это выражение действительно *((*(&ages)) + 1). Итак, вы получаете указатель на int[5], превратите его обратно в int[5], а затем добавьте 1 к нему. Это требует разложения int[5] на int*, а затем с помощью арифметики указателя. Затем вы получаете доступ к значению по этому адресу.

-1

Для # 4 это, скорее всего, результат оптимизации компилятора. Принимая адрес, а затем разыменовывая результирующий указатель, фактически является no-op, поэтому оптимизатор, скорее всего, просто ускоряет эти операции, оставляя в качестве аргумента только ages, и это значение, которое вы видите.

Для # 6 вы находите адрес массива ages и добавляете к нему 1, что в арифметике указателя означает «добавить 1 * размер переменной, на которую указывает». 5-элементный массив, предполагающий 4-байтовые целые числа, имеет длину 20 байтов, и если вы проверите 0x7FFF1FD500F0 + 0x14, вы получите результат 0x7FFF1FD50104, который вы видите.

Массив не является указателем, точно. Он действует очень как один, но & (возрасты) не идентичен & (в возрасте [0]). Оба имеют одно и то же очевидное значение, но первое является указателем на 4-байтовое целое число, а второе является указателем на 20-байтовый массив из 4-байтных целых чисел. Вы должны помнить об этом при работе с указателями на элементы массива, не забывая брать адрес элемента, когда вы хотите работать в массиве, и, например, нужно, например. адрес элемента 0 массива, а не адрес самого массива.

+2

* «Для # 4 это, скорее всего, результат оптимизации компилятора». * Это неверно. –

0

Указатель - это не просто адрес, это адрес со связанным типом, который имеет размер.

Таким образом, когда вы увеличиваете указатель на тип массива, он пропускает весь массив.

Другая проблема в коде - это распад массива, где выражение, которое обозначает массив, такой как a или *&a, распадается на указатель на первый элемент массива.

Это неудачное неявное преобразование, которое имело смысл в момент раннего C.

В C++ это вызывает много проблем.

0

Массивы слишком эффективны, но накладные расходы массива - это размер, который нельзя изменить во время выполнения.

указатель является переменной некоторого типа, который имеет свой адрес и тип данных, он указывает, например:

//int* iPtr = 7; error pointers stores addresses not values 
int* iPtr = 0x00000000 //(NULL) points to this address (correct); 

выше указатель указывает на целое число инициализируется, чтобы указать на 0x00000000, который является NULL,

int a = 7; 
int* iPtr = &a; 
cout << "a: " << a << endl; // printing value of a; result is a: 7 
cout << "&a: " << &a << endl; // printing address of a: result is: &a: 0x0018FF44 
cout << "iPtr: " << iPtr << endl; // printing the address iPtr points to as you guess it is the address of a so the result: iPtr: 0x0018FF44 

cout << "*iPtr: " << *iPtr << endl; // printing the value stored in the address the iPtr points to so the result is as you guess: *iPtr: 7 

мощное использование указателей: они слишком гибкие, но опасные они могут указывать на массив, переменную, меняя адрес, размер во время выполнения ... они используются связанным списком, oop (polymorphism .. .)

*** Не путайте с указателями

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