C언어 핵심

C언어에서 정말 핵심이 되는 부분만 정리해봤습니다.

<변수>
C언어는 근본적으로 메모리와 그 메모리 내의 값을 다루는 언어입니다. 일단 기본적으로 메모리를 생성하는 법을 배워봅시다.
 1. 변수(variable)
  값(value)을 담을 수 있는 특정 메모리 공간.
 2. 자료형
  변수를 생성할 때 변수의 크기를 정할 수 있도록 해준다.
  참고로 자료형에는 여러 종류가 있다. 우선 배우는 단계에선 char, int, float 정도만 알아두면 된다.
  char : 1바이트(byte)의 메모리 공간을 할당한다. 일반적으로 문자 및 문자열을 저장하는 데에 쓰인다.
  int : 4바이트의 메모리 공간을 할당한다. 일반적으로 정수를 저장하는 데에 쓰인다.
  float : 4바이트의 메모리 공간을 할당한다. 일반적으로 실수를 저장하는 데에 쓰인다.
 3. 변수 이름
  변수 선언시에 사용자가 변수에 임의로 이름을 붙일 수 있다.
 3. 변수 선언
  변수를 다루기 위해 변수를 생성하는 것을 말한다.
  * 문법
  자료형 변수이름;
  * 예시
    int a;    // int형 변수 a가 생성되었다.
    char b;    // char형 변수 b가 생성되었다.


<연산자>
결국 C언어의 근본 원리는 logic(논리)입니다. 논리를 세우기 위해선 기본적인 수리적 계산이 필요하며, 연산자는 그러한 계산의 토대가 됩니다.
 1. 산술 연산자
  + : 왼쪽 피연산자와 오른쪽 피연산자 간의 덧셈 연산을 수행한다.
  - : 왼쪽 피연산자와 오른쪽 피연산자 간의 뺄셈 연산을 수행한다.
  * : 왼쪽 피연산자와 오른쪽 피연산자 간의 곱셈 연산을 수행한다.
  / : 왼쪽 피연산자와 오른쪽 피연산자 간의 나눗셈 연산을 수행한다.
  % : 왼쪽 피연산자에서 오른쪽 피연산자를 나누었을 때, 그 나머지를 구하는 연산을 수행한다. 
    ex) 7%3;    // 7에서 3을 나눈 뒤에 그 나머지인 1이 결과 값으로 나올 것이다.
  ++ : 왼쪽 또는 오른쪽 피연산자의 값을 1 증가시킨다.    // 왼쪽을 증가시키는 경우, 같은 문장 내 모든 연산을 수행한 후 왼쪽 피연산자의 값을 1 증가시킨다.
  -- : 왼쪽 또는 오른쪽 피연산자의 값을 1 감소시킨다,    // 왼쪽을 증가시키는 경우, 같은 문장 내 모든 연산을 수행한 후 왼쪽 피연산자의 값을 1 감소시킨다.
 2. 대입 연산자
  = : 오른쪽 피연산자의 값을 왼쪽 피연산자 값에 대입시킨다. ex) int a=3;    // int형 변수 a에 정수형 값 3을 대입시킨다.
                                                                                        char b='a'    // char형 변수 b에 문자 값 a를 대입시킨다.(문자는 따옴표를 붙여준다.)
 3. 관계 연산자
 관계 연산자는 그 자체로 어떤 변화를 발생시키지는 않지만, 조건 식을 세울 때 다루게 된다.
  < : 오른쪽 피연산자가 왼쪽 피연산자보다 더 크다는 것을 표현.
  > : 왼쪽 피연산자가 오른쪽 피연산자보다 더 크다는 것을 표현.
  >= : 왼쪽 피연산자가 오른쪽 피연산자보다 크거나 같다는 것을 표현.
  <= : 오른쪽 피연산자가 왼쪽 피연산자보다 크거나 같다는 것을 표현.
  == : 왼쪽 피연산자와 오른쪽 피연산자가 같다는 것을 표현.    // = (대입 연산자) 와 헷갈리지 말자.
  != : 왼쪽 피연산자와 오른쪽 피연산자가 다르다는 것을 표현.
  && : 왼쪽 피연산자와 오른쪽 피연산자가 둘 다 참이라는 것을 표현. ex) (a==2) && (b>=3)    // a가 2와 같고, b가 3보다 크거나 같을 때 참이라는 것을 표현.
  || : 왼쪽 피연산자와 오른쪽 피연산자 중 하나라도 참이라는 것을 표현. ex) (a==2) || (b>=3)    // a가 2와 같거나 b가 3보다 크거나 같을 때 참이라는 것을 표현.
  ! : 오른쪽 피연산자가 참이면 거짓, 거짓이면 참으로 바꾼다. ex) !(a==2)    // a가 2와 같을 때 거짓이라는 것을 표현.


<제어문>
규칙적인 문장을 여러 번 쓰기 귀찮아서 쓰는 것이 for문이고, 미연시처럼 사용자가 뭘 선택했냐에 따라 실행에 차이를 두게 만드는 것이 if문입니다. 
 1. for문
  특정 조건에 부합할 때, 특정 행동을 반복시키기 위해 사용한다.
  * 실행 순서
   ① 초기문으로 반복할 변수 선언. ② 조건문에서 조건에 맞는 지 확인. ③ 반복 내용 실행. ④ 증감문 실행.
   그 이후 2~4의 내용을 계속해서 반복하다가 조건에 맞지 않는 순간이 오면 for문에서 빠져나간다.
  * 문법
    for(초기문 ; 조건문 ; 증감문)
    {
        반복 내용
    }
  * 예시
    int j=0;
    for(int i=0 ; i<2 ; i++)
    {
        j = i + i;
    }
    // ① int i가 선언되고 0이 대입되었다. ② 조건문에서 i<2이므로 조건에 들어 맞는다. ③ j = i + 1을 실행하여 j가 1이 된다. ④ i의 값을 1 증가시켜 i가 1이 된다.
        ⑤ 조건문에서 i<2이므로 조건에 들어 맞는다. ⑥ j = i + 1을 실행하여 j가 2가 된다. ⑦ i의 값을 1 증가시켜 i가 2가 된다.
        ⑧ 조건문에서 i<2이므로 조건에 맞지 않는다. for문을 빠져나간다.
  * continue : 반복문 내에 continue; 를 적으면 나머지 내용을 무시하고 다음번 조건문을 검토한다.
  * break : 반복문 내에 break; 를 적으면 나머지 내용을 무시하고 for문에서 빠져나간다. 
  
 2. if문
  특정 조건에 부합할 때, 특정 행동을 실행시키기 위해 사용한다.
  * 실행 순서
   ① 조건문에서 조건에 맞는 지 확인. ② 실행 내용 실행. ③ 실행한 뒤에 if문에서 빠져나간다.
  * 문법
    if(조건문)
    {
        실행 내용
    }
  * 예시
    int i=0;
    int j=0;
    if(i<2)
    {
        j++;
    }
    // ① 조건문에서 i<2이므로 조건에 들어 맞는다.  ② j++을 실행하여 j가 1이 된다. ③ 실행한 뒤에 if문에서 빠져나간다.


<함수>
수학에서의 함수를 생각하시면 이해가 쉽습니다. y=f(x)에서 f는 함수이름, x는 인자, y는 리턴 값 이라고 생각하시면 됩니다.
단지 C언어에서 값을 처리할 때는 자료형이 중요하므로 인자와 리턴 값 앞에 자료형을 명시해주는 것 말고는 차이가 없습니다.
C언어에서 어떤 함수의 구조를 만드는 것을 '함수 정의'라고 하며,
함수 정의를 하고자 할 때는 다음과 같은 형식으로 해주시면 됩니다.
 * 문법
  리턴자료형 함수이름(인자자료형1 인자이름1, 인자자료형2 인자이름2 ...)
  {
      함수 내용
  }
 * 예시
  int add(int a, int b)
  {
     return a+b;
  }
  // a에 int형 값을 인자로 받고, b에 int형 값을 인자로 받는다. 그 후 a+b를 연산한 뒤 a+b의 값을 int형으로 리턴한다.

그렇다면 만든 함수를 사용하고자 할때는 어떻게 해야 할까요?
어떤 만들어진 함수를 사용하는 행위를 '함수 호출'이라고 하며,
함수 호출을 하고자 할 때는 다음과 같은 형식으로 해주시면 됩니다.
 * 문법 
  함수이름(인자이름1, 인자이름2);
 * 예시
  add(3,5);    // add(3,5)의 결과 값은 위의 add 함수 정의에 의해서 정수 8이 리턴된다.


<지역>
C언어에서 {와 }사이는 하나의 지역입니다.
if문이나 for문에서 쓰이는 {와 }도 하나의 지역을 설정하여 그 내부의 제어문이 실행되도록 하는 원리입니다.
어떤 지역 내에 변수를 생성하면 그 생성된 변수를 지역 변수(local variable)라고 부릅니다.
지역 변수는 자신이 생성된 지역 내에서만 사용되어질 수 있고, 외부에서 설령 똑같은 이름의 변수가 있다하더라도 이 변수와는 별개의 변수입니다.
또한 지역 변수는 자신이 태어난 지역에서 벗어나게 되면 메모리 공간을 잃고 사라집니다.

어떤 지역 내에서 생성된 것이 아닌 변수를 전역 변수(global variable)라고 부릅니다.
전역 변수는 모든 지역에서 사용되어질 수 있으나, 특정 지역에서 자신과 똑같은 이름의 변수가 있다면 그 지역에 한해서는 무시됩니다.
또한 전역 변수는 프로그램 자체가 종료되면 메모리 공간을 잃고 사라집니다.


<배열>
작은 프로그램을 하나 만들더라도 메모리 공간은 무수히 많이 필요하게 됩니다.
RPG게임을 예로 들자면,
int hp, int mp, float exp, unsigned int level, char *character_name ... 이외에도 우리 눈엔 보이지 않지만 여러 인터페이스들을 위해서 수많은 변수가 필요합니다.
이런 변수들을 일일이 만들기도 번거롭고, 또 일일이 다루기도 번거로워서 만들어진 개념이 배열(array)입니다.
배열을 통해 연관성있는 변수들을 한 번에 만들고, 한 번에 처리할 수 있습니다.
 * 문법
  자료형 변수이름[크기];    // 변수이름[0], 변수이름[1], ..... , 변수이름[크기-1] 의 변수들을 한번에 생성시킨다.
 * 예시
  int a[5];    // int형 변수 a[0], a[1], a[2], a[3], a[4]가 생성되었다.
  float b[3];    // float형 변수 b[0], b[1], b[2]가 생성되었다.
  for(int i=0 ; i<5 ; i++)
  {
      a[i] = i + 1;
  }
  // a[0]에 1이 대입되고, a[1]에 2가 대입되고, a[2]에 3이 대입되고, a[3]에 4가 대입되고, a[4]에 5가 대입되고 for문에서 빠져나간다.

또한 char형 배열을 이용하여 문자열을 다룰 수 있습니다.
문자열은 쌍따옴표를 붙여줘야하고, 마지막 배열 요소에 자동으로 널문자(\0)가 붙습니다.
 ex) char a[4] = "abc";    // a[0]에 a가 들어있고, a[1]에 b가 들어있고, a[2]에 c가 들어있고, a[3]에 \0가 들어있다.


<포인터>
포인터(pointer)는 그 이름대로 '가리키는 자' 입니다.
포인터의 값이 가변적이면 '포인터 변수', 불변하면 '포인터 상수', 함수를 가리키면 '함수 포인터' 라고 합니다.
가장 중요한 개념은 포인터 변수(pointer variable)인데 이 포인터 변수도 결국 변수이기 때문에 특정 메모리 공간을 가지고 있습니다.
이 메모리 공간에는 특정 메모리 공간의 '주소'가 들어있습니다.
이 주소를 따라가면 결국 어떤 메모리 공간이 나오는데 이것을 포인터 변수가 특정 메모리 공간을 '가리킨다'라고 표현합니다.
주소(address)라는건 결국 컴퓨터 메모리 내의 메모리 공간의 위치 정보를 말하는 것이기 때문에 그 어떤 상황에서도 주소를 이용하면 메모리 공간에 접근이 가능합니다.
예를 들면, int a; 는 int형 변수 a를 생성한다는 뜻입니다. 같은 지역에선 a에 값을 넣고 빼고 할 수 있지만, 컴파일러가 함수 호출때문에 함수 정의를 보고 있는 경우에는 다른 지역에 있기 때문에 a에 접근할 수가 없습니다. 하지만 포인터 변수를 인자에 넣어서, a의 주소를 알 수 있게 된다면? 다른 지역일지라도 a에 접근할 수가 있게 됩니다.

포인터 변수를 선언하는 방법 : 자료형 *변수이름;    ex) int *a;    // int형 포인터 변수 a가 생성되었다. a에는 int형 변수의 주소 값을 담을 수 있게 됩니다.
포인터와 관련된 연산자 :
& : 오른쪽 피연산자의 주소값을 리턴합니다. ex) &a    // a의 주소값을 리턴합니다.
* : 오른쪽 피연산자가 가리키는 값을 리턴합니다. ex) *a    // a 내부의 주소가 가리키는 특정 메모리 공간 내부의 값을 리턴합니다.

참고로 배열에서 변수의 이름을 '배열 이름'이라고 하며, 배열 이름은 '포인터 상수'입니다.
그렇다면 배열 이름은 무엇을 가리키는가? 배열이름[0]의 메모리 공간을 가리킵니다.
그리고 배열은 a[0]부터 a[크기-1]까지 모든 배열 요소가 메모리 공간 내에서 빈틈없이 연속적으로 생성됩니다.
즉, int a[3]; 일 때 a와 a[0]의 주소값이 같고, a[1]의 주소값은 a[0]의 주소값+4이고, a[2]의 주소값은 a[0]의 주소값+8 인 동시에 a[1]의 주소값+4가 됩니다.
그리고 char형 포인터 변수를 이용해 문자열을 다룰 수 있습니다.
 ex) char *p = "abc";    // abc라는 문자열을 특정 메모리 공간에 넣고, 그 주소 값을 p에 대입시킨다.

또한 포인터는 덧셈과 뺄셈이 가능합니다.
일반적으로는 다음과 같이 쓰입니다.
*(포인터변수 + 인덱스값)    // 포인터 변수 안에 담긴 주소 값에서 시작해서 포인터 변수가 가리키는 대상의 자료형의 크기 x 인덱스 값 만큼의 위치에 있는 메모리 공간의 값을 리턴합니다.
예를 들면, int a[3]; a[1] = 1; 의 두 문장을 실행시키면 a[1]의 메모리 공간에는 1이 들어있게 됩니다.
int *p = &a; *(p+1) = 1; 의 두 문장을 실행시키면 먼저 a의 주소가 p에 담기게 되고, 두번째 문장에서는 p에 담긴 주소 값(a의 주소 값)에서 시작해서 p가 가리키는 대상의 자료형(int)의 크기(4) x 인덱스 값(1) 만큼의 위치에 있는 메모리 공간(a[1])을 리턴하고, 그 공간에 1을 대입하여 a[1]의 메모리 공간에는 역시 1이 들어있게 됩니다.


<동적 할당>
컴퓨터 메모리 구조상 프로그램 실행 중에 사용자가 어떤 값을 입력했을 때 그 크기만큼의 배열을 만들어내는 것은 불가능합니다.
그래서 나온 개념이 동적 할당입니다. 동적 할당은 배열과 분명 차이가 있지만 구조 자체는 배열과 큰 차이가 없습니다.
일반적으로 다음과 같이 사용됩니다.
자료형 *변수이름 = (자료형*)malloc(sizeof(자료형*크기));    // 자료형 변수이름[크기]; 와 비슷한 문장입니다.
ex) int *p = (int*)malloc(sizeof(int*3));    // int a[3]; 와 비슷한 문장입니다.

그리고 동적할당한 메모리의 사용이 끝난 후에는 메모리 누수를 방지하기 위해 반드시 할당을 해제해줘야 합니다.
free 변수이름;
ex) free(p);

댓글

이 블로그의 인기 게시물

iOS 아이폰용 앱 개발을 위한 디자인시, 디자이너가 참고 해볼만한 사항들

스냅드래곤 기반 크롬북, ‘트로그도어’ 개발 중

[펌] '악마는 프라다를 입는다'의 진짜 명대사