C언어

Hagi 2012. 1. 17. 16:49

(출처 :  http://ko.wikipedia.org/wiki/%EB%B0%B0%ED%83%80%EC%A0%81_%EB%85%BC%EB%A6%AC%ED%95%A9 )


배타적 논리합 (exclusive or)은 수리 논리학에서 주어진 2개의 명제 가운데 1개만 참일 경우를 판단하는 논리 연산이다. 약칭으로 XOR, EOR, EXOR라고도 쓴다.


예시

“내 키는 160cm 이상이다.”와 “내 몸무게는 60kg 미만이다.” 이 두 명제의 배타적 논리합은, “내 키는 160cm 이상이고 몸무게는 60kg 이상이다. 혹은, 내 키는 160cm 미만이고 몸무게는 60kg 미만이다.”가 된다.

추가로, 두 명제 A,B 에 대한 교집합 (A  B)가 공집합이면, 배타적 논리합은 논리합과 같게 된다. 예를 들어, A = “내 키는 160cm이다.”와 B = “내 키는 170cm이다.”는 동시에 성립할 수 없기 때문에(교집합이 없음), (A xor B)와 (A  B)는 동일하게 “내 키는 160cm와 170cm 중 하나다.”가 된다.


진리표

명제 P명제 QP ⊻ Q
거짓
거짓
거짓
거짓거짓거짓

비트간 배타적 논리합

0011_{(2)} \oplus 0110_{(2)} = 0101_{(2)} .


 
 
 

C언어

Hagi 2012. 1. 17. 16:46

 (출처 : 뇌를 자극하는 ASP.NET 2.0 프로그래밍, P102)


&는 'AND'이고, &&는 'short-circuit AND'입니다. 그리고 |는 'OR'이고, ||는 'short-circuit OR'입니다. short-circuit의 뜻을 굳이 풀이하자면 '짧은 선회', '짧은 회로' 또는 '단락' 정도로 풀이할 수 있습니다. 그런데 &&과 ||는 왜 이런 뜻을 가지고 있을 까요?

AND의 경우 좌측 피연산자가 false이면 우측 피연산자가 무엇이든지 간에 결과값이 false입니다. 


 false AND ( true or false) -> 무조건 false


하지만 &는 좌측 피연산자가 false라도 무조건 우측 피연산자를 검사합니다. 반면에 &&(short-circuit AND)의 경우 좌측 피연산자가 false이면 우측 피연산자를 검사하지 않고 바로  false라는 결과 값을 반환합니다. 물론 좌측 피연산자가 true이면 우측 피연산자에 따라 결과가 바뀌므로 우측 피연산자를 검사합니다. 


OR의 경우도 마찬가지입니다. OR는 좌측 피연산자가 true이면 우측 피연산자에 상관없이 결과값은 true입니다. |는 좌측 피연산자가 true라고 하더라도 우측 피연산자를 무조건 검사합니다. 반명에 ||는 그렇지 않다는 것이죠. 


 그러므로 보통 논리 비교 시 &, |보다는 효율성 있는 &&과 ||을 많이 사용합니다. 하지만 경우에 따라 &, |또는 중 어느 것을 사용하느냐에 따라 그 결과가 달라질 수 있습니다. 다음과 같은 예가 바로 그와 같은 경우입니다. 


 int a = 10;

 int b = 20;

 bool c = (++a > ++b) & (++a > ++b);


&을 사용하면 a는 12, b는 22가 될 것이고, & 대신 &&을 사용하면 a는 11, b는 21이 될 것입니다. 그 이유는 현재 좌측 피연산자가 false이므로 &&을 사용할 경우 우측 피연산자가 연산되지 않기 때문입니다. 우측 피연산자가 연산되지 않으면 변수 a,b에 사용된 증가 연산자가 실행되지 않습니다. 고의성이 없는 한 이렇게 코딩하는 경우는 드뭅니다. 하지만 있을 수 있는 경우이므로 &,|과 &&,||의 차이점을 확실히 해둡시다. 

 
 
 

C언어

Hagi 2010. 7. 13. 08:31

(출처 : http://dj0155.tistory.com/11 )



프로그램이 메모리에 올라가게 되면 Data Segment 와 Code Segment 로 나뉘게 된다.

 

Heap 과 Stack 은 Data Segment 를 이용하게 된다.

 





  • 힙(Heap)은 런 타임시에 크기가 결정되는 요소들이 저장되는 공간이다.

C의 malloc() 함수나 C++의 new 연산자로 메모리 할당이 될 때에는 이 Heap 공간에 메모리가 잡히게 된다.

 





  • 스택(Stack)은 컴파일시에 크기가 결정되어있는 요소들이 저장되는 공간이다.

함수가 받는 매개 변수나 함수내에서 사용되는 지역변수가 이 Stack 영역에 저장이 된다.

 

 

메모리를 가상으로 그림으로 그려보면 다음과 같이 나타낼 수 있다.

 

 

※데이터 영역

전역 변수와 static 변수 저장

----------------------------------

※힙 영역

동적 할당되는 데이터 저장

 (데이터가 위부터 순차적으로 저장)

 

 

 

※스택 영역

지역 변수와 매개변수가 저장

 (데이터가 아래부터 순차적으로 저장)

 

 

보통 전역 변수나 static 변수는 heap 의 윗 부분에 위치하고, Stack 은 Data Segment 의 처음부터 할당이 되고,

 

Heap 은 끝부분 부터 할당이 된다.

 

 

예제 코드를 작성하여 돌려보았다.

 

 

#include <stdio.h>

int A, B;

main()
{
 int a = 0;
 int b = 0;

 int *p1 = NULL;
 int *p2 = NULL;

 p1 = (int*)malloc(sizeof(A));
 p2 = (int*)malloc(sizeof(A));

 printf("전역 변수의 주소값 출력\n");
 printf("%d\n", &A);
 printf("%d\n", &B);
 printf("동적할당된 포인터의 주소값 출력\n");
 printf("%d\n", p1);
 printf("%d\n", p2);
 printf("지역 변수의 주소값 출력\n");
 printf("%d\n", &a);
 printf("%d\n", &b);

 free(p1);
 free(p2);
}

 

Visual Studio 2003에서 작성하였고, 알아보기 쉽게 10진수로 출력을 하였다.

 

메모리 할당이 된 간격은 틀리지만,

 

전역 변수와 동적 할당된 포인터의 메모리 할당 순서가 지역 변수의 메모리 할당 순서와 다른것을 확인할 수 있다.

 

 

Stack 영역에 올라가는 데이터는 프로그램이 실행되자마자 마로 메모리에 할당이 되고, 함수가 종료되거나, 프로그램이 종료될 때 자동으로 메모리 공간이 해제된다.

 

 

그에 반해, Heap 영역에 올라가는 데이터는 프로그램이 실행되는 중간에 메모리에 할당되고, 프로그램이 종료될 때

 

자동으로 메모리 공간이 해제되지 않는다. 메모리 공간을 해제하면 다시 그 영역을 사용할 수 있다.

 

 

따라서 동적으로 할당된 메모리 공간은 C의 free() 함수나 C++의 delete 연산자를 사용하여 메모리 공간을 다시 해제하여 주어야 한다.

 

그렇지 않을 경우 메모리 누수(leak) 가 발생하게 된다.

 

 

그렇다면 메모리 동적할당이 왜 필요한 것인가?

 

 

예를들어 다음과 같이 5개의 정수형 데이터를 입력하는 프로그램이 있다고 하자.

 

#include <stdio.h>

 

main()
{
 int arr[5];

 int i = 0;
 for(i = 0; i < 5; i++) {
  printf("정수 입력 : ");
  scanf("%d", &arr[i]);
 }
 for(i = 0; i < 5; i++) {
  printf("%d번째 입력 데이터 : %d\n", i+1, arr[i]);
 }
}

 

이 프로그램은 죽었다 깨어나도 5개의 정수형 데이터 밖에 입력을 하지 못한다. 이제 이 프로그램을 실행 시 마다 입력 되는 데이터의

 

수가 다르게 작성을 하고 싶어서 다음과 같이 프로그램을 작성하였다.

 

#include <stdio.h>

void func(int n);

main()
{
 int *p = NULL;
 int i = 0, n = 0;
 printf("입력할 데이터의 수를 입력 : ");
 scanf("%d", &n);
 func(n);
}

void func(int n)
{
 int arr[n];                     // Compile Error
 int i = 0;
 for(i = 0; i < n; i++){
  printf("정수 입력 : ");
  scanf("%d", &arr[i]);
 }
 for(i = 0; i < n; i++)
  printf("%d번째 입력 데이터 : %d\n", i+1, arr[i]);
}

 

-------------------------------------------------------------------------------------------------------------------

func() 함수 내의 arr 이라는 배열은 func 함수가 실행된 후 종료될 때 스택 메모리 영역에서 메모리가 해제 되므로

 

출력까지 func 함수 내에서 처리하였다. 주의깊게 보아야 할 부분은 배열 선언 시 인덱스 값으로 변수가 올 수 없다는 것이다.

 

(배열 선언시의 인덱스 값에는 상수만이 올 수 있다.)

-------------------------------------------------------------------------------------------------------------------

 

그럼 다음과 같은 문제를 포인터를 사용한 동적 메모리 할당으로 해결해보면 다음과 같은 방식으로 작성할 수 있다.

 

포인터와 배열의 차이점과 공통점을 어느정도 알고 있어야 한다.

 

#include <stdio.h>
#include <stdlib.h>


main()
{
 int *p = NULL;
 int i = 0, n = 0;
 printf("입력할 데이터의 수를 입력 : ");
 scanf("%d", &n);
 p = (int*)malloc(sizeof(int)*n);
 for(i = 0; i < n; i++){
  printf("정수 입력 : ");
  scanf("%d", (p)+i);
 }
 for(i = 0; i < n; i++)
  printf("%d번 입력 데이터 : %d\n", i+1, *((p)+i));

 free(p);                   // Important!!
}

 

 

-------------------------------------------------------------------------------------------------------------------

main() 함수 내에 포인터 변수 p가 선언되어 있고, 이를 입력받은 n의 갯수만큼 int 형으로 동적 메모리 할당을 하고 있다.

이후 포인터 변수 p를 이용하여 데이터를 입력받고 출력 한 후 할당받은 메모리 공간을 해제한다.

-------------------------------------------------------------------------------------------------------------------

 

 

동적 메모리 할당을 사용하면 위와 같은 이점을 활용할 수 있게된다.

 

 

C++에서의 new 와 delete 연산자를 이용한 메모리 할당/해제 방식은

 

 

int *p;

p = new int[데이터갯수];

delete[] p;

 

와 같은 방법으로 할당과 해제를 한다.

 

예를들어 '데이터 갯수' 부분에 5를 입력하게 되면

 

malloc(sizeof(int)*5) 와 같은 크기를 할당하게 된다. (new 연산자 사용시에는 데이터 타입을 알 수 있다.)