기술면접 준비 - (1~10)

이준호·2024년 1월 26일
0

📌 기술면접 준비 (1 ~ 10)



➔ 1. float와 int의 표현 가능한 수의 범위가 다른 이유는 무엇인가요?

두 데이터 타입이 내부적으로 숫자를 표현하는 방식이 다르기 때문이다.

32비트를 예로 설명하자면,
정수(int)의 경우 "1비트는 부호는 나타내고, 나머지 31비트는 숫자"를 나타낸다.
부호 비트에 따라 양수와 음수의 범위를 고려하면 범위는 대략
-2,147,483,648 ~ 2,147,438,647 정도까지 표현이 가능하다.
범위는 비트 수에 따라서 변할 수 있고, 더 큰 비트를 사용하면 더 큰 범위를 나타낼 수가 있다.
예를 들면 부호가 있는 64비트 long (부호가 없이 양수만 표현하는 uint, ulong도 존재)

부동 소수점(float)의 경우 32비트의 고정된 크기를 가지며 "1비트는 부호를 나타내고, 8비트는 지수를, 나머지 23비트는 가수"를 각각 따로 저장하고 나타낸다.
범위는 대략 ±1.5 x 10−45 ~ ±3.4 x 1038 정도까지 표현이 가능하다.
이러한 구조로 소수점 이하 자리수가 적은 수를 빠르게 처리할 수 있지만, 가수 부분이 한정되어 있기 때문에 매우 큰 수를 표현하면 소수점 이하의 정밀도가 떨어질 수 있다.
이보다 큰 64비트 부동 소수점 double과 128비트 고정 소수점 decimal도 있다.












➔ 2. 'ref'와 'out'의 사용 시 차이는 무엇인가요?

'ref'와 'out'은 명시적으로 ref,out 키워드를 작성해줘야 하고, 변수를 참조 형태로 전달하는 공통점이 있습니다.

하지만 ref는 매개변수로 전달하기 전에 반드시 초기화가 되어야 하고
out은 매개변수로 전달되기 전에 초기화하지 않아도 되지만, 전달된 함수 내부에서 반드시 할당되어야 합니다.

즉, ref는 메서드 내에서 전달하는 변수를 변경할 때 용이하고 레퍼런스로 전달하기 때문에 직접접근하여 복사가 일어나는 것을 방지하고 성능 향상의 기대가 있습니다. (시간과 메모리의 절약 //매개변수가 큰수록)
out은 메서드 내에서 잔달하는 변수를 초기화 할 때 용이하고 코드의 가독성을 높이고 작성해야하는 코드의 양을 줄여줍니다. 또한 반드시 할당해야하기 때문에 실수를 방지해줍니다.












➔ 3. 접근제한자란 무엇이며, 각각 어떤 차이가 있는지 비교해서 설명해주세요.

접근제한자란 클래스, 필드, 생성자, 메소드에 접근할 수 있는 권한을 제한하는 키워드입니다.
접근제한자의 종류로는
private

  • 클래스 내부에서만 접근이 가능합니다.
    public
  • 모든 곳에서 접근이 가능합니다.
    protected
  • 클래스 외부에서는 접근할 수 없고, 클래스 내부 또는 상속받은 클래스에서만 접근이 가능합니다.
    internal
  • 동일한 어셈블리에서만 public으로 접근이 가능합니다.
    procted internal
  • 동일한 어셈블리에서만 protected로 접근이 가능합니다.
    private protected
  • 동일한 어셈블리의 상속받은 클래스에서만 접근이 가능합니다.

어셈블리(Assembly)는 .NET 프레임뭐크에서 코드를 배포하고 실행하는 단위입니다.
컴파일을 하여 나온 결과 파일로 ('.dll' or '.exe'이 컴파일이 되서 나온다면) 어셈블리 라고 부릅니다.
(Test.cs를 컴파일하여 만들어진 Test.exe 또한 어셈블리가 되고 어셈블리의 안에 있는 클래스들은 namespace를 통해서 구분된다.)












➔ 4. struct와 class를 비교해서 설명해주세요.

struct는 값(Value) 타입으로 스택 메모리에 할당됩니다. struct는 값 복사를 통해서 데이터를 전달하거나 수정하여 원본에 영향을 주지 않습니다.
class는 참조(Reference) 타입으로 힙 메모리 주소에 할당됩니다. class는 참조(메모리의 주소)를 통해 데이터를 전달하기에 같은 주소를 공유하는 다른 변수들도 같이 변하기에 '얕은 복사' 와 '깊은 복사'에 유의하여 알맞게 사용해야 합니다.
또한 class는 상속이 가능하지만 struct는 상속이 불가능합니다.












➔ 5. 가비지 컬렉터에 대해 설명해주세요. (@보충 필요)

스택 메모리의 경우는 자동으로 메모리를 자동으로 할당하고 해제하기에 따로 신경쓸 필요가 없지만(호출시 할당되고 완료되면 해제) 힙 메모리의 경우는 따로 해제를 해주지 않으면 메모리가 할당되어있는 상태가 유지됩니다. low level 언어의 경우는 메모리를 코드상으로 직접 관리해줘야 했지만 C#의 경우는 가비지 컬렉터(Garbage Collector)가 주기적으로 자동으로 사용하지 않는 메모리를 관리해줍니다.

1.가비지 컬렉션은 시스템에서 더 이상 사용하지 않는 동적 메모리 블럭을 찾아 자동으로 해제해 주는
메모리 관리 방법 입니다.
2.가비지 컬렉터(garbage collector)는 자동 메모리 관리 시스템으로
프로그램에 의해 할당된 메모리가 더이상 인스턴스를 참조하지 않아
가비지(garbage) 상태가 되면 자동으로 메모리를 초기화해주는 프로그램입니다.












➔ 6. 가비지 컬렉터를 회피하기 위한 전략은 무엇이 있나요? (@보충 필요)

Unity에서 명시적으로 가비지 컬렉터를 피하는것은 비효율적일 수도 있지만 보통 효과적으로 회피하는 전략이 몇가지 있습니다.

  1. 오브젝트 풀링(Object Pooling) : 객체의 생성(Instantiate)과 파괴(Destory)가 자주 일어난다면 가비지 컬렉터의 호출 시기를 빠르게 오기에 효과적인 방법입니다 "오브젝트 풀링" 을 이용하면 미리 오브젝트들을 생성한 뒤에 재사용 하여 생성 및 삭제를 최소화 할 수있는 방법입니다.
  2. 메모리 관리 : 메모리의 누수를 방지하기 위해서 참조들을 관리하고 불필요한 참조는 끊어주는 등의 최적화를 하는 방법이 있습니다.
  3. Sturct(값 타입) : 힙 메모리인 클래스가 아닌 스택 메모리인 구조체를 사용하여 정의한다면 컬렉션의 부하를 줄일수가 있습니다. 다만 구조체의 특정 상황에 맞게 사용해야 합니다.











➔ 7. 가비지 컬렉션이란 무엇인지 설명해주세요.

!! 가비지 컬렉터와 컬렉션은 다른 의미(아 다르고어 다르다)
!! 가비지 컬렉터는 사용중이지 않는 메모리를 식별하여 해제해주는 시스템
!! 가비지 컬렉션은 가비지 컬렉터에 의해 수행되는 그 자체

가비지 컬렉션은 동적으로 할당된 메모리를 관리하는 "과정"입니다. 더 이상 사용되지 않거나 필요가 없는 메모리를 식별하여 해제하여 메모리를 관리해주는 최적화 시스템입니다. 가비지 컬렉터에 의해 수행되는 과정이라고 볼 수 있습니다.












➔ 8. 박싱과 언박싱에 대하여 설명해주세요.

a. (꼬리질문) 방식, 언박싱을 사용할 때 주의해야 할 점이 있다면 무었이 있나요?

박싱과 언박싱은 값 형식(Value Type)과 참조 형식(Reference Type)간의 변환을 말합니다.
박싱은 Value Type(값)을 Reference Type(참조)으로 변환하는 과정을 말합니다.
힙(heap) 메모리에 새로운 객체를 생성하고 값 형식의 데이터를 복사하여 저장하는 과정을 말합니다.
언박싱은 Reference Type을 Value Type으로 변환하는 과정을 말합니다.
힙(heap)에서 값을 꺼내와서 스택에 저장하는 과정을 말합니다.

꼬리질문

박싱과 언박싱은 값과 참조간의 타입 볍환이 생기므로 성능에 저하가 발생할 수 있으므로 반복적인 박싱과 언박싱 작업을 주의하여 사용해야합니다.
object타입을 다른 형식으로 변환할 때에도 박싱과 언박싱이 일어나므로 주의해야 하며 제네릭을 사용하여 값을 직접 저장하고 처히라여 데이터 형식의 변환을 최소화시켜 박싱과 언박싱을 피할수 있습니다.












➔ 9. 배열과 List, ArrayList, Dictionary의 차이점을 설명해주세요. 

a. (꼬리질문) Dictionary는 어떻게 구현해야 하나요?

b. (꼬리질문) Dictionary가 검색이 빠른 이유는 무엇인가요?

배열(Array)은 고정된 크기를 갖습니다. 선언할 때 크기가 고정되어 할당되며, 동적으로 변경할
수 없습니다. 또한 동일한 데이터 형식만 포함할 수 있지만 인덱스에 빠르게 접근할 수 있습니다.

리스트(List)는 동적으로 크기를 조절할 수 있습니다. 제네릭을 사용하여 데이터를 할당할 수 있
지만 한번 담은 뒤에는 동일한 데이터만 포함할 수 있습니다. 또한 메서드를 통해 요소를 추가,
삭제,검색 할 수 있고 배열에 비해 약간의 오버헤드가 있지만 대부분 상횡에서는 미미합니다.

어레이리스트(ArrayList)는 리스트처럼 크기를 동적으로 조절할 수 있습니다. 하지만 어레이리스트는 서로 다은 데이터 형식의 요소를 포함하는 것이 가능합니다. 다만, 리스트보다 느릴 수 있고 요소를 가져올 때 형변환을 해야합니다.

딕셔너리(Dictionary)는 키(Key)와 값(Value)로 이루어진 데이터 구조입니다. 키를 사용하여 매우 빠르게 값을 찾을 수 있어 배열이나 리스트 등에서 특정 항목을 찾는 것 보다 훨씬 빠르며 효율적입니다.

꼬리질문 a

딕셔너리는 키와 값을 제네릭으로 받기에 각각 키-값에 데이터 타입을 결정하여 선언하고 객체를 생성한 후에는 'Add' 메서드를 이용하여 키-값의 쌍을 추가할 수 있고 'Remove'메서드를 사용하여 특정 키와 그에 상응하는 값을 딕셔너리에서 제거할 수도 있습니다.. 데이터를 검색하거나 접근하려면 대괄호[]안에 키를 넣어 사용하고 이 때, 키가 딕셔너리에 존재하지 않는 예외가 발생할 수 도 있으므로 'ContainsKey' 메서드를 사용하여 키의 유무를 확인 하는것이 좋습니다.

꼬리질문 b

딕셔너리는 해시 테이블(Hash Table)이라는 자료구조를 사용하기 때문입니다. 해시 테이블은 키를 해시 함수를 사용해 고유한 인덱스로 변환하고, 해당 인덱스에 값을 저장합니다. 이 해시 값은 데이터가 저장되는 배열의 인덱스로 사용되므로, 키를 통해 직접 위치를 찾아가기 때문에 (LinkedList처럼 타고타고 가는것이 아닌) 데이터의 양에 관계없이 일정한 시간 내에 데이터를 찾을 수 있습니다. 시간 복잡도로 O(1)이 나옵니다.












➔ 10. 제네릭이란 무엇인가요?

제네릭은 C#에서 사용할 수 있는 강력한 기능입니다. 코드의 재사용성을 높이고, 타입의 안전성을 향상시키는데 도움이 되는 프로그래밍 기능입니다. 제네릭은 데이터 형식을 매개변수화 하여 하나의 클래스, 메서드, 인터페이스, 델리게이트 등을 정의할 수 있습니다. 제네릭을 사용하면 타입 변환에 따른 성능의 저하를 방지하고, 컴파일 타임에 타입 안정성을 보장합니다. 예를 들면, 대표적으로 List<\T>와 같은 클래스가 있습니다. T라는 플레이스 홀더를 사용해 어떠한 타입의 데이터든 저장할 수 있어서, List<\int>, List<\string>, List<\GameObject> 등의 형태로 사용이 가능합니다. 이렇게 제네릭을 사용하면 코드의 재사용성을 높이고 타입의 안정성을 유지,향상 시키는데 매우 효과적입니다.

플레이스 홀더 : 타입의 종류를 지정해주지는 않지만 어떤 타입이 들어오게 될 것이라는 것을 의미

profile
No Easy Day

0개의 댓글