내 C ++ 코드가 읽기 (…) 함수를 사용한 후 세분화 오류를 일으키는 이유는 무엇입니까?

StackOverflow https://stackoverflow.com/questions/677444

문제

내 응용 프로그램은 아무 문제가없는 것으로 보이는 코드 라인에 중단되지만 내 IDE는 오류로 해당 라인에 중단되는 것으로 보입니다.

GDB/MI (24/03/09 13:36) (종료. 신호 'SIGSEGV'수신. 설명 : 세분화 오류.)

코드 줄은 단순히 코드가없는 메소드를 호출합니다. Null 참조가있을 때 분할 결함이 아닌가? 그렇다면 빈 메소드는 어떻게 널 참조를 가질 수 있습니까?

이 코드는 문제를 일으키는 것 같습니다.

#include <sys/socket.h>

#define BUFFER_SIZE 256

char *buffer;

buffer = (char*)GetSomePointer()->SomeStackMemoryString.c_str();
int writeResult = write(socketFD, buffer, BUFFER_SIZE);

bzero(buffer, BUFFER_SIZE);
int readResult = read(socketFD, buffer, BUFFER_SIZE);

라인이 read(...) 메소드가 주석을 달고 문제가 사라집니다.

업데이트:

실제 문제를 지적하기 위해 질문을 변경했으며 모든 관련이없는 코드를 제거했습니다. 내 자신의 질문에 대답했습니다 이 글을 읽는 사람들이 문제가 무엇인지 구체적으로 알 수 있도록 "당신은 바보입니다!"라고 말하기 전에 내 대답을 읽으십시오.

도움이 되었습니까?

해결책

코드는 가짜입니다. 버퍼는 임의의 메모리를 가리 킵니다. Bzero와의 라인이 왜 실패하지 않는지 잘 모르겠습니다.

올바른 코드는 다음과 같습니다.

   char buffer[BUFFER_SIZE];

   bzero(buffer, BUFFER_SIZE);
   int readResult = read(socketFD, buffer, BUFFER_SIZE);

또는 Calloc (1, buffer_size)을 사용하여 메모리를 할당하고 제로화 할 수 있습니다.

다른 팁

먼저, 널 포인터 또는 참조를 통해 메소드를 호출하는 것은 엄격하게 정의되지 않은 동작입니다. 그러나 통화가 가상이 아니라면 성공할 수 있습니다.

가상 메소드 호출 사실상 (클래스 :: method on won won way of encation을 통해 파생 클래스가 아닌 포인터/참조를 통해) 가상/포인터가 vtable에 액세스하고 null을 통해 vtable에 액세스하기 때문에 항상 실패합니다. 포인터/참조는 불가능합니다. 따라서 참조/포인터를 통해 빈 가상 메소드를 호출 할 수 없습니다.

이를 이해하려면 코드 구성 방법에 대해 더 알아야합니다. 모든 비 인식 방법에 대해서는 메소드를 구현하는 기계 코드를 포함하는 코드 세그먼트 섹션이 있습니다.

호출이 비 면도 적으로 수행되면 (기준/포인터를 통해 파생 클래스 또는 비 약독 메소드에서) 컴파일러는 정확히 어떤 메소드 (다형성 없음)를 알고 있습니다. 그래서 그것은 단지 코드의 정확한 부분에 호출을 삽입하고 통과합니다. 이것 첫 번째 매개 변수로서 포인터. 널 포인터를 통한 호출의 경우 이것 또한 null이지만 방법이 비어 있는지 신경 쓰지 않습니다.

통화가 사실상 (참조/포인터를 통해) 완료되면 컴파일러는 어떤 정확한 호출을 호출 해야하는지 알지 못하면 가상 메소드 테이블이 있고 테이블 주소가 객체에 저장된다는 것을 알고 있습니다. 호출 할 방법을 찾으려면 먼저 포인터/참조를 연도하여 테이블에 도착하고 메소드 주소를 가져 와서 메소드를 호출하십시오. 테이블 읽기는 컴파일 중에가 아니라 런타임에서 수행됩니다. 포인터/참조가 NULL 인 경우이 시점에서 분할 오류가 발생합니다.

이것은 또한 가상 호출을 감소시킬 수없는 이유를 설명합니다. 컴파일러는 컴파일 중에 소스를 볼 때 어떤 코드를 인라인으로 내릴지 모르겠습니다.

코드가 없으면 내가 할 수있는 최선은 거친 추측입니다. 하지만 여기가 간다 :

"장기 실행 코드"는 유효하지 않은 포인터에 쓰고 있습니다. (완전히 임의의 포인터이거나 버퍼 또는 어레이의 시작/시작을지나갑니다). 이것은 객체에 대한 가상 함수 테이블을 깎아 내고 있습니다. 객체에 대한 포인터 또는 객체의 VPTR 멤버를 덮어 쓰거나 해당 클래스의 실제 글로벌 가상 함수 테이블을 덮어 쓰고 있습니다.

시도해 볼 몇 가지 사항 :

  • 수업에 Sentinel 회원을 넣으십시오. 예를 들어 생성자에서 알려진 패턴 (0xdeadbeef 또는 0xcafebabe)으로 초기화 된 int는 변경되지 않습니다. 가상 함수 호출을하기 전에 여전히 올바른 값을 가지고 있는지 확인하십시오 (assert ()).
  • 메모리 디버거를 사용해보십시오. Linux에는 옵션에는 전기 울타리 (Efence) 또는 Valgrind가 포함됩니다.
  • 디버거 아래에서 프로그램을 실행하고 (GDB는 괜찮습니다), Segfault가 발생한 후 사후 사후 사후 또는 Segfault로가는 장소 직전에 중단 점을 설정하여 무엇이 잘못되었는지 확인하십시오.

나는 그 자체로 빈 방법이 그러한 문제를 일으킬 이유를 생각할 수 없습니다. 다른 맥락이 없다면, 나의 첫 번째는 다른 곳에서 문제가 기억을 손상시키는 것입니다. 그리고 여기에서 이런 식으로 나타나는 것입니다.

우리는 전에 그런 종류의 문제가 있었고, 나는이 답변에 그것에 대해 썼습니다. 여기. 같은 질문에는 도움이 될 수있는 다른 많은 좋은 조언도 있습니다.

Null 참조가있을 때 분할 결함이 아닌가?

아마도 반드시 그런 것은 아닙니다. Segfault의 원인은 다소 플랫폼 별이지만 기본적으로 프로그램이 메모리에 액세스하지 않아야한다는 것을 의미합니다. 당신은 그것을 읽고 싶을 수도 있습니다 위키 백과 기사 그것이 무엇인지 더 잘 알고 있습니다.

확인할 수있는 한 가지, 빈 메소드에 리턴 유형이 있습니까? 이것에 대해 틀릴 수 있지만 객체를 반환하면 메소드가 실제로 객체를 반환하지 않으면 사본 생성자가 쓰레기로 호출되는 방법을 알 수 있습니다. 이것은 모든 종류의 기발한 행동을 유발할 수 있습니다.

반환 유형을 void로 변경하거나 값을 반환하는 경우 동일한 결과를 얻습니까?

문제는 그 때문입니다 buffer 변수는 할당되지 않은 메모리를 사용하여 read(...) 함수는 데이터를 넣습니다 buffer.

일반적으로 Bzero는 실제로 분할 오류를 일으키지 만 문자열이 메모리 위치에 할당되기 때문에 읽기 기능은 할당 된 메모리를 지나서 (누출을 일으키는) 글을 쓸 수있었습니다.

/* this causes *some* memory to be allocated, 
 * tricking bzero(...) to not SIGSEGV */
buffer = (char*)GetSomePointer()->SomeStackMemoryString.c_str();

int writeResult = write(socketFD, buffer, BUFFER_SIZE);

이 변경 사항은 메모리 누출을 해결합니다.

#define BUFFER_SIZE 256

// Use memory on the stack, for auto allocation and release.
char buffer[BUFFER_SIZE];

// Don't write to the buffer, just pass in the chars on their own.
string writeString = GetSomePointer()->SomeStackMemoryString;
int writeResult = write(socketFD, writeString.c_str(), writeString.length());

// It's now safe to use the buffer, as stack memory is used.
bzero(buffer, BUFFER_SIZE);
int readResult = read(socketFD, buffer, BUFFER_SIZE);

기본 클래스의 생성자에서 가상 메소드를 호출하고 있습니까? 문제가 될 수 있습니다 : 수업에서 순수한 가상 방법을 부르는 경우 Base 안에 Base의 생성자는 실제로 클래스에서만 정의됩니다. Derived, 당신은 아직 설정되지 않은 vtable 레코드에 액세스 할 수 있습니다. Derived그 시점에서 생성자가 실행되지 않았습니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top