본문 바로가기
~ 2024.03/C 언어

[C 언어 기초] C 언어 문법 훑어보기 - 데이터 타입, 제어문, 함수, 포인터

by Monett 2023. 12. 15.
반응형

K.N.King 의 C Programming - A Modern Approach 를 공부하며 내용을 정리한 글 입니다.
GPT의 답변을 기준으로 C 언어 문법을 훑어보는 글입니다.
현재 블럭의 내용은 작성자의 의견 혹은 생각이며, 틀린 내용이 있을 수 있습니다. 지적 감사드립니다.

기본 문법

  • C 는 절차 지향적 프로그래밍 언어로, 코드는 함수로 구성됨.
  • 세미콜론 (;)으로 문장이 끝남.

데이터 타입

정수형 데이터 타입

16비트에서의 정수형

타입 최솟값 최댓값
short int -32,768 32,767
unsigned short int 0 65,535
int -32,768 32,767
unsigned int 0 65,535
long int -2,147,483,648 2,147,483,647
unsigned long int 0 4,294,967,295

32비트에서의 정수형

타입 최솟값 최댓값
short int -32,768 32,767
unsigned short int 0 65,535
int -2,147,483,648 2,147,483,647
unsigned int 0 4,294,967,295
long int -2,147,483,648 2,147,483,647
unsigned long int 0 4,294,967,295

64비트에서의 정수형

타입 최솟값 최댓값
short int -32,768 32,767
unsigned short int 0 65,535
int -2,147,483,648 2,147,483,647
unsigned int 0 4,294,967,295
long int -9,223,372,036,854,775,808 9,223,372,036,854,775,807
unsigned long int 0 18,446,744,073,709,551,615

10진수

0~9까지의 수로 이루어진다. 

15 255 32767

8진수

0~7까지의 수로 이루어지며, 0으로 시작한다. 

017 0377 077777

16진수

0~9까지의 수와 A부터 F까지의 영문자로 이루어지며, 0x로 시작한다.

0xf 0xff 0x7fff (소문자와 대문자는 동일)

소수형 데이터 타입

IEEE 표준에서의 소수형

타입 최소 양수값 최댓값 정밀도
float 1.17549 * 10-38 3.40282 * 1038 소수점 이하 6자리
double 2.22507 * 10-308 1.79769 * 10308 소수점 이하 15자리

문자형 데이터 타입

C의 문자형 데이터 타입은 char형이다.

char형 변수에는 문자 하나를 할당할 수 있다.

char ch = 'a';

 

C에서 문자는 값이 작은 정수와 같다.

형정의 (type definition)

프로그래머는 형정의을 통해 사용자 정의 데이터 타입을 만들 수 있다.

typedef int Bool;

 

형정의를 하게되면 컴파일러는 자신이 이해할 수 있는 타입 목록에 Bool을 추가하게 된다.

이제 Bool 을 변수 선언, 변환식 등지에 내장된 타입처럼 사용할 수 있다.

(컴파일러는 Bool을 int의 동의어로 취급한다.)


구조체(structure)

구조체는 서로 다른 형을 가질 수도 있는 값(구성원. member)의 집합이다.

struct {
    int number;
    char name[NAME_LEN + 1];
    int on_hand;
} part;

 

위 구문에서 타입은 struct {...}이고, part는 해당 타입의 변수이다.

 

구조체 변수의 구성원은 다음과 같이 접근할 수 있다.

part.number = 258;
part.on_hand++;

 

그러나 위처럼 선언하면 동일한 타입의 구조체를 선언할 때 어려움이 생길 수 있다.

특히, 같은 구조체 정보를 선언한다고 해도 호환 가능한 형이 아니게 된다.

 

이러한 문제를 해결하기 위해 구조체의 타입을 나타낼 수 있는 이름을 정의하여야 한다.

C에서는 두 가지 방법으로 정의할 수 있다.

구조체 태그 선언

struct part {
    int number;
    char name[NAME_LEN + 1];
    int on_hand;
};

 

이후에 part 타입의 변수를 선언할 수 있다. (물론 즉시 선언도 가능하다.)

struct part part1;

구조체 형정의

typedef struct {
    int number;
    char name[NAME_LEN + 1];
    int on_hand;
} Part;

 

이후에 Part 타입의 변수를 선언할 때는 struct를 붙이지 않아도 된다.

Part part1;

 

※ 연결리스트(Linked list)처럼 구조체 내부에 해당 구조체 타입의 변수가 있는 경우에는 구조체 태그를 사용한다.


제어 구조

조건문

if문

if ( expression ) statement

if-elseif-else문

if ( expression1 )
{
    statements
} else if ( expression2 )
{
    statements
} else
{
    statements
}

조건 표현식 (삼항 연산자)

expression1 ? expression2 : expression3

반복문

while문

while ( expression ) statement

do문

do statement while ( expression );

for문

for ( initialization; condition; update) statement

함수

함수는 반환 타입, 함수 이름, 매개변수 목록, 함수 몸체로 구성된다.

return-type function-name ( parameters )
{
    declarations
    statements
}

 

함수 호출은 함수 이름과 괄호로 둘러쌓인 입력 변수의 목록으로 이루어져 있다.

get_average(x, y);
// 함수이름(매개변수1, 매개변수2, ...);

 

함수 선언

int main(void)
{
    get_data();
}

double get_data()
{
    ...
}

 

위 코드처럼 main 함수에서 다른 함수를 호출하지만, 해당 함수의 선언이 main 함수보다 아래에 있을 경우에 컴파일러는 get_data 함수가 int형을 반환한다고 가정한다. (컴파일러는 호출시점에 get_data 함수의 매개변수와 반환형을 알 수 없다.)

 

이를 암시적 선언(implicit delaration)이라고 부른다.

 

이러한 문제를 방지하기 위해 함수를 모든 호출 이전에 정의해줄 수 있지만, 언제나 가능한 방법이 아니며 가독성을 해치게 된다. 따라서 우리는 호출하기 전에 선언하고, 이후에 정의해줄 수 있다.

double get_data();

int main(void)
{
    get_data();
}

double get_data()
{
    ...
}

 

이렇게 하면 컴파일러는 정상적으로 get_data 함수가 double 타입의 값을 반환할 것을 알 수 있다.


포인터

https://developnerror.tistory.com/28

 

프로그램의 모든 변수들은 메모리에서 한 개 이상의 바이트를 차지한다.

첫 번째 바이트의 주소를 변수의 주소라고 부르는데, 이를 저장하기 위한 변수가 포인터 변수이다.

 

i 변수의 주소를 포인터 변수 p에 저장하면 p는 i를 "가리킨다"고 말한다.

int i = 15;

int *p = &i;

 

위 코드는 정수형 변수 i를 선언하고, 15를 할당한다.

 

이후 정수형 변수를 가리키는 포인터 변수 p를 선언한 후, i의 주솟값(&i)를 할당한다.

 

이제 p는 i를 가리키며, 아래와 같이 가리키는 주소의 값에 접근할 수 있다.

printf("%d", *p); // 15

동적 메모리 할당

https://developnerror.tistory.com/31

 

포인터를 활용해 동적으로 메모리를 할당할 수 있다.

 

동적으로 메모리를 할당하기 위해 우리는 <stdlib.h> 헤더에 정의된 세가지 메모리 할당 함수 중 한 가지를 사용하면 된다.

  • malloc : 메모리를 할당한다. (초기화하지 않는다)
  • calloc : 메모리를 할당하고 초기화한다.
  • realloc : 이전에 할당한 메모리의 크기를 재설정한다.
malloc(n);

 

위 함수를 호출하면 n 크기만큼 메모리를 할당하고, 할당된 메모리를 가리키는 void*를 반환한다.

 

만약 메모리가 충분하지 않아 실패한다면, 함수는 널 포인터를 반환한다.

널 포인터는 "아무것도 가리키지 않는 포인터"를 의미하며, NULL이라는 이름의 매크로로 정의되어 있다.

 

다음과 같이 메모리 할당 실패를 확인할 수 있다.

p = malloc(10000);

if(p == NULL)
{
    // allocation failed; take appropriate action
}

동적 메모리 할당 해제

동적으로 할당한 메모리의 해제를 위해서 free 함수를 호출할 수 있다.

free(p);

 

 

 

반응형