C (프로그래밍 언어) 편집하기


편집하면 당신의 IP 주소가 공개적으로 기록됩니다. 계정을 만들고 로그인하면 편집 시 사용자 이름만 보이며, 위키 이용에 여러 가지 편의가 주어집니다.

편집을 취소할 수 있습니다. 이 편집을 되돌리려면 아래의 바뀐 내용을 확인한 후 게시해주세요.

최신판 당신의 편집
30번째 줄: 30번째 줄:
* 사용자가 프로그램의 모든 부분을 관리해야만 한다.
* 사용자가 프로그램의 모든 부분을 관리해야만 한다.
:위에서 적은 장점은 곧 단점이기도 하다. 배열의 범위를 벗어나도 컴파일러는 오류를 내뱉지 않는다. 사용자가 메모리를 꼼꼼하게 관리하지 않으면 메모리 누수가 발생한다.
:위에서 적은 장점은 곧 단점이기도 하다. 배열의 범위를 벗어나도 컴파일러는 오류를 내뱉지 않는다. 사용자가 메모리를 꼼꼼하게 관리하지 않으면 메모리 누수가 발생한다.
* 문법은 단순하지만 언어 사용 기술은 단순하지 않다.
* 문법은 단순하지만 언어 사용 기술은 단순하지 않다.
:문법의 종류가 적다고 코딩이 쉬운 것은 아니다. 문법에서 지원하는 기능이 몇 개 없다는 건 바꿔 말하면 지원하지 않는 기능을 모두 손수 구현해서 사용한다는 뜻이 된다. 더불어 포인터와 관련되어 개발된 각종 트릭들은 C 언어를 본격적으로 사용할 때의 난이도를 대폭 상승시킨다.
:문법의 종류가 적다고 코딩이 쉬운 것은 아니다. 문법에서 지원하는 기능이 몇 개 없다는 건 바꿔 말하면 지원하지 않는 기능을 모두 손수 구현해서 사용한다는 뜻이 된다. 더불어 포인터와 관련되어 개발된 각종 트릭들은 C 언어를 본격적으로 사용할 때의 난이도를 대폭 상승시킨다.
* 상당히 보수적인 언어다.
* 상당히 보수적인 언어다.
:물론, C에도 최신 규격인 C99나 C11이 있다. 다만 C99나 C11 등 최신 규격이 발표됨에도 사람들이 대부분 ANSI C나 C89/90을 고집하고, C11에 이르러선 아예 있는지도 모르는 이유는 컴파일러 호환성 문제 등이 있기 때문이다. 기실, 현재까지도 생산성이 높지 않은 C를 쓴다는 것은 '''C를 쓸만한 당위성'''이 있는 경우라는 것인데, 이런 경우가 대부분 저수준 임베디드 같은 컴파일러 호환성이 낮은 분야나, 아니면 성능에 굉장히 민감해서 사람이 수동으로 제어를 해주는게 더 나을 수 있는 경우(거의 없어지기는 했다) 정도인데, 이런 분야에서 굳이 C99나 C11에 들어간 기능들이 추가적으로 더 필요하지도 않고, 보수성만 더 나쁘게 만들기 때문이다. 이런 점에서 ANSI C 등은 기준이 명확하고, 과거에 쭉 써온 많은 코드와 라이브러리, 인력 풀 등이 남아있기 때문에 C는 오히려 최신 규격보다 과거 규격이 훨씬 더 안정적이라 여겨지는 것이다. 하지만 [[C++]]과 같이 생산성도 중시하는 언어 같은 경우는 덜 보수적이다보니 최신 규격의 채용에 있어서도 C에 비해 훨씬 더 적극적인 경우가 많다.
:물론, C에도 최신 규격인 C99나 C11이 있다. 다만 C99나 C11 등 최신 규격이 발표됨에도 사람들이 대부분 ANSI C나 C89/90을 고집하고, C11에 이르러선 아예 있는지도 모르는 이유는 컴파일러 호환성 문제 등이 있기 때문이다. 기실, 현재까지도 생산성이 높지 않은 C를 쓴다는 것은 '''C를 쓸만한 당위성'''이 있는 경우라는 것인데, 이런 경우가 대부분 저수준 임베디드 같은 컴파일러 호환성이 낮은 분야나, 아니면 성능에 굉장히 민감해서 사람이 수동으로 제어를 해주는게 더 나을 수 있는 경우(거의 없어지기는 했다) 정도인데, 이런 분야에서 굳이 C99나 C11에 들어간 기능들이 추가적으로 더 필요하지도 않고, 보수성만 더 나쁘게 만들기 때문이다. 이런 점에서 ANSI C 등은 기준이 명확하고, 과거에 쭉 써온 많은 코드와 라이브러리, 인력 풀 등이 남아있기 때문에 C는 오히려 최신 규격보다 과거 규격이 훨씬 더 안정적이라 여겨지는 것이다. 하지만 [[C++]]과 같이 생산성도 중시하는 언어 같은 경우는 덜 보수적이다보니 최신 규격의 채용에 있어서도 C에 비해 훨씬 더 적극적인 경우가 많다.
77번째 줄: 79번째 줄:
   double complex x2 = 1.0 - 4.0 * I;
   double complex x2 = 1.0 - 4.0 * I;


   printf("x1 = %.2f + %.2fi\tx2 = %.2f %+.2fi\n",
   printf("x1 = %.2f + %.2fi\tx2 = %.2f %+.2fi\n",  
           creal(x1),
           creal(x1),  
           cimag(x1),
           cimag(x1),  
           creal(x2),
           creal(x2),  
           cimag(x2));
           cimag(x2));
}
}
126번째 줄: 128번째 줄:
* 자료형은 아래와 같다.
* 자료형은 아래와 같다.
* 저장 가능한 수의 범위는 2를 자료형의 비트 수로 제곱하면 나온다. short의 비트는 16비트 2<sup>16</sup>=65536 양수 음수로 나누면 -32768 ~ 32767 이 범위에 들어가는 숫자만을 저장할 수가 있다. short 앞에 unsigned를 입력하면 0에서 65535에 해당하는 숫자를 저장할 수 있는 자료형이 선언된다.
* 저장 가능한 수의 범위는 2를 자료형의 비트 수로 제곱하면 나온다. short의 비트는 16비트 2<sup>16</sup>=65536 양수 음수로 나누면 -32768 ~ 32767 이 범위에 들어가는 숫자만을 저장할 수가 있다. short 앞에 unsigned를 입력하면 0에서 65535에 해당하는 숫자를 저장할 수 있는 자료형이 선언된다.
* char: 하나의 문자를 저장한다. 비트 수는 8비트로 1바이트{{취소선|그러면 글자 여러 개는 어떻게 저장하는데?}} {{취소선|다 방법이 있음 아래에서 보셈}}
* char: 하나의 문자를 저장한다. 비트 수는 8비트로 1바이트{{취소선|그러면 글자 여러 개는 어떻게 저장하는데?}} {{취소선|다 방법이 있음 아래에서 보셈}}
<syntaxhighlight lang="c">
<syntaxhighlight lang="c">
207번째 줄: 210번째 줄:
* /: 나누기, 보통 쓰는 나누기 기호가 아니라 슬래시를 쓴다.
* /: 나누기, 보통 쓰는 나누기 기호가 아니라 슬래시를 쓴다.
* %: 나머지 연산. A%B라고 쓰면 A를 B로 나누었을 때의 나머지를 반환한다. 예를 들면 5%2 = 1 이런식으로. 당연히 만약 B가 A보다 더 크면 그냥 A를 반환한다.
* %: 나머지 연산. A%B라고 쓰면 A를 B로 나누었을 때의 나머지를 반환한다. 예를 들면 5%2 = 1 이런식으로. 당연히 만약 B가 A보다 더 크면 그냥 A를 반환한다.
* =: 대입연산자. 일상에서 사용하는 등호와 방식이 조금 다르다. 좌항에는 반드시 대입이 가능한 변수가 있어야 하고, 우항에는 변수에 대입할 값이 있어야 한다.
* =: 대입연산자. 일상에서 사용하는 등호와 방식이 조금 다르다. 좌항에는 반드시 대입이 가능한 변수가 있어야 하고, 우항에는 변수에 대입할 값이 있어야 한다.
* +=, -=, *=, /=, %=: A+=B라 할 때, A = A + B와 같다. (수학적으로는 말도 안되지만 프로그래밍 언어의 =는 수학의 등호와는 다르다. 변수 A의 자리에 A+B의 값을 대입한다고 봐야한다.) 예를 들어서, a=1; a+=2;를 하면 a가 3이 된다. -=는 이하도 해당 연산자 버전으로 마찬가지이다.
* +=, -=, *=, /=, %=: A+=B라 할 때, A = A + B와 같다. (수학적으로는 말도 안되지만 프로그래밍 언어의 =는 수학의 등호와는 다르다. 변수 A의 자리에 A+B의 값을 대입한다고 봐야한다.) 예를 들어서, a=1; a+=2;를 하면 a가 3이 된다. -=는 이하도 해당 연산자 버전으로 마찬가지이다.
272번째 줄: 276번째 줄:
</syntaxhighlight>
</syntaxhighlight>
*조건식이 참(또는 값이 1)이면 반복한다.
*조건식이 참(또는 값이 1)이면 반복한다.
*무한 반복
*무한 반복
<syntaxhighlight lang="c">
<syntaxhighlight lang="c">
283번째 줄: 288번째 줄:
</syntaxhighlight>
</syntaxhighlight>
*조건문 안에서 사용할 변수 정의, 반복 조건, 반복할 때만 실행할 코드까지 정의하기에 알맞다.
*조건문 안에서 사용할 변수 정의, 반복 조건, 반복할 때만 실행할 코드까지 정의하기에 알맞다.
*단순 반복
*단순 반복
<syntaxhighlight lang="c">
<syntaxhighlight lang="c">
298번째 줄: 304번째 줄:
===사용자정의변수===
===사용자정의변수===
C언어에서는 typedef 키워드로 사용자 변수 타입을 정의할 수 있다. 즉 typedef int bool; 을 이용해 int와 같은 성질을 갖는 자료형을 bool 자료형의 이름으로 사용 가능하다. 또한 여러 자료형과 변수를 한 단위로 묶기 위한 구조체와 공용체, 상수의 집합을 보다 알아보기 쉽게 정의할 수 있는 열거형 또한 사용자 정의 자료형으로 취급한다.
C언어에서는 typedef 키워드로 사용자 변수 타입을 정의할 수 있다. 즉 typedef int bool; 을 이용해 int와 같은 성질을 갖는 자료형을 bool 자료형의 이름으로 사용 가능하다. 또한 여러 자료형과 변수를 한 단위로 묶기 위한 구조체와 공용체, 상수의 집합을 보다 알아보기 쉽게 정의할 수 있는 열거형 또한 사용자 정의 자료형으로 취급한다.
*구조체
*구조체
<syntaxhighlight lang="c">
<syntaxhighlight lang="c">
382번째 줄: 389번째 줄:
우선 주소 참조와 관련된 두 가지 연산자를 알아둘 필요가 있다. 이 둘은 이미 위에서 모두 나왔다. 그렇다, &과 *이다. 이 두 연산자의 사용법은, 변수명 앞에 이 두 연산자를 붙이는 것이다. 이 둘의 기능은 정반대라고 생각하면 되는데, &를 사용할 경우 결과는 그 변수가 저장되어 있는 주소가 되고, *를 사용할 경우 결과는 그 변수에 저장된 주소값에 위치한 변수에 저장된 값이 된다. 즉 간단히 말하자면 *의 경우 변수에 저장된 주소값을 통해 다른 변수를 참조하게 해주고, &의 경우 그 반대의 기능을 수행하는 것이다. 그래서 *을 참조 연산자, &의 이름을 역참조 연산자라고 부르기도 한다.
우선 주소 참조와 관련된 두 가지 연산자를 알아둘 필요가 있다. 이 둘은 이미 위에서 모두 나왔다. 그렇다, &과 *이다. 이 두 연산자의 사용법은, 변수명 앞에 이 두 연산자를 붙이는 것이다. 이 둘의 기능은 정반대라고 생각하면 되는데, &를 사용할 경우 결과는 그 변수가 저장되어 있는 주소가 되고, *를 사용할 경우 결과는 그 변수에 저장된 주소값에 위치한 변수에 저장된 값이 된다. 즉 간단히 말하자면 *의 경우 변수에 저장된 주소값을 통해 다른 변수를 참조하게 해주고, &의 경우 그 반대의 기능을 수행하는 것이다. 그래서 *을 참조 연산자, &의 이름을 역참조 연산자라고 부르기도 한다.
이제 포인터에 대한 기본적인 내용을 익혔으니 그 응용으로 넘어가 보자.
이제 포인터에 대한 기본적인 내용을 익혔으니 그 응용으로 넘어가 보자.
*array를 이용한 string
*array를 이용한 string
<syntaxhighlight lang="c">
<syntaxhighlight lang="c">
454번째 줄: 462번째 줄:
</syntaxhighlight>
</syntaxhighlight>
이 코드의 실행 결과는 어떻게 될까? 이미 우리는 윗 문단을 통해 첫 번째 값은 pa에 저장된 a의 주소를 통해 참조한 a의 값인 5임을 알고 있다. 다중 포인터에서도 똑같은 논리가 그대로 적용되는데, 2번째 값의 경우 ppa에 저장된 pa의 주소를 통해 참조한 pa의 값, 그러니까 a의 주소가 출력될 것이다. 3번째 값은 ppa에서 2번 참조한 것이 출력되는데, 이것은 2번째 경우에서 참조를 한 번 더 실행한 경우를 뜻하므로, ppa에 저장된 pa의 주소를 통해 pa를 참조하고, pa에 저장된 a의 주소를 통해 a를 참조하여 첫 번째 값과 같은 a의 값인 5가 출력될 것이다.
이 코드의 실행 결과는 어떻게 될까? 이미 우리는 윗 문단을 통해 첫 번째 값은 pa에 저장된 a의 주소를 통해 참조한 a의 값인 5임을 알고 있다. 다중 포인터에서도 똑같은 논리가 그대로 적용되는데, 2번째 값의 경우 ppa에 저장된 pa의 주소를 통해 참조한 pa의 값, 그러니까 a의 주소가 출력될 것이다. 3번째 값은 ppa에서 2번 참조한 것이 출력되는데, 이것은 2번째 경우에서 참조를 한 번 더 실행한 경우를 뜻하므로, ppa에 저장된 pa의 주소를 통해 pa를 참조하고, pa에 저장된 a의 주소를 통해 a를 참조하여 첫 번째 값과 같은 a의 값인 5가 출력될 것이다.
이를 통해 C++의 Call By Reference를 비슷하게 흉내내는것 또한 가능하다.
<syntaxhighlight lang="c">
// 할당된 메모리 주소는 이해를 돕기 위한 임의의 값.
#include <stdlib.h>
int AllocMe(void **ptr, size_t sz)
{
  if(!*ptr) // 3. **ptr은 2번 *myInt 의 주소인 0x01를 가르키고 있으므로 이를 dereferencing 하면 *myInt가 가르키고 있는 값인 NULL값을 읽음.
  {
    *ptr = malloc(sz); // 4. *myInt의 주소의 값에 malloc(); 1.번의 myInt는 malloc()이 반환한 0x10주소를 저장. (메모리 주소 0x1에는 0x10이라는 값이 들어 있음)
    return *ptr ? 1 : 0; // 5. *ptr에 malloc()이 반환한 주소(0x10)가 들어있다.
  }
}


int main()
그러면 다중 포인터는 대체 어디에 쓰는가? 다중 포인터는 보통 다차원 동적 배열을 만들 때 쓴다. [[작성중]]
{
  int *myInt = NULL; // 1. NULL 포인터 - *myInt := 스택 메모리 0x01, 0x01에 저장된 값은 0 (NULL)
  if(AllocMe(&myInt, 8)) // 2. 1번의 포인터가 위치한 주소를 전달 (Call by value) 주소값인 0x01이 전달
  {
    free(myInt); // 6. AllocMe()에서 할당한 메모리 주소(0x10)를 해제
    myInt = NULL; // myInt (0x1)의 값은 다시 NULL
    return 0;
  }
  return -1;
}
</syntaxhighlight>
 
그러면 다중 포인터는 대체 어디에 쓰는가? 다중 포인터는 보통 다차원 동적 배열을 만들 때 쓴다.
이 다차원 배열중 흔히 볼 수 있는 예시는 프로세스 진입 지점인 int main()에서 찾을 수 있다.
<syntaxhighlight lang="c">
#include <stdio.h>
 
int main(int argc, const char **argv)
{
  for (int i = 0; i < argc; ++i)
  {
    printf("%d: %s\n", i, argv[i]); // *(argv + 1)과 동일
  }
}
</syntaxhighlight>
C언어에서 문자열(String)또한 단순 1차원 배열에 불과하지만 이런 배열을 포함하는 또다른 배열을 만드는 것이 가능하다.
이를 통해 char* 는 배열의 주소를 담고 있지만 이 배열의 주소를 담고 있는 또다른 포인터를 만듬으로써 2차원의 배열을 만드는 것이 가능하다. 이를 통해 3차원은 물론 그 이상의 다차원 배열을 만드는 것 또한 가능하다.


====함수포인터====
====함수포인터====
함수 포인터는 말 그대로 함수를 가르키는 포인터이며 이 값은 함수 심볼의 진입 주소가 된다.
주로 플러그인과 같이 동적 라이브러리에서 링킹 없이 동적으로 심볼만 가져다 쓰는 상황에서 주로 사용되며 C에서 객체지향을 흉내낼 때도 사용하는것이 가능하다.
문법은 일반 함수 선언과 비슷하다.
<syntaxhighlight lang='c'>
<반환 자료형> (*<함수 포인터 이름>)(<매개변수>);
</syntaxhighlight>
예를 들어 void* MyFunction(int, void*) 꼴의 함수를 담을 수 있는 함수 포인터는 다음과 같이 선언할 수 있다.
<syntaxhighlight lang='c'>
void* (*MyFunction_ptr)(int, void*);
</syntaxhighlight>
<syntaxhighlight lang='c'>
void* (*MyFunction_ptr)(int, void*) = &MyFunction;
MyFunction_ptr(0, NULL); // MyFunction(0, NULL)과 동일
</syntaxhighlight>
===매크로===
===매크로===
<syntaxhighlight lang='c'>
#define <매크로 이름> <매크로 값(옵션)>
</syntaxhighlight>
변수나 상수와 달리 매크로는 단순히 컴파일단계 중 전처리기에 의해 값이 단순히 치환된다.
예를 들어
<syntaxhighlight lang='c'>
#define SIZE 64;
int myArray[SIZE];
</syntaxhighlight>
는 전처리기에 의해 SIZE 부분이 64로 치환되며 결과적으로 int myArray[64]; 가 된다.
매크로 값은 생략하는것이 가능한데 보통 생략하는 경우 해당 매크로는 0이 아닌 값(true)로 바뀐다. 이를 통해 다음과 같이 응용할 수 있다.
<syntaxhighlight lang='c'>
#define BUILD_PRINT_DEBUG_MESSAGE
...
#ifdef BUILD_PRINT_DEBUG_MESSAGE
printf("[DEBUG] Print Debug Message!\n");
#endif // BUILD_PRINT_DEBUG_MESSAGE
</syntaxhighlight>
또한 매크로는 코드의 일부가 될 수 있다.
<syntaxhighlight lang='c'>
#include <stdio.h>
#define PROCEDURE(x) int x
#define PRINTHELLOWORLD printf("Hello World!\n");
#define BEGIN {
#define END }
PROCEDURE(main) // int main
(int argc, const char *argv[])
BEGIN // {
    PRINTHELLOWORLD; // printf("Hello World!\n);
    return 0;
END // }
</syntaxhighlight>


{{각주}}
{{각주}}
리브레 위키에서의 모든 기여는 크리에이티브 커먼즈 저작자표시-동일조건변경허락 3.0 라이선스로 배포됩니다(자세한 내용에 대해서는 리브레 위키:저작권 문서를 읽어주세요). 만약 여기에 동의하지 않는다면 문서를 저장하지 말아 주세요.
글이 직접 작성되었거나 호환되는 라이선스인지 확인해주세요. 리그베다 위키, 나무위키, 오리위키, 구스위키, 디시위키 및 CCL 미적용 사이트 등에서 글을 가져오실 때는 본인이 문서의 유일한 기여자여야 하고, 만약 본인이 문서의 유일한 기여자라는 증거가 없다면 그 문서는 불시에 삭제될 수 있습니다.
취소 편집 도움말 (새 창에서 열림)

| () [] [[]] {{}} {{{}}} · <!-- --> · [[분류:]] · [[파일:]] · [[미디어:]] · #넘겨주기 [[]] · {{ㅊ|}} · <onlyinclude></onlyinclude> · <includeonly></includeonly> · <noinclude></noinclude> · <br /> · <ref></ref> · {{각주}} · {|class="wikitable" · |- · rowspan=""| · colspan=""| · |}