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);
'~ 2024.03 > C 언어' 카테고리의 다른 글
[C 언어 기초] 타입 별 메모리 크기 (0) | 2024.01.05 |
---|---|
[C 언어 기초] 동적 메모리 할당 (0) | 2023.12.13 |
[C 언어 기초] 문자열 문자마다 읽기 (0) | 2023.12.13 |
[C 언어 기초] 포인터와 배열 (0) | 2023.12.13 |
[C 언어 기초] 포인터 (0) | 2023.12.13 |