#c #pointers
#c #указатели
Вопрос:
Я думал, что понял основные концепции указателей в соответствии с учебными пособиями по C, но я действительно запутываюсь при написании кода. У меня есть несколько вопросов:
1 — Допустим, у меня есть:
customList arrayOfLists[3]; //global
//...
void someMethod()
{
int i;
for (i=0; i<3; i )
{
customList l = arrayOfLists[i];
//METHOD1
//Next time I come back to this function, element is still there!
removeFirstElement(amp;l);
//METHOD2
//Next time I come back to thsi function, element is gone!
//removeFirstElement(amp;(arrayOfLists[i]));
}
}
Почему оба метода Method1 и Method2 не будут работать одинаково? В обоих случаях я в основном говорю удалить первый элемент списка, расположенный по адресу addressX. Создает ли это копию массива?
2 — В чем разница между:
struct1.ptr1->ptr2->someIntValue //1
amp;(struct1).ptr1->ptr2->someIntValue //2
Второй способ для меня не имеет смысла, и я действительно не знаю, что там происходит, но, похоже, он работает. Я ожидал, что сработает первый способ, но он не дает мне правильного ответа.
3 — Допустим, я делаю это:
ptr2 = ptrToStruct;
ptr1 = ptr2;
ptr2->intProp = 5;
ptr2 = ptrToStruct2;
Указывает ли ptr1 на начальную ячейку памяти по-прежнему, или это тот же ptr2? Что такое ptr1-> intProp?
Спасибо.
Комментарии:
1.
customList
это typedef, который скрывает свою заостренность, верно? Что-то вродеtypedef struct mystruct *customList
? ??2. Возможно, вам захочется прочитать c-faq . Особенно раздел 6.
3. да, это верно, это typedef
4. Некоторым людям (включая меня) нравится всегда видеть
*
при работе с указателями. Скрывая остроту, вы облегчаете неправильное смешивание типов в некоторых ситуациях.
Ответ №1:
Указатели трудно понять, потому что они абстрагируют память компьютера без ссылки на эту память в описании операции, которую вы можете выполнить с указателями.
Понять указатели намного проще, если вы немного разбираетесь в ассемблере.
Ответ №2:
customList l = arrayOfLists[i];
//METHOD1
//Next time I come back to this function, element is still there!
removeFirstElement(amp;l);
Это неправильно: вам нужно что-то вроде:
customList *l = amp;arrayOfLists[i];
removeFirstElement(l);
Что касается второго вопроса:
struct1.ptr1->ptr2->someIntValue // this will give you someIntValue
amp;(struct1).ptr1->ptr2->someIntValue // this will give you the address of memory where someIntValue is stored
Ответ №3:
Вопрос 3)
ptr2 = ptrToStruct;
ptr1 = ptr2;
ptr2->intProp = 5;
ptr2 = ptrToStruct2;
Указывает ли ptr1 на начальную ячейку памяти по-прежнему, или это тот же ptr2?
Да, ptr1
указывает на ячейку памяти, на которую ptrToStruct
указывает. Изменение ptr2
указания на другое местоположение ( ptrToStruct2
) не влияет ptr1
.
Что такое ptr1-> intProp?
5.
Комментарии:
1. @C-Noob — Проверьте этот пример ideone.com/5GVlP . Кроме того, подумайте об ответе @pmg . Это описание еще проще.
Ответ №4:
-
customList
является локальным указателем;arrayOfLists
является глобальным массивом. Они находятся в разных областях памяти. -
amp;
Имеет более низкий приоритет, чем.
или->
. Если бы я упростил 1-е выражение доj
, 2-е выражение было бы упрощено какamp;j
. -
Указатели точно такие же, как и любой другой тип. В приведенном ниже коде
int j, k; k = 5; j = k; k = 12;
у j их 5 или 12?
Комментарии:
1. Хорошо, для # 2, допустим, я хочу прочитать или обновить someIntValue (изменить фактическое целочисленное значение), я должен использовать 1-й метод? К сожалению, это 2-й, который дает мне правильное значение int, так что, возможно, что-то еще не так в моей программе.
2. Нам понадобилось бы объявление ваших структур и определение объектов, чтобы понять, что происходит. Я написал небольшой пример, который «работает» без
address-of
оператора: ideone.com/IyKce смотрите строки 23 и 24
Ответ №5:
Что касается вопроса 1, метод 1 принимает адрес переменной l
. l
инициализируется для хранения копии значения i
‘-го элемента массива. Это совершенно другое место в памяти. Обратите внимание, что если removeFirstElement()
функция зависит от того, что аргумент the является указателем на массив, она с треском провалится при использовании метода 1.
Что касается вопроса 2, строка 2 допустима, но не по той причине, о которой вы, вероятно, думаете. Что он оценивает, так это получение адреса struct1.ptr1->ptr2->someIntValue
. Обратите внимание, что вы взяли не адрес структуры, а член member элемента, на который она указывала. Вы, вероятно, имели в виду, что это должно быть как:
(amp;struct1).ptr1->ptr2->someIntValue
Это привело бы к плачевному завершению.
Оператор доступа к элементу ( .
) используется для значений структуры для доступа к элементу. То, что у вас есть в LHS, является указателем на структуру и должно использовать оператор arrow ( ->
) для доступа к элементу. Или сначала разыменуйте указатель, затем используйте точку (чему эквивалентен оператор arrow).
(*(amp;struct1)).ptr1
/* is equivalent to: */
(amp;struct1)->ptr1
Что касается вопроса 3, предполагая, что ptr1
и ptr2
являются указателями на любую структуру, на которую ptrToStruct
указывает, тогда да, ptr1
все еще указывает на то, на что она указывала. Вы никогда не изменяли ptr1
переменную, только структуру, на которую она указывала. Поскольку оба ptr1
и ptr2
указывают на одну и ту же структуру (другими словами, с псевдонимами), вы должны видеть одно и то же значение при доступе к элементу ptr1->intProp
.