왜 대부분의 델파이 예제가 fillchar ()를 사용하여 레코드를 초기화합니까?

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

  •  13-09-2019
  •  | 
  •  

문제

방금 대부분의 델파이 예제가 왜 fillchar ()를 사용하여 레코드를 초기화하는 이유를 궁금해했습니다.

type
  TFoo = record
    i: Integer;
    s: string; // not safe in record, better use PChar instead
  end;

const
  EmptyFoo: TFoo = (i: 0; s: '');

procedure Test;
var
  Foo: TFoo;
  s2: string;
begin
  Foo := EmptyFoo; // initialize a record

  // Danger code starts
  FillChar(Foo, SizeOf(Foo), #0);
  s2 := Copy("Leak Test", 1, MaxInt); // The refcount of the string buffer = 1
  Foo.s = s2; // The refcount of s2 = 2
  FillChar(Foo, SizeOf(Foo), #0); // The refcount is expected to be 1, but it is still 2
end;
// After exiting the procedure, the string buffer still has 1 reference. This string buffer is regarded as a memory leak.

여기 (http://stanleyxu2005.blogspot.com/2008/01/potential-memory-leak-by-initializing.html)은이 주제에 대한 나의 메모입니다. IMO, 기본값으로 상수를 선언하는 것이 더 나은 방법입니다.

도움이 되었습니까?

해결책

역사적 이유, 대부분. Fillchar ()는 터보 파스칼 시대로 거슬러 올라가며 그러한 목적으로 사용되었습니다. 이름은 실제로 약간의 잘못된 이름입니다.(), 그것은 정말로 채워집니다바이트(). 그 이유는 마지막 매개 변수가 숯을 잡을 수 있기 때문입니다. 또는 바이트. 따라서 Fillchar (foo, sizeof (foo), #0) 및 Fillchar (foo, sizeof (foo), 0)는 동일합니다. 혼란의 또 다른 원인은 Delphi 2009의 현재 Chillchar가 WideChar와 동일하더라도 여전히 바이트를 채우는 것입니다. 대부분의 사람들이 Fillchar를 사용하여 실제로 메모리를 캐릭터 데이터로 채우거나 주어진 바이트 값으로 메모리를 초기화하는 데 사용하는지 여부를 결정하기 위해 Fillchar의 가장 일반적인 용도를 살펴 보는 동안, 우리는 후자의 경우 사용을 지배하는 것이라는 것을 알았습니다. 전자보다는. 그와 함께 우리는 Fillchar 바이트 중심을 유지하기로 결정했습니다.

"관리 된"유형 (문자열, 변형, 인터페이스, 동적 배열) 중 하나를 사용하여 선언 된 필드를 포함하는 Fillchar로 레코드를 지우는 것은 적절한 컨텍스트에서 사용하지 않으면 안전하지 않을 수 있습니다. 그러나 당신이 준 예에서는 실제로 로컬로 선언 된 레코드 변수에서 Fillchar를 호출하는 것이 안전합니다. 그 범위 내에서 레코드에 가장 먼저하는 일이라면. 그 이유는 컴파일러가 레코드에서 문자열 필드를 초기화하기 위해 코드를 생성했기 때문입니다. 이것은 이미 문자열 필드를 0 (nil)으로 설정했습니다. 호출 FOO, FOO, SIZEOF (FOO), 0)는 이미 0 인 스트링 필드를 포함하여 전체 레코드를 0 바이트로 덮어 씁니다. ~ 후에 문자열 필드에 값이 할당되었으며 권장되지 않습니다. 초기화 된 상수 기술을 사용하면 컴파일러가 할당 중에 기존 레코드 값이 올바르게 완료되도록 적절한 코드를 생성 할 수 있기 때문에이 문제는 매우 좋은 솔루션입니다.

다른 팁

Delphi 2009 이상이있는 경우 사용하십시오. Default 레코드를 초기화하려면 전화하십시오.

Foo := Default(TFoo); 

보다 다윗의 대답 질문에 델파이에 한 번에 다양한 유형을 포함하는 레코드를 제대로 무료로 제공하는 방법은 무엇입니까?.

편집하다:

사용의 장점 Default(TSomeType) 전화, 레코드가 지워지기 전에 마무리된다는 것입니다. 메모리 누출도없고 충전재 또는 0에 대한 명시 적 위험한 낮은 수준 호출이 없습니다. 레코드가 복잡하고 아마도 중첩 기록 등을 포함하는 경우 실수를 저지르는 위험이 제거됩니다.

레코드를 초기화하는 방법은 더 간단하게 만들 수 있습니다.

const EmptyFoo : TFoo = ();
...
Foo := EmptyFoo; // Initialize Foo

때로는 매개 변수가 비 기본 값을 갖기를 원한 다음 다음과 같습니다.

const PresetFoo : TFoo = (s : 'Non-Default'); // Only s has a non-default value

이렇게하면 약간의 타이핑이 절약되며 중요한 사항에 중점을 둡니다.

Fillchar는 쓰레기를 새롭고 초기화되지 않은 구조 (레코드, 버퍼, Arrray ...).
재설정이 무엇인지 모르고 값을 "재설정"하는 데 사용해서는 안됩니다.
글을 쓰는 것 이상이 아닙니다 MyObject := nil 메모리 누출을 피할 것으로 예상됩니다.
Decululart에서는 모든 관리 유형을주의 깊게 시청해야합니다.
참조 마무리하십시오 기능.

메모리와 직접 바이올링을 할 힘이 있으면 항상 발에 자신을 쏘는 방법이 있습니다.

충전국 일반적으로 채우는 데 사용됩니다 배열 또는 기록 숫자 유형과 배열만으로. 당신은 그것이있을 때 익숙해 져서는 안된다는 것이 맞습니다. 문자열 (또는 참고 문헌 변수) 기록.

사용하겠다는 제안이지만 Const 초기화하기 위해 작동하면 문제가 발생하면 문제가 발생합니다. 가변 길이 정렬 초기화하고 싶다.

질문은 또한 다음과 같이 묻습니다.

  • 충전국
  • 그리고 아닙니다 0 회의?

아니요 0 회의 Windows에서 기능. 헤더 파일에서winbase.h) C 세계에서 돌아 서서 Memset을 부르는 매크로입니다.

memset(Destination, 0, Length);

0은 언어 중립 용어입니다 "메모리 제로에 사용할 수있는 플랫폼의 기능"

델파이에 해당합니다 memset ~이다 FillChar.

Delphi는 매크로가없고 (인라인 시절 전) 0 회의 실제로 가기 전에 추가 기능 호출의 벌금을 겪어야했다는 것을 의미했습니다. 충전국.

그래서 여러 가지면에서 전화를 걸었습니다 충전국 성능 미세 최적화입니다. 이제 더 이상 존재하지 않습니다. 0 회의 감소된다 :

procedure ZeroMemory(Destination: Pointer; Length: NativeUInt); inline;

보너스 읽기

Windows도 포함합니다 Securezeromemory 기능. 그것은 똑같은 일을합니다 0 회의. 그것이 같은 일이 있다면 0 회의, 왜 존재합니까?

일부 스마트 C/C ++ 컴파일러가 해당 메모리 설정을 0 메모리를 제거하기 전에 시간 낭비입니다. 0 회의.

Delphi의 컴파일러가 다른 많은 컴파일러만큼 똑똑하다고 생각하지 않습니다. 따라서 필요하지 않습니다 SecureFillchar.

전통적으로 캐릭터는 단일 바이트 (Delphi 2009의 경우 더 이상 사실이 아님)이므로 Fillchar를 #0으로 사용하면 메모리가 할당 된 메모리를 비현실화하여 NULLS 또는 BYTE 0 또는 BIN 00000000 만 포함합니다.

대신 사용해야합니다 0 회의 이전 필 차량과 동일한 호출 매개 변수를 갖는 호환성 기능.

이 질문은 오랫동안 내 마음에 있었던 더 넓은 의미를 가지고 있습니다. 나도 기록을 위해 Fillchar를 사용하여 자랐습니다. 이것은 종종 (데이터) 레코드에 새로운 필드를 추가하고 물론 Fillchar (Rec, Sizeof (Rec), #0)가 그러한 새로운 필드를 처리하기 때문에 좋습니다. 우리가 '올바르게 수행'하면 레코드의 모든 필드를 반복해야하며, 그 중 일부는 열거 된 유형이며, 그 중 일부는 레코드 자체 일 수 있으며 결과 코드는 덜 읽기 쉬우 며 새로 추가하지 않으면 잘못 될 수 있습니다. 부지런히 필드를 기록합니다. 문자열 필드는 일반적이므로 FillChar는 지금이 아닙니다. 몇 달 전, 나는 문자열 필드가있는 레코드에서 모든 필 차원을 반복적 인 청소로 전환했지만 솔루션에 만족하지 않았으며 단순히 유형 (Ordinal /에서 '채우기'를 수행하는 깔끔한 방법이 있는지 궁금해했습니다. float) 및 변형과 문자열에서 '최종'?

다음은 Fillchar를 사용하지 않고 물건을 초기화하는 더 좋은 방법입니다.

기록에 기록 (초기화 할 수 없음)
정적 배열을 초기화하는 방법은 무엇입니까?

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