Creative Commons License

Microsoft .NET

닷넷!시작하기
닷넷! Ver 2.0~
닷넷!스킬업
웹개발
윈폼개발
실용모듈개발
Tip & Tech
하루 한 문법

Microsoft .NET 개발자들을 위한 공간입니다. 기초강의에서 부터 고급 기술 정보 및 팁등을 다루도록 하겠습니다.

.

닷넷!시작하기

Microsoft. NET 을 시작하는 분들을 위한 강좌입니다. 주로 기초적인 내용과 때론 기본적인 내용을 다룹니다

[연산자 이야기] & vs && 그리고 | vs ||

작성자 : 박종명
최초 작성일 : 2009-07-08 (수요일)
최종 수정일 : 2009-07-08 (수요일)
조회 수 : 4946

다음 글은 연산자 &,&& 그리고 |,|| 에 대해 나일수님이 정리해 주신 글입니다
나일수님의 닷넷, 특히 윈도우응용프로그램에 스킬과 열정을 가진 닷넷개발자이며 저의 가까운 지인이기도 합니다
------------------------------------------------------------------------------------------------------------------------------------------------------------

우리가 개발할 때 쓰는 연산자 중에 가장 흔히 쓰는 것이 &, && , |, || 일것이다.
이항 연산자로써의 이 연산자들을 제대로 알고 쓰고 있는지 생각해 보는 시간을 가져볼까 한다.

1) & 연산자 와 && 연산자
이항 & 연산자의  정수 계열 형식과 bool 에 대해 미리 정의되어 있다.
bool 피연산자의 경우 & 연산자는 피연산자에 대한 논리곱을 계산한다.
즉, 두 피연산자가 모두 true인 경우에만 결과가 true이다.

조건부 논리곱 연산자(&&) bool 피연산자의 논리곱을 수행한다.

두 연산자 모두 이항연산자로써 bool 연산을 할때 피연산자에 대한 논리곱을 계산한다.
그렇다면 bool 연산에 쓰이는 두 연산자는 차이가 없는 것일까?
(차이점이 없다면 이 글을 쓰지도 않았을 것이다.....ㅠ,.ㅠ)

차이점은 이항 & 연산자 의 경우 첫번째 피연산자와 두번째 피연산자를 모두 계산하고 조건부 논리곱 연산자(&&) 의 경우 bool 피연산자의 논리곱을 수행하지만 둘째 피연산자는 필요한 경우에만 계산한다.

첫째 피연산자가 false 인 경우 두번째 피연산자는 계산하지 않고 false 를 리턴한다.

뭔말인지 이해가 되는가?

이해를 돕기 위해 예를 들어보자.
우리가 ADO.NET 을 이용하는 프로그래밍을 한다손 치면 대부분 결과셋은 DataSet 의 형태일 것이다.
이때 무턱대고 DataSet 내부의 프로퍼티에 접근한다면 NullReferenceException 이 발생할 여지가 크다.

private DataTable dt = null;

public void InitializeTable(DataSet ds)
{
       this.dt= ds.Tables[0];
}

이와 같은 형태의 단순명료한 코드에서 조금더 방어적인 코드를 짜본다면 ds 변수가 null 인지 검사를 하고
ds 변수안에 Tables 프로퍼티가 노출하는 컬렉션이 null 인지 검사를 한다.

public void InitializeTable(DataSet ds)
{

       if(ds != null & ds.Tables != null)
              this.dt = ds.Tables[0];
}

 

하지만 이렇게 방어를 하면 다 끝날까?
만약 Tables 가 null 이 아닌 DataTableCollection 의 객체를 리턴하더라도 DataTableCollection 안에 어떠한
DataTable 도 존재하지 않는다면 OutOfMemoryException 이 발생할 여지가 있다.
이 부분까지 방어하는 코드를 짠다면 아래와 같다.

public void InitializeTable(DataSet ds)
{

       if(ds != null & ds.Tables != null & ds.Tables.Count > 0)
              this.dt = ds.Tables[0];
}

자 이제 왠만해선 Exception 이 발생하지 않을 것이다.
눈치가 빠른 독자라면 이미 이쯤에서 무엇이 잘못된 것인지 눈치 챘을거라 생각된다.

위 코드는 문제가 있다. 우리는 한 객체의 내부 객체로의 접근 과정에서 null 참조 오류와 인덱스 오류를 방지하기
위해 흔히 쓰는 & 연산자를 무심코 쓸 수 있다.

하지만 위에서 이야기 했듯이 이항 & 연산자 의 경우 피연산자 모두를 검사하게 되는데 이과정에서 다시금
NullReferenceException 이 발생할 수 있다.

만약 ds 가 null 이라 가정해 보자.
ds != null 라는 구문은 false 가 될것이다. 여기까진 문제 없다.
하지만 ds.Tables != null 이라는 구문을 검사할 때 NullReferenceException 이 발생한다.

코드를 아래와 같이 바꾸어보자.

public void InitializeTable(DataSet ds)
{

       if(ds != null && ds.Tables != null && ds.Tables.Count > 0)
              this.dt = ds.Tables[0];
}

이렇게 바꾸면 ds 가 null 이라는 구문은 false 가 되고  조건부 논리곱 연산자(&&) 에 의해 더이상의 논리곱 계산은 하지 않고 if 문 자체가 false 가 되어 NullReferenceException 이 없이 코드를 빠져나가게 되므로 더 이상 NullReferenceException 이 발생하지 않게 된다.

이렇듯 첫째 피연산자가 false 가 되어 둘째 피연산자의 계산을 하지 않는 것을 단락(short-circuit) 계산이라고
한다.

이제 이해가 되는가? 그렇다면 이와 유사한 | 와 || 는 어떻게 될까?
이제 | 와 || 에 대해 알아보자.

2) | 연산자 와 || 연산자
이항 | 연산자의  정수 계열 형식과 bool 에 대해 미리 정의되어 있다.
bool 피연산자의 경우 | 연산자는 피연산자에 대한 논리합을 계산한다.
즉 피연산자가 모두 false인 경우에만 결과가 false이다.

조건부 논리곱 연산자(||) bool 피연산자의 논리합을 수행한다.

역시 모두 이항연산자로써 bool 연산을 할때 피연산자에 대한 논리합을 계산한다.
차이점은 & 와 && 의 차이점과 유사하다. 

이항 | 연산자 의 경우 첫번째 피연산자와 두번째 피연산자를 모두 계산하고 건부 논리합 연산자(||) 의 경우
bool 피연산자의 논리합을 수행하지만 둘째 피연산자는 필요한 경우에만 계산한다.

첫째 피연산자가 true 인 경우 두번째 피연산자는 계산하지 않고 false 를 리턴한다.
&& 연산자의 경우과 유사하게 첫째 피연산자가 true 가 되어 둘째 피연산자의 계산을 하지 않는 것을
락(short-circuit) 계산이라고 한다.

| 연산자 와 || 연산자 에 대한 예제는 들지 않겠다.

한번더 생각을 정리해 보는 차원에서 여러분들이 흔히 발생할 수 있는 예제를 만들어보길 바란다.

3) 고찰
우리가 코드를 작성할 때 이와 같이 상황에 맞는 연산자를 써야 잠재오류를 줄일 수 있고 능에도 좋은 영향을 줄 수 있다. 이런 경우를 생각해 보자. 요즘은 하드웨어의 성능이 비교도 안될 정도로 좋아서 눈에 띄게 성능의 차이를 느끼지는 않겠지만 예제와 같이 오류를 줄이는 차원이 아닌 경우랄지라도 논리적인 계산에 의한 쓸데없는 계산의 횟수를 줄일 수 있다.

단락(short-circuit) 계산에 의해 둘째, 셋째, 넷째... 피연산자의 계산을 건너뛴다면 아주 아주 아주 미약하나마 성능에 도움이 되지 않을까 생각해 본다.

이것으로 "연산자 이야기 - & vs && 그리고 | vs ||" 를 마친다.

참고)
위에서 제시한 && 예제는 아래의 코드와 같다.
public void InitializeTable(DataSet ds)
{

       if(ds != null)

       {

             if(ds.Tables != null)

             {

                    if(ds.Tables.Count > 0)
                           this.dt = ds.Tables[0];

              }

        }
}

이름
비밀번호
홈페이지
QK <- 왼쪽의 문자를 오른쪽 박스에 똑같이 입력해 주세요