Странный побочный эффект при использовании оператора ‘->’ в c

#c #pointers #linked-list #side-effects

#c #указатели #связанный список #побочные эффекты

Вопрос:

Я получаю этот странный побочный эффект при использовании operator ‘->’ в коде, который я написал на C. Указатель, который я использовал -> on , изменен, чтобы содержать некоторый мусор.

Более конкретно:

У меня есть следующие структуры:

 typedef void* ListElement ;

typedef  struct List_t* List ;

typedef struct Node_t* Node;

Struct Node_t {
  ListElement data ;
  Node next;
}

Struct List_t {
  Node* head;
  Node* current
}
  

когда я использую следующее ListGetFirst() , я получаю проводное поведение :

 ListElement ListGetFirst(List list)
{
  if( list == NULL || list->head==NULL)
  {
    return NULL;
  }
  list->current=list->head;
  Node* head =list->head; // here is the problem
  ListElement data = (*head)->data;
  return data;
}
  

когда я использовал debugger, я выяснил, что список указателей-> head изменен в отмеченной строке, упомянутой выше.

Я действительно понятия не имею, почему, и я не знал, что ‘->’ может иметь побочный эффект

заранее спасибо

Комментарии:

1. Скорее всего, вы неправильно интерпретируете происходящее.

2. Нет, -> не имеет побочных эффектов в C. Почему бы просто не сделать return list->head->data ?

3. Это ваш код, а не «побочный эффект» -> . В этом можно не сомневаться.

4. @meagar — если это мой код, пожалуйста, помогите мне найти ответ, я в отчаянии

5. Вы просто не должны использовать typedefs, которые скрывают тот факт, что типы являются указателями. Нас всех это смущает!

Ответ №1:

Вы уверены, что это именно то, что вы хотите сделать?

 typedef struct Node_t* Node;

Node* head =list->head; 
  

Поскольку вы определили Node как указатель на Node_t, разве вы не должны делать:

 Node head =list->head; 
  

Редактировать:

Подводя итог всему этому, я думаю, что этот typedef вводит вас в заблуждение:

 typedef struct Node_t* Node;
  

Это имело бы больше смысла, если бы это было просто:

 typedef struct Node_t Node;
  

Комментарии:

1. @karlphillip — посмотри на структуру . Я решил иметь в структуре list_t переменные Node* , а не Node.

2. Я думаю, что у Карла есть это; вы используете Node *, который является адресом указателя, а не значением указателей, которое было бы адресом списка.

3.@RanZilber То, что вы делаете в своем коде, эквивалентно Node_t** head = list->head; и вам явно нужно сделать Node_t* head = list->head; , что может быть достигнуто с помощью Node head = list->head;

4. @karlphillip — но list-> head имеет тип Node * , или Node_t ** , вот как я это определил.

5. Да! Используйте Node head =list->head; вместо этого. Код поврежден. Если у вас это получилось таким образом, и вы хотите, чтобы это работало, вам придется это исправить.

Ответ №2:

Блин, указатели, скрытые за typedefs; если тип не должен быть полностью непрозрачным, это почти всегда плохой juju. Для моей пользы я собираюсь удалить typedefs, чтобы мне было легче видеть, с чем вы на самом деле играете.

 struct Node_t {   
  void *data ;   
  struct Node_t *next; 
};

struct List_t {   
  struct Node_t **head;   
  struct Node_t **current; 
};

void *ListGetFirst(struct List_t *list)
{
  if( list == NULL || list->head==NULL
  {        
     return NULL;
  }
  list->current=list->head;             
  struct Node_t **head =list->head; // here is the problem
  void *data = (*head)->data;
  return data;           
}           
  

Я ничего не получил. Кажется, что все типы совпадают. -> Оператор, что наиболее важно, не имеет никаких побочных эффектов; все, что он делает, это разыменовывает указатель. Дополнительный уровень косвенности для head и current в struct List_t вызывает недоумение, и это заставляет меня задуматься, правильно ли они распределяются. Все, что я могу понять, это то, что list->head указывает не на память, которой вы на самом деле владеете, и каким-то образом перезаписывается, когда вы достигаете этой точки (IOW, вы вызвали неопределенное поведение где-то еще в вашем коде).

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

Комментарии:

1. Спасибо, это ответ, который я искал

Ответ №3:

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

В List_t вы определяете head как Node*, где Node уже является Node_t * .

hth

Марио

Комментарии:

1. Разве это не то, что я опубликовал в своем ответе 4 минуты назад? = P

2. @Mario The Spoon — Меня попросили создать список таким образом. Я знаю, что это странно, и лучшим решением было бы построить его всего с одним указателем — но я ограничен этим решением. Почему это должно быть проблемой?

3. @karlphillip: grrrrrrrrrrr 😉

4. @RanZilber: какое требование заставило вас это сделать? Это просто не имеет никакого смысла…

5. @Mario, они просили тебя создать двусвязный список ?)