연결된 목록에서 항목을 제거하는 올바른 방법


10

Slashdot 인터뷰에서 Linus Torvalds는 다음과 같이 인용됩니다.

"이전"항목을 추적하여 단일 링크 목록 항목을 삭제 한 다음 항목을 삭제하여 다음과 같은 작업을 수행하는 사람이 너무 많습니다.

if (prev)
  prev-> next = entry-> next;
else
  list_head = 항목-> 다음;

그런 코드를 볼 때마다 "이 사람은 포인터를 이해하지 못합니다"로 이동합니다. 슬프게도 매우 흔합니다.

포인터를 이해하는 사람들은 "항목 포인터에 대한 포인터"를 사용하고 list_head의 주소로 초기화합니다. 그런 다음 목록을 탐색 할 때 "* pp = entry-> next"를 수행하여 조건을 사용하지 않고 항목을 제거 할 수 있습니다.

PHP 개발자로서 저는 10 년 전 대학 소개 C 이후 포인터를 다루지 않았습니다 . 그러나 나는 이것이 내가 익숙해야 할 상황 유형이라고 생각합니다. 리누스는 무엇에 대해 이야기하고 있습니까? 솔직히 말하면, 링크 된 목록을 구현하고 항목을 제거하라는 요청을 받으면 위의 '잘못된'방법은 내가 갈 길입니다. Linus가 가장 잘 말한 것처럼 코딩하려면 무엇을 알아야합니까?

실제로 프로덕션 코드 에서이 문제가 없으므로 Stack Overflow 대신 여기에 묻습니다.


1
그가 말한 것은 prev전체 노드를 저장하는 대신의 위치를 저장해야 할 때의 위치 만 저장할 prev.next수 있다는 것입니다. 관심있는 유일한 항목이므로 포인터에 대한 포인터입니다. 그리고 그렇게하면 어리석은 것을 피할 수 있습니다. if이제 list_head노드 외부에서 포인터가 어색한 경우가 없기 때문에 . 리스트의 헤드에 대한 포인터는 의미 적으로 다음 노드에 대한 포인터와 동일합니다.
Ordous

@Ordous : 감사합니다. 왜 코멘트? 그것은 간결하고 명확하며 조명적인 답변입니다.
dotancohen

@Ordous 해당 코드 스 니펫과 관련된 모든 것은 포인터이므로 포인터를 저장하는 것과 전체 노드를 저장하는 것과 관련이 없습니다.
Doval

답변:


9

내 L331 MS Paint 기술 사용 :

여기에 이미지 설명을 입력하십시오

원래 솔루션은을 통해 노드를 가리키는 것 curr입니다. 이 경우 다음 노드 다음 curr에 삭제 값이 있는지 확인 하고 curr노드 next포인터를 재설정하십시오 . 문제는 목록의 헤드를 가리키는 노드가 없다는 것입니다. 그것은 그것을 확인하기 위해 특별한 경우가 있어야 함을 의미합니다.

Linus가 제안하는 것은 현재 검사 된 노드에 대한 포인터를 저장하는 것이 아니라 현재 노드에 대한 포인터를 가리키는 것입니다 (이라고 표시 pp). 작업이 동일합니다. pp포인터가 올바른 값을 가진 노드를 가리키는 경우 포인터를 재설정합니다 pp.

차이점은 목록의 맨 처음에옵니다. 목록의 헤드를 가리키는 노드는 없지만 실제로는 목록의 헤드를 가리키는 포인터가 있습니다. 그리고 이것은 다른 노드 next포인터 와 마찬가지로 노드에 대한 포인터입니다. 따라서 목록의 시작을위한 특별한 조항이 필요하지 않습니다.


아 나는 지금 본다.… 매일 새로운 것을 배운다.
Lawrence Aiello

1
나는 당신이 제대로 일을 설명 생각하지만, 나는 적절한 솔루션을 갖도록 제안 list_head뭔가를 가리킨 next최초의 실제 데이터 항목 점 (그리고 것을 노드 prev같은 더미 객체로 초기화). prev이러한 트릭이 앨리어싱을 통해 정의되지 않은 동작을 도입하고 코드를 구조 레이아웃에 불필요하게 민감하게 만들 수 있기 때문에 다른 유형의 무언가 를 지적하는 것을 좋아하지 않습니다 .
supercat

@supercat 정확히 요점입니다. prev노드 를 가리키는 대신 포인터를 가리 킵니다. 항상 같은 유형의 노드, 즉 포인터를 가리 킵니다. 당신의 제안은 본질적으로 동일합니다- prev" next노드로" 무언가를 지적 하십시오. 쉘을 폐기하면 초기 list_head포인터 만 얻습니다 . 즉, 다음 노드에 대한 포인터를 가져야 만 정의되는 것은 의미 적으로 노드에 대한 포인터와 같습니다.
Ordous

@Ordous :이 있음을 전제로하지만 그 말이 list_head하고 next포인터 같은 "종류"개최한다. C의 문제는 아니지만 C ++의 문제 일 수 있습니다.
supercat

@supercat 저는 항상 언어에 관계없이 연결된 목록의 "정식"표현이라고 가정했습니다. 그러나 나는 그것이 C와 C ++ 사이에 실제로 차이를 만드는지 여부와 표준 구현이 무엇인지 판단하기에 능숙하지 않습니다.
Ordous

11

여기에 이미지 설명을 입력하십시오 여기에 이미지 설명을 입력하십시오 여기에 이미지 설명을 입력하십시오 여기에 이미지 설명을 입력하십시오 여기에 이미지 설명을 입력하십시오

코드 예

// ------------------------------------------------------------------
// Start by pointing to the head pointer.
// ------------------------------------------------------------------
//    (next_ptr)
//         |
//         v
// [head]----->[..]----->[..]----->[..]----->[to_remove]----->[....]
Node** next_ptr = &list->head;

// ------------------------------------------------------------------
// Search the list for the matching entry.
// After searching:
// ------------------------------------------------------------------
//                                  (next_ptr)
//                                       |
//                                       v
// [head]----->[..]----->[..]----->[..]----->[to_remove]----->[next]
while (*next_ptr != to_remove) // or (*next_ptr)->val != to_remove->val
{
    Node* next_node = *next_ptr
    next_ptr = &next_node->next;
}

// ------------------------------------------------------------------
// Dereference the next pointer and set it to the next node's next
// pointer.
// ------------------------------------------------------------------
//                                           (next_ptr)
//                                                |
//                                                v
// [head]----->[..]----->[..]----->[..]---------------------->[next]
*next_ptr = to_remove->next;

노드를 파괴하기위한 로직이 필요하다면 끝에 코드를 추가하면됩니다.

// Deallocate the node which is now stranded from the list.
free(to_remove);
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.