[C] C99~C11 조금 낯선 기능들

언어/C 2018. 12. 30. 20:53

구조체 리터럴 (C99)

변수명 없이 임시적으로 생성되는 구조체 리터럴을 선언할 수 있다.

(struct 구조체명) { 요소들...}


구조체 지정 초기화 (C99)

멤버를 직접 지정해서 초기화한다.
보다 명시적이고, 순서와 관련없이 대입해 넣을 수 있다.

여기엔 두가지 방법이 있는데, 
.과 =을 쓰는 방법이 이것이다.

= { .멤버명 = 대입값, ...};




근데 매번 .을 붙이려면 그것도 꽤 귀찮지 싶다.
그런 이들을 위한 두번째 방법이 있다.

= {멤버명 : 대입값, ...};



Flexible 배열 멤버 (C99)

아래와 같이 길이가 정해지지 않은 구조체 멤버 배열을 사용하는 기능이다.

struct flex_t{
    int age;
    char name[];
};

여기에는 몇가지 주의사항과 제약사항이 있다.
사실 좀 많이 까다롭다.
일단 위처럼 플렉서블 배열은 맨 뒤에 와야 한다.
그리고 다른 멤버가 하나 이상 있어야 한다.

일반적인 방법으로 사용하지도 못한다. 길이가 없기 때문에 메모리에 그를 위한 공간이 할당되지 않기 때문이다. 
위에서 사용한 구조체 리터럴 예제에 플렉서블을 사용하면 에러가 난다.

그럼 어떻게 써야할까?


결국 동적으로 할당해서 써야한다..

게다가 배열의 추가용량까지 써줘야 한다. 이렇게.



그리고 배열의 특성 중 하나인 '길이' 도 없다.


체크불가.


도대체 왜 넣은 기능인지 모르겠다. 
불편하기만 하고 쓸모없다.



익명 구조체 (C11)

구조체 안에다가 이름없는 구조체를 사용할 수 있는 기능이다.

내부의 익명 구조체 멤버는 외부의 구조체 멤버와 같이 액세스를 할 수 있다.

사실 이건 별로 쓸모가 없다. 그냥 중괄호 빼고 합쳐버리지.

어쨌든 이렇게 쓸 수 있다.



익명 공용체 (C11)

익명 공용체는 익명 구조체와 동작방식이 유사하다. 하지만 더 쓸모가 있다.

가끔 비트나 메모리 갖고 장난치다보면 구조체에 공용체를 멤버로 집어넣을 때가 있는데, 
이러면 접근시 공용체의 이름까지 써야 하기 때문에 호출하기가 굉장히 귀찮아진다.

그럴때 이걸 사용하면 번거로움을 줄일 수 있다.




__Noreturn (C11)

함수가 반환하지 않고 종료가 됨을 명시한다.

대표적으로 프로그램을 종료하는 exit함수가 이것으로 수식을 하고 있다.

근데 뭐 직접 쓸 일은 별로 없을 것 같다.





_Static_assert (C11)

c++의 static_assert와 동일하다.

컴파일타임에 assert를 뱉는다.

첫번째 인자는 논리식을 받아서 참이면 수행하고, 거짓이면 컴파일을 중단하고 두번째 인자의 문자열을 뱉는다.

성공



실패



이중자/삼중자 (C99)

어떤 키보드는 C에서 필수적으로 사용되는 기호들을 제공하지 않기도 한다.

그래서 흔한 기호를 2~3개의 기호를 조합해서 대체할 수 있게끔 이중자와 삼중자라는 것을 제공한다.

삼중자는 ""보다 우선순위가 높지만 이중자는 그렇지 않다.

# => 삼중자: ??=    이중자: %:

[  => 삼중자: ??(     이중자: <:

]  => 삼중자: ??)     이중자: :>

{  => 삼중자: ??<    이중자: <%

}  => 삼중자: ??>    이중자: %>

\  => 삼중자: ??/

|   => 삼중자: ??!

^  => 삼중자: ??`

~ => 삼중자: ??-


인라인 함수 (C99)

키워드는 inline이고 사용방법은 c++과 동일하다.




2018. 8. 31.



설정

트랙백

댓글

[C] Q: 프로그램이 실행되자마자 꺼져요!

언어/C 2018. 12. 30. 20:43

가장 흔한 질문 중 하나인데, 원래 콘솔에서는 할일 다하면 바로 꺼지는 게 맞는 겁니다.

게다가 요즘 컴퓨터는 겁나게 빠르기 때문에 텍스트 처리 정도의 가벼운 작업 정도는 쉬시식 끝내버리죠. 그래서 그냥은 볼 새가 없습니다.

해결법 1 : Visual Studio를 사용중이라면 컨트롤+F5로 실행합니다. 그럼 꺼지지 않습니다.
Clion 같이 자체 콘솔을 내장하는 IDE를 사용하는 것도 좋습니다.

해결법 2 : windows.h헤더의 system함수로, 코드 마지막 부분, return 앞에 system("pause"); 라고 써줍니다. 
pause는 좀 멈춰있으라는 커맨드 명령어입니다.

해결법 3: 사실 방법은 많습니다. 그냥 꺼지지 않게만 하면 되기 때문에, return 앞 끝부분에다 비어있는 무한루프를 붙여도 되고, 의미없는 입력함수를 넣어도 됩니다..
이중에 제일 간단한건 getchar(); 입니다.




2018. 8. 27

설정

트랙백

댓글

[C] 함수 호출 규약.link

언어/C 2018. 12. 17. 11:45

함수 호출 규약(Function calling convention)
함수 시그너처에서, 리턴타입과 함수명 사이에 붙어서 함수의 작동 방식을 결정하는 것.
사실 대부분의 경우에서는 직접 명시할 필요가 별로 없음

https://ko.m.wikipedia.org/wiki/X86_호출_규약
위키피디아


http://sweeper.egloos.com/m/829514
친절함


http://qufl1999.egloos.com/m/2338204



http://blog.eairship.kr/m/254




결론.

stdcall
대부분의 경우에는 __stdcall이 약간 더 가볍고 빠름.
다른 별칭으로 CALLBACK, WINAPI, PASCAL, APIENTRY 등이 있음
가변인자 사용 불가

cdecl
가변인자를 사용하려면 __cdecl 사용 필요.
다른 별칭으로 WINAPIV 등이 있음
함수 디폴트 설정임

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

[C] C99~C11 조금 낯선 기능들  (0) 2018.12.30
[C] Q: 프로그램이 실행되자마자 꺼져요!  (0) 2018.12.30
[C] 쇼트 서킷으로 조건문 구현  (0) 2018.12.17
[C] sizeof 연산자에 대해서  (0) 2018.12.17
[C] 포인터란?  (0) 2018.12.17

설정

트랙백

댓글

[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=&num;
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

설정

트랙백

댓글