[C언어] #2 헷갈리는 문법 및 키워드 정리

Ilhoon·2022년 1월 23일
1
post-thumbnail

모든 변수들은 블럭의 시작에 선언되어야 한다.

함수

C언어는 함수를 사용하기 전에 함수를 선언해줘야 한다. (전방선언)

  • 선언되지 않은 함수를 바로 사용하면 컴파일 에러
  • 함수 반환형, 이름, 매개변수 자료형을 파일의 제일 위(혹은 헤더파일)에 미리 표기해둔다

한 줄에 있는 피연산자들은 평가순서를 보장하지 않는다

  • 함수 매개변수의 평가 순서는 컴파일러마다 다를 수 있다.

    /* add(), substract(), divide() 코드 생략*/
    int result = add(num1, num2) + substract(num1, num2) * divide(num1, num2);
    • 위와 같은 코드에서 어떤 함수가 먼저 실행될지? 알수없다.

    • 연산자 우선순위가 높은 함수가 먼저 호출될까? 관계없다.

    • 다음 코드의 실행 순서는 어떻게될까?

      if (++i || ++j && ++k)
      • ||, && 연산자는 시퀀스 포인트 (실행 순서를 보장해주는 포인트)
        1. 연산자 우선순위에 의해 &&조건에 괄호
          • if ( ++i || ( ++j && ++k) )
        2. ++i 실행
          • i = 1
          • if (true || (++ j && ++k) )
        3. short circuit 평가에 의해 true반환 종료
          • A or B 일때 A가 true이면 B를 볼 필요없이 true이다.
  • 한 함수의 매개변수들이 동일한 변수를 수정할 경우, 결과가 정의되지 않음

    • 연산자는 피연산자의 평가 순서를 보장하지 않는다.
    • 어떤 연산이 먼저 수행될지 알 수 없고, 동시에 비트를 건드려 값이 깨질 수도 있다.
    • 확실한 시퀀스 포인트 ;를 사용해서 명확히 순서를 정의하자.
    /* 안 좋은 사용 예 */
    int main(void){
        int num = 10;
        num = ++num + num++;
        printf("num : %d\n", num);
    }
    
    i = ++i + i++;
    i = i++ + 1;
    array[i] = i++;

전역 변수

  • 전역 변수는 어디서든 접근할 수 있다 (장점이자 단점)

  • 스택 메모리에 들어가지 않고 데이터 섹션에 들어간다.

    C 메모리 구조

    스택데이터코드

extern

  • 전역 변수를 A 파일에서 선언 후 B파일에서 사용하려고 하면 오류가 발생한다.
  • 왜냐하면 소스파일은 각각 따로 컴파일 되기 때문에 컴파일 과정에서 다른 파일에 있는 전역 변수를 알 방법이 없기 때문이다.
  • 이럴 때 사용할 수 있는 키워드가 바로 extern
  • extern키워드는 다른 파일에 정의된 전역 변수들을 사용할 수 있게 해준다.
  • B 파일에서 사용하려고 하는 변수 앞에 extern키워드를 붙여서 선언 하면 A 파일에 있는 전역변수에 접근할 수 있다.
  • 모든 파일에서 접근가능하도록 하려면 헤더에 extern 변수를 선언해주고 해당 헤더를 include해서 사용하면 된다.

static

  • 내 파일에 전역변수를 다른 파일에서 사용 못하도록 하고싶을 때 사용할 수 있는 키워드
  • extern 키워드 써서 접근하려고 해도 링커 오류 발생
  • 함수 내에 지역변수로 사용된 static 변수는 함수 내에서만 접근 가능하지만, 함수가 리턴되도 값이 초기화 되지 않는다.
  • static변수는 스택 메모리에 저장되지 않고 데이터 영역에 저장된다.
  • 전역 변수 뿐만 아니라 함수 앞에도 사용가능하다

const

상수를 선언할 때 붙이는 키워드로서 변수의 값을 변경할 수 없도록 한다.

  • 변경하지 말아야할 변수를 변경하는 오류를 방지할 수 있다.

    • 기본적으로 많이 사용해서 값이 의도하지 않게 변하는 것을 방지해주는 것이 좋다.
    • 함수의 매개변수에서도 사용이 가능하다.
    int calculate_risk(const int id){
    	int age = db_get_age(id);
        int amount;
        /* 코드 생략 */
        
        id += 2; /* 컴파일 오류 */
        
        /* 코드 생략 */
        
        amonut = db_get_deposit_amount(id);
        return amount;
    }

goto

코드를 실행 후 특정한 위치로 이동해서 코드를 실행시키고 싶을 때 사용하는 키워드

goto <label_name>;
/* */
<label_name>:
  • 반복문, 조건문도 내부적으로는 goto를 사용하는 코드
    • 어셈블리어를 까보면 거의 똑같이 동작
    • 반복문은 다시 처음으로 goto
    • 조건문은 특정 블록을 건너뛰고 goto
  • goto는 가독성을 떨어트리기 쉽기 때문에 사용을 주의해야 한다.
  • goto가 유용한 경우
    • 여러개의 루프를 한번에 탈출하고 싶을 때
    • 한 함수 안에 여러 조건문이 공통된 코드를 실행해야 할 때
      • 함수를 만들자니 유지보수의 문제, 함수를 안만들자니 코드 중복문제가 있을 때 유용
    • 실제 linux, apple 등 많은 프로그램 코드를 보면 goto를 사용하고 있다.
    • 동적메모리 할당 후 해제할 때
    • 항상 아래쪽으로만 점프하도록 사용하는 것이 좋다.
배열

C언어는 배열요소의 값을 알아서 초기화해주지 않는다. (변수도 마찬가지)

배열의 모든 요소 0으로 초기화하는 best practice

int nums[10] = {0, }		/* ,가 있어서 이해가 쉽고 가독성이 좋다. */

다차원 배열

아래의 두 배열은 실제 메모리 상에서 볼 때 똑같은 배열이다. 2차원 배열도 메모리 상에서는 순서대로 저장된다.

/* 2차원배열 */ 
int buffer[3][2];
int i;
int j;

for (i = 0; i < 3; i++){
    for (j = 0; j < 2; j++){
        buffer[i][j] = 0;
    }
}
/* 1차원 배열 */
int buffer[3 * 2];
int i;
int j;

for (i = 0; i < 3; i++){
    for (j = 0; j < 2; j++){
        buffer[i * 2 + j] = 0;
    }
}
profile
꾸준하게!

0개의 댓글