[C] 쇼트 서킷으로 조건문 구현

언어/C 2018. 12. 17. 06:30

보통 조건문을 사용할땐 if 키워드를 사용하곤 한다.

너무 진부하지 않은가?

스위치도 있지만 경우가 좀 다르고

삼항연산자도 나쁘지 않지만 더 세련된 조건문의 대체재가 있다.

바로 쇼트서킷이라 하는 최적화 현상을 이용하는 것이다.

이게 뭐냐면, 가령 && 연산을 한다 쳐보자. 
이 연산은 양쪽이 전부 true일 경우에만 true를 반환한다. 
그 말인즉 하나라도 false면 무조건 false가 된다는 말이고, &&연산의 앞부분이 false로 판명나면 뒷부분을 무시하고 지나가도록 최적화를 한다.
이게 바로 쇼트서킷이다.

그리고 ||도 마찬가지다. 앞부분이 true면 그냥 넘어간다.

이 현상을 이용해서 우린 더 멋진 분기를 구성해볼수 있다.

이런 식으로





'언어 > C' 카테고리의 다른 글

[C] Q: 프로그램이 실행되자마자 꺼져요!  (0) 2018.12.30
[C] 함수 호출 규약.link  (0) 2018.12.17
[C] sizeof 연산자에 대해서  (0) 2018.12.17
[C] 포인터란?  (0) 2018.12.17
[C] C언어 유니코드 사용법  (0) 2018.12.17

설정

트랙백

댓글

[C] sizeof 연산자에 대해서

언어/C 2018. 12. 17. 06:27

알다시피 sizeof는 특정 타입이나 변수의 바이트 크기를 구하는 연산자다.

그리고 컴파일 타임에 수행되기때문에 성능을 먹지 않는다.
직관적인 코드를 만드는데 도움을 준다.


사용법은 다음과 같다.

ex)
sizeof(char); //반환값 1
char num;
sizeof(num); //반환값 1
sizeof num; //반환값 1, 변수에는 괄호 생략가능

반환값이 1인 이유는 당연히 char 크기가 1바이트라서다.

그리고 int 타입과 포인터 타입은 컴파일러마다 크기가 다를 수 있다.
32비트를 사용하는 컴파일러라면 둘다 4바이트가 될 것이고,
64비트 컴파일러라면 8바이트가 될 것이다.


!
그리고 가장 헷갈릴만한게 배열과 포인터에 대한 연산이다.
배열은 기본적으로 크기와 주소가 고정적인 포인터이므로 각 요소의 크기X요소 개수로 반환값이 나오지만, 포인터는 그냥 포인터 크기로 나온다.

여기서 특히 주의할 점은, 배열의 주소를 포인터에 넣더라도 그 포인터가 배열이 되는건 아니라는 말이다. 그냥 배열의 첫번째 주소값을 가지는 포인터일 뿐이다.

그러므로 32비트 기준으로 다음과 같은 코드는

int arr[10];
int ptr=arr;
printf("%d, %d",sizeof(arr),sizeof(ptr);

"40, 4" 라는 내용을 출력한다.


그리고 배열을 파라미터로 전달할 때도 마찬가지다. 그냥 포인터 타입으로 전달된다.
배열의 특성을 유지하면서 파라미터로 넘길 순 없다.

'언어 > C' 카테고리의 다른 글

[C] Q: 프로그램이 실행되자마자 꺼져요!  (0) 2018.12.30
[C] 함수 호출 규약.link  (0) 2018.12.17
[C] 쇼트 서킷으로 조건문 구현  (0) 2018.12.17
[C] 포인터란?  (0) 2018.12.17
[C] C언어 유니코드 사용법  (0) 2018.12.17

설정

트랙백

댓글

[C] 포인터란?

언어/C 2018. 12. 17. 06:26

C언어 학습자들을 가로막고 있는 가장 큰 장벽은 아마 포인터일 것이다.

사실 나는 그렇게 어렵다고 느껴본 적은 없는데... 그렇다면 그런거겠지

정말 이해만 하면 별거아닌 개념이다.


변수가 무엇인지 기억하는가?

변수를 생성한다는 것은 특정 메모리공간에 이름을 붙여준다는 뜻이다.

그리고 포인터는 그 특정 메모리공간의 진짜 이름을 가리키는 값이다.

비유하자면 변수는 별명이고, 포인터는 본명이라고 할 수 있겠다.

별명은 부르기에 따라서 수십수백개도 될 수 있지만 본명은 하나 뿐이다.(귀화를 하지 않았다면)


그래서 이걸 도대체 어떻게 쓴다는 건가?

이젠 방법론적인 측면으로 봐보자.

일단 포인터 변수는 다음과 같이 사용할 수 있다.

int num=20;
int* ptr=#
printf("%d",*ptr);
//output->20

int* 는 "int 타입을 가리키는 포인터 타입" 이라고 이해할 수 있다.
마찬가지로 char*, short*... 뭐든 *를 갖다붙이면 그 타입을 가리키는 포인터 타입이 된다.

그래서 int* 타입의 변수 ptr가 생성된다.

그리고 ptr도 변수니까 값을 넣을 수 있다!

10이나 50같은 그냥 숫자도 넣을 수 있긴 있는데, 그래봐야 쓰지도 못하는 쓰레기가 될 뿐이다.

포인터 변수니까 당연히 그에 맞는 포인터 값(이하 주소값)을 넣어줘야 한다.

그래서 등장하는 부분이 바로 &num이다.
num 변수의 주소값을 반환한다는 의미이고, 이것이 ptr에 대입됨으로써 ptr은 진정한 포인터의 역할을 할 수가 있게 된다...

이제 ptr은 num의 본명(주소값)을 갖고 있다.
이 본명을 굳이 알고싶다면

printf("%p",ptr); 이렇게 쓰면 된다. 숫자와 알파벳으로 얼룩진 16진수 숫자가 나올 것이다...

그리고 *ptr; 이라는 연산을 행해보자.
여기에서의 *은 곱셈도 아니고, int* 타입에서의 *도 아닌 별개의 연산이다. 그냥 기호만 같다.

저 구절을 해석하자면 "ptr에 담긴 본명(주소값)을 따라가서 거기에 저장된 값에 접근하겠다"

라는 의미가 된다.

그리고 
*ptr=40;
이런식으로 접근해서 넣어주면

본명을 통해서 본질적인 접근을 한 것이기 때문에 당연히 num의 값도 40이 된다. (num은 별명일 뿐이다.)


그리고 특정 구간의 로컬 값을 외부에서 컨트롤할수 있도록 넘겨줄 수도 있다.


int 타입 변수 num을 함수에 넘겨서 하나 증가시키는 코드를 짜보자.

void AddOne(int add)
{
ㅤadd++;
}

int main()
{
 ㅤint num=10;
ㅤAddOne(num);
ㅤprintf("%d",num);
}

자 컴파일을 해보자.

무슨 값이 나올까?

우리가 기대하는 값은 11인데

결과는 10이다.

왜 그럴까?

매개변수로의 값 전달은 오로지 '값' 전달만 가능하기 때문이다.

num에 담긴 값은 10이고, AddOne 함수의 인자로는 10이 전달된다. num이 아니라.

AddOne 안에서 그냥 상수 10을 가져다가 add 변수에 값을 대입해넣는데,

그걸 0으로 만들든 35로 만들든 num과 도대체 무슨 상관이겠는가?

이럴 경우에 포인터가 사용된다.

포인터를 적용해서 고쳐보자.

void AddOne(int* add)
{
ㅤ*add++;
}

int main()
{
 ㅤint num=10;
ㅤAddOne(&num);
ㅤprintf("%d",num);
}

됐다. 이젠 정상적으로 11이 출력된다.

AddOne에 num의 주소를 전달하고,
AddOne 안에서는 그 주소를 따라서 num의 본체에 접근했기 때문이다.


그리고 또 하나의 용도가 있다.

int나 double같은 건 뭐 4바이트 8바이트밖에 안되니까 다른 함수에 전달할때 그냥 그대로 전해주면 된다.

그런데 구조체같은 경우라면 어떨까?
이런저런 정보들이 많이 들어가다보면 수십바이트를 차지할 수도 있고, 복사 시간도 더 잡아먹게 된다.

포인터의 크기는 보통 4바이트이기 때문에 구조체 대신 구조체의 포인터를 전달하면 효율을 향상시킬 수 있다.


그리고 포인터 중에서도 이질적인 포인터가 있다.

바로 void* 타입이다.

이 타입은 어떤 포인터 타입이든 다 저장할 수 있다.

그대신 참조 접근 { ex)*ptr } 이 불가능하다.

그래도 뭐 *(int*)ptr 이런식으로 편법을 쓰면 되고, 보통 포인터 타입을 확정할 수 없을 때 쓴다.

예로, 메모리를 동적할당해주는 alloc 계열 함수(malloc, calloc...)들은 모두 void*로 값을 반환한다. 그래서 int* ptr=(int*)malloc(sizeof(int)); 같은 식으로 사용하는 것이다.
alloc에 넣을 타입이 뭔줄알고 int*, char*같은걸 반환하겠는가...


포인터에 대한 설명은 이게 다다.

별거 없다.

설정

트랙백

댓글

[C] C언어 유니코드 사용법

언어/C 2018. 12. 17. 06:25

c언어는 기본적으로 유니코드가 아닌 아스키코드를 사용하기 때문에 한글 같은 문자들을 사용하려면 약간의 번거로움이 따른다.


여기서 말하는 아스키 코드라는 건 미국에서 알파벳이나 기본적인 문장부호들을 컴퓨터에서 표현하려고 각각의 수를 문자에 대응시켜놓은 표준 체계인데, 이 체계는 1바이트 = 8비트 -> 2의 8제곱=256만큼의 수만을 갖기 때문에 로마자와 몇개의 기호만을 포함하기 때문에 한글이나 한자, 가나 등의 다른 문자를 표현할 수 없다.

그에 반해 유니코드를 포함한 기타 확장문자 체계들은 2바이트=16비트로 16제곱=65536개의 수를 표현할 수 있어 주로 사용되는 대부분의 언어를 지원하고도 공간이 남는다.


비교적 뒤에 제작된 JAVA는 기본적으로 자료형 char를 2바이트로 설정해놓아서 알아서 2바이트 기준의 확장문자들을 사용하는데, c언어는 1바이트로 되어있어 char형 두개씩을 배열로 묶어 멀티바이트 형식으로 표현하거나 2바이트 이상의 정수 타입들을 사용하는 수밖에 없다.

유니코드 입출력을 표현하려면 일단 컴파일러 내에서 유니코드를 사용하게끔 적용해야 한다.

비쥬얼의 경우에는 프로젝트->문자열 체크 속성 에서 유니코드 집합 사용을 체크하면 된다.

이런걸 wide character(확장문자)라고 하는데 이것에 대한 함수를 지원하는 라이브러리가 바로 <wchar.h>이다.

http://www.cplusplus.com/reference/cwchar/


​복잡하게 생각할건 없고 그냥 stdio나 stdlib에 있는 통상 함수들 이름 앞에 w를 붙이면 대부분 해당 함수의 확장문자 버전이 된다. 

그리고 유니코드 문자열을 위한 자료형으로서 wchar_t가 정의되어 있는데 대체로는 unsigned short와 같다. 근데 이게 표준으로 명확하게 명시된게 아니라서 int일 경우도 있다. 그냥 char라는 정수타입을 함수 내에서 문자를 처리하는 타입이라 약속해놓고 쓰는것처럼, w계열 함수들도 wchar_t를 문자 타입으로 약속해놓고 쓰는 것이다.

그리고 리터럴을 2바이트 문자열로 인식하게 해주는 접두어 L이 있다. c언어는 기본적으로 1바이트 형식으로 문자열을 인식하고, 리터럴마저도 그런 법칙에서 벗어나진 않기 때문에 이걸 앞에 붙여주지 않으면 문제가 생긴다.



그리고 필요한게 이것뿐만이 아니다.

로케일이란게 필요하다. 문자열 처리방식을 어떻게 해야 할까를 정하는 것이다.

C언어의 로케일은 기본적으로 아스키코드만을 사용하기 때문에 이것도 확장을 해줘야 제대로 처리를 한다.

http://www.cplusplus.com/reference/clocale/setlocale/


여태껏 나열한 것을 바탕으로 출력 예제를 작성하면 다음과 같이 할 수 있다.


구글링하면 훨씬  더 좋은 글들이 많다.


2017/2/2 작성

2018/7/22 수정 

'언어 > C' 카테고리의 다른 글

[C] Q: 프로그램이 실행되자마자 꺼져요!  (0) 2018.12.30
[C] 함수 호출 규약.link  (0) 2018.12.17
[C] 쇼트 서킷으로 조건문 구현  (0) 2018.12.17
[C] sizeof 연산자에 대해서  (0) 2018.12.17
[C] 포인터란?  (0) 2018.12.17

설정

트랙백

댓글