C언어 문법
기초 문법
이름짓기 규칙
변수의 이름은 알파벳이나 _언더바로 시작해야한다.
두번쨰 문자부터 숫자가 나올 수 있다.
변수명 중복 안된다.
변수명은 대소문자를 구분한다.
값의 할당
변수에 값을 넣는 것을 할당이라고 한다.
값을 넣을 떄에는 이콜(=) 기호를 활용한다.
실수 자료형:
형변환 (Type-casting):
서로 다른 데이터 형태를 바꿔서 일치시켜주는 것.변수 = (
float
)변수;변수의 3요소 :
이름, 주소, 값변수의 이름과 주소 :
프로그래머는 ‘변수의 이름’을 이용해 변수를 구분하지만 컴퓨터(기계)는 ‘변수의 주소’를 이용해 변수를 구분한다. 즉, 변수의 주소는 컴퓨터에게 있어서의 변수명이다.
기본 자료형
void :
함수가 리턴을 하지 않아도 되게 해주는 데이터 타입Bool :
1byte(8bit)참 거짓을 판단 (true/false)
Char :
1byte(8bit)-128~127 숫자를 저장
대부분의 문자 / 문자열을 저장하는 데 유용하게 사용
Int :
4byte(32bit)-2,147,483,648~2,147,483,647의 숫자를 저장
integer의 약자로 정수를 의미
컴퓨터에서 가장 빨리 수행할 수 있는 정수 연산 단위
Float :
4byte(32bit)소수점수를 저장
float point의 약자
부동 소수점수로 우리가 흔히 실수형태의 자료형을 계산할 때 사용
double :
8byte(64bit)소수점수를 저장
2배 정밀도를 갖는 소수점수
float보다 표현 범위가 더 넓다
연산자와 표현식
사칙연산 기호
+, -, *, /, %
이항 연산자 : 피연산자가 두개(a+b, a/b)
단항 연산자 : 피연산자가 한개 (a++, a—)
논리 연산자
&&
: 논리적 AND 연산 (~이고 ~라면)||
: 논리적 OR 연산 (~또는 ~라면)→ 둘은 이항연산자
→
AND
는true
+true
일때만true
, 나머지는 모두false
→
OR
는false
+false
일때만false
, 나머지는 모두true
비교 연산자
A==B
: A와 B가 같다면true
A ≠ B
: A와 B가 다르다면true
A > B
: A가 B보다 크다면true
A < B
: A가 B보다 작다면true
A ≥ B
: A가 B보다 크거나 같다면true
A ≤ B
: A가 B보다 작거나 같다면true
Bitwise 연산
비트를 기준으로 논리연산을 하는 것을 비트와이즈 연산이라고 한다.
비트와이즈 and : & 연산자를 활용한다
비트와이즈 or : | 연산자를 활용한다
왼쪽 쉬프트 :<< 연산자를 활용한다.
오른쪽 쉬프트 : >> 연산자를 활용한다.
제어문
조건문: if, else, switch 활용.
반복문: for, while, do-while의 사용.
break/continue: 반복 제어.
헷갈리는 제어문 구분:
for문: 반복 횟수가 정해진 경우에 사용.switch문: 값에 따른 여러 분기 처리에 사용.
while문: 조건에 따라 반복할 때 사용.
함수
함수의 정의 :
입력을 받아 어떤 값을 만들어내는 동작을 하는 구문.
이 입력이란 사용자로부터 받는 입력이 아니며,
프로그램 내부에서 파라미터로 받는 입력을 말함.
int main() {…}
진입점 함수, 모든 C/C++ 프로그램은 main 함수가 존재한다.파라미터:
함수로 입력되는 값, 파라미터는 함수 내부에서 변수처럼 동작한다.매개변수:
재귀 함수:
배열과 문자열
1차원 배열의 활용 :
선언의 경우 : int변수명
[배열크기
]활용의 경우 :
변수명
[배열번호
]배열번호(인덱스)는 0부터 시작한다.
다차원 배열 활용 방법
선언의 경우 : int
변수명
[배열크기
][배열크기
]활용의 경우 :
변수명
[배열번호
][배열번호
]문자의 배열 = 문자열
첫번쨰 방법 : char string[100];
두번째 방법 : const char* string;
#include <cstring> :
문자열 관련 함수의 선언을 담고 있는 헤더 파일strcpy (…) : 문자열을 복사하는 함수
strcmp (…) : 문자열을 비교하는 함수
strcat (…) : 문자열을 이어붙이는 함수
strpos (…) : 문자열을 찾는 함수
문자열 출력은 %S를 활용
포인터
개념 :
C/C++에서는 이 컴퓨터에게 적용되는 변수명인 ‘주소’라는 것을 직접적으로 조작할 수 있는 포인터 라는 ‘방법’을 제공하는데 이 주소를 조작하는 것을 ‘포인터를 이용한다’라고 한다.포인터의 데이터 타입 :
모든 기본형 데이터 타입 포함 사용자 정의 데이터 타입 바로 오른쪽에 *(애스터리스크)를 붙여주게 되면 해당 데이터타입의 포인터타입이 만들어지게 된다int
*myPointer
주소값을 가져오는 방법 :
모든 변수(기본형, 사용자 정의 데이터 포함)의 변수명 바로 왼쪽에 &(앰퍼샌드)를 붙여주게 되면 해당 변수의 주소값이 나오게 된다.int
myPointer
\= 0; &myPoiner
; // 주소값포인터 값을 가져오는 것의 의미 :
(*pv)의 형태로 가져온다는 것은 해당 주소값의 값을 가져온다는 것pv[0]의 형태로 가져온다는 것은 해당 주소값에서 0번째 위치의 값을 가져온다는 것
즉 결론적으로 (*pv)라는 표현과 pv[0]이라는 표현은 동일한 표현이다.
문자열 포인터 :
문자열을 포인터로 사용할 때에는const char*
를 활용한다단순히
char*
를 사용할 떄는 동적할당 문자열을 사용할 때 사용하며const char*
를 사용하는 경우는 우리가 실제로 “” 큰따옴표로 묶인 문자열을 이콜(=) 기호로 할당할 때 사용한다. (const = 상수, 프로그램상 직접 박혀있는 값)그래서 const char* myString = “Hello”; 의 값은 변경할 수 없다.
0-value, nullptr :
포인터는 nullptr이라는 값으로 0-value가 지정가능하다.
0-value란 각각의 변수가 가질 수 있는 무(zero) 값을 의미한다. 0-value에는 널문자, 0, false, nullptr 등이 있다.
0-value, 배열 초기화 :
배열의 모든 값을 0-value로 초기화를 하기 위해서는 다음의 표현이 가능하다Type
v[10] = {0,};혹은 포인터 타입의 배열일 경우
Type
* v[10] = {nullptr
,};이중 포인터 :
기본형 데이터 타입일 경우 : 2차원 배열일 가능성이 높음구조체 타입일 경우 : 포인터의 배열일 가능성이 높음
int
**v; //int
의 동적배열일 가능성이 높음Type
**v; //Type
의 1차원 동적배열일 가능성이 높음함수의 파라미터의 포인터 :
파라미터의 포인터는 단순히 변수의 주소값을 받기 위함일 수 있지만, 배열변수를 받기 위함일 수도 있다. 구분하는 방법은 보통 배열일 경우 배열의 갯수를 알려주는 변수를 따로 파라미터로 받게 된다.void foo(int* a, int count)
는 주로 배열을 처리하는 함수로, 특정 작업을 수행하고 값을 반환하지 않는다.int foo(int* count)
는 주어진 정수 포인터를 사용하여 작업을 수행하고, 그 결과를 정수 값으로 반환하는 함수이다.const char* :
하드코딩된 문자열의 타입, const char*는 하드코딩된 문자열을 넣기 위해 이콜 (=) 기호를 사용한다
const char 에는 strcpy를 사용할 수 없다.
파라미터의 const char* :
하드코딩된 문자열을 넣을 수 있지만, 문자열의 배열도 파라미터로 집어넣을 수 있다.char*, char 배열 등등의 변수이름은 모두 const char*의 파라미터 형식으로 받을 수 있다.
void* :
어떤 포인터도 될 수 있는 것이 void* 모든 포인터의 기본형char* str = "Hello World";
: 문자열 리터럴을 가리키는 포인터로, 문자열은 수정할 수 없음.char str[] = "Hello World";
: 문자열 리터럴을 배열로 초기화하여, 문자열을 수정할 수 있음.
구조체와 공용체
Struct :
변수를 한데 묶어서 관리해줄 수 있는 일종의 변수의 집합const :
는 상수라는 뜻이다 상수라는 것은 프로그램이 시작되면서 끌날때까지 절대 변하지 않는 값을 의미한다. 하지만 진정한 의미로서의 cosnt는 sefine 매크로이며, const 키워드를 이용해 만든 변수는 상수이지만, ‘저장공간’을 갖고 있는 변수이며 실질적으로 프로그램 내부에 박혀있는 값은 아니다.
Static :
tatic이라는 것은 ‘정적’ 이라는 뜻이다. 정적인 어떤가 라는 뜻이며, 유일하고 독립적인 어떤 것을 의미한다. 이 static은 class의 내부에 존재할 경우 class 내부에서 유일하고 독립적인 멤버라는 뜻이 되며, class 외부에 존재할 경우 obj 파일 내부에서 유일하고 독립적인 존재라는 뜻이 된다.extern :
extern 키워드는 ‘외부에 존재한다’ 라는 뜻이다. 어떻게 보면 C/C++에서 obj 파일로 링크가 되면서 만들어지는 진정한 전역 변수를 만들어 줄 수 있는 키워드라고 볼 수 있다. 이 extern 키워드의 활용과 뜻은 굉장히 많지만 초보적인 내용에서는 ‘전역변수’를 만들어준다.
Class 클래스
개념 :
간단히 설명하면 구조체 안의 함수class 키워드를 이용해 클래스를 생성할 수 있다.
class는 struct를 대체하기 떄문에 앞으로 모든 struct는 class로 만들게 한다.
이 클래스 내부에는 꼭 public: 이라는 키워드가 있어야한다.
클래스에 들어가 있는 모든 것을 멤버임
클래스 인스턴간 차이 :
new 키워드를 이용해 클래스를 만들게 되면 클래스의 실제 값이 Heap영역에 저장되는 것이 가장 큰 차이다. Heap 영역에 저장되는 클래스의 인스턴스 값은 함수의 실행주기(라이프사이클)에 관계없이 유지된다.delete :
따라서 new 키워드를 이용해 클래스 인스턴스를 만들었을 경우 delete 키워드를 이용해 클래스 인스턴스의 실제 값을 Heap 메모리에서 삭제해줘야한다. 이 개념은 malloc(…)과 free(…) 두 함수의 활용 경우와 동일하다고 볼 수 있다.생성자 (Constructor) :
클래스는 생성자를 통해 초기 행동을 지정해줄 수 있다. 이를 생성자라고 한다.생성자는 클래스명(파라미터들…)로 선언할 수 있다.
생성자는 리턴타입을 갖지 않는다.
this 키워드 :
클래스 멤버 함수는this
라는 키워드를 통해 자기자신의 인스턴스(포인터 타입)를 가져올 수 있다.클래스 멤버 함수를 호출하기 위해서는 호출한 주체인 어떤 클래스 인스턴스가 존재하게 되는데 그 호출 주체를
this
라고 한다.인스턴스(Instance) :
클래스는 정의만으로는 존재할 수 없다. 그냥 초안일 뿐이다.우리가 여지껏 ‘클래스의 변수’라고 말해왔던 것을 클래스 인스턴스라고 생각하면 편하다.
소멸자 (Destructor) :
클래스는 소멸자를 통해 메모리에서 해제될 때의 행동을 지정해줄 수 있다. 이를 소멸자라고 한다.소멸자는 ~클래스명() 으로 선언할 수 있으며, 소멸자도 리턴타입이 존재하지 않는다.
소멸자 호출의 필요성 :
모든 동적 메모리를 수동으로 관리해줘야 하는 c++의 특성상 클래스 멤버 변수에 동적 메모리 할당된 개체가 있다고 할 경우 소멸자에서 처리하지 않으면 하나하나 클래스 외부에서 delete 전에 메모리를 해제해야 한다. 이는 굉장히 불편함과 불합리함을 초래하게 된느데, 이를 해결할 수 있는 것이 소멸자이다.상속 (Inheritance) :
어떤 상위 개념을 가지고 더 디테일한 하위 개념을 만드는 것 클래스를 상속하게 되면 상속하는 클래스와 상속받는 클래스가 존재상속하는 클래스를 부모 클래스라고 하고, 상속받는 클래스를 자식 클래스라고 한다.
상속을 받을 클래스의 오른쪽에 클론 : public과 함께 부모클래스명을 기입하면 상속이 이루어진다.
멤버변수와 멤버함수 :
자식클래스는 부모클래스의 상속받은 모든 멤버변수와 멤버함수를 활용할 수 있으며, 외부에서도 활용할 수 있다.생성자의 실행 순서 :
자식클래스의 생성자를 호출하게 되면 자식클래스는 기본적으로 부모 클래스의 ‘기본 생성자’를 호출하게 된다.자식클래스의 생성자가 호출되기 직전 자동으로 호출되며 부모클래스의 생성자가 호출된 이후 자식클래스의 생성자가 호출된다.
파라미터가 있는 생성자 :
파라미터가 있는 생성자를 지정해주게 되면 자식클래스에서는 부모클래스의 생성자를 호출해줘야 한다. 부모클래스의 생성자를 실행하려면 파라미터가 있어야 하기 때문다형성(Polymorphism) :
클래스 인스턴스는 다형성을 통해 하나의 부모 클래스로 관리할 수 있다. 이는 객체 관리에 굉장히 유용한 관점을 제공해 준다.접근지정자 (Public, Protected, Private) :
public : 이 키워드 안에 있는 멤버는 클래스 내외부에서도 자유롭게 접근 가능protected : 이 키워드 안에 있는 멤버는 상속된 클래스에서만 자유롭게 접근 가능 외부에서는 접근이 안되고, 내부에서만 가능함
private : 이 키워드 안에 있는 멤버는 클래스 자신에게서만 자유롭게 접근 가능
상속에서의 접근 지정자 :
public : 부모 클래스의 모든 멤버를 그대로 가져옴protected : 부모 클래스의 모든 public 멤버를 protected 멤버를 가져와서 protected로 만듬
private : 부모 클래스의 모든 public, protected 멤버를 가져와서 private로 만듦
순수 가상 함수의 목적과 사용 시기
인터페이스 역할: 순수 가상 함수는 다른 클래스들이 구현해야 하는 인터페이스 역할을 합니다. 예를 들어, 위의
Shape
클래스는 모든 도형 클래스(Circle
,Square
등)들이Draw()
메서드를 구현하도록 강제합니다.다형성(Polymorphism) 지원: 순수 가상 함수가 있는 클래스는 다형성을 지원합니다. 이를 통해 부모 클래스의 포인터나 참조를 사용하여 자식 클래스의 구체적인 메서드를 호출할 수 있습니다.
공통 기능의 강제 구현: 특정 기능을 모든 자식 클래스가 반드시 구현하도록 강제합니다. 예를 들어, 모든 도형 클래스는
Draw()
메서드를 구현해야 한다고 요구할 수 있습니다.순수 가상 함수는 인터페이스 역할을 하여 다형성을 지원하고, 모든 자식 클래스가 특정 메서드를 반드시 구현하도록 강제하는 데 사용
객체 지향
오버로딩(Overloading) :
오버로딩은 동일한 이름의 함수나 연산자를 여러 번 정의할 수 있게 하는 기능이다. 단, 각 함수나 연산자는 서로 다른 매개변수 목록(매개변수의 타입, 개수, 순서)이 있어야 한다.오버라이딩 (Overriding) :
오버라이딩은 상속 관계에서 부모 클래스에 정의된 메서드를 자식 클래스에서 재정의하는 기능이다. 자식 클래스는 부모 클래스에서 상속받은 메서드의 동작을 변경할 수 있다.가상함수 (virtual) :
오버라이딩 할 때, 부모 클래스의 함수에서 vitual 키워드를 추가하게 되면 정상적으로 우리가 원하는 오버라이드 된 함수를 찾아가서 실행하게 된다. 이렇게 vitual을 추가해주어야 하는 이유는 함수가 virtual이 아닐 때 더 빠른 실행속도가 보장되기 때문이다.virtual 함수는 부모클래스에서 선언되어있으면 자식클래스에서 virtual이 아니더라도 찾아서 실행하게 해준다.
virtual
키워드virtual
키워드는 부모 클래스에서 가상 함수를 선언할 때 사용된다. 가상 함수는 파생 클래스에서 해당 함수를 재정의할 수 있도록 하는 함수이다.
override
키워드override
키워드는 파생 클래스에서 부모 클래스의 가상 함수를 재정의하고 있음을 명시적으로 나타낸다.
열거형 (Enumeration) :
enum Gender
는 범위가 지정되지 않은 열거형이다.따라서,
MALE
과FEMALE
은 전역 범위에서 사용되며,Gender::MALE
또는Gender::FEMALE
과 같이 사용할 필요가 없다.
파일 입출력
소스파일 - 오브젝트 파일
소스파일은 컴파일이 될 때 소스파일 하나하나가 오브젝트 파일을 생성하게 된다.
.cpp → .obj
맥이나 리눅스의 경우 .o 파일이 된다. C++의 컴파일은 이렇게 만들어진 obj 파일을 묶어서 하나의 실행파일을 만들어내게 된다.
매크로의 컴파일
매크로는 소스파일당 한번만 유효하다.
매크로가 소스파일 전체에 걸쳐있는 것이 아니라 include 되어 포함되거나, 혹은 소스파일 그 자체
에 있는 매크로가 소스파일 단위로 동작하게 된다.
즉, 이에 따라 ifndef 헤더파일 가드 같은 것도 소스파일별로 이루어지게 된다.
선언과 정의
함수의 선언은 여러개 있어도 되지만, 정의는 하나만 있어야함.
변수는 선언과 정의가 동시에 들어있지만, 하나만 있어야 한다.
헤더 파일(header file)에 선언하고, 소스 파일(.cpp file)에서 정의
File fopen(char filename, char* mode);
mode: 파일을 여는 방법
w: 쓰기 (존재하지 않으면 새로 생성, 존재하면 지우고 다시 생성)
r: 읽기 (존재하지 않으면 오류, 존재하면 처음부터 읽어나가기)
a: 파일을 이어나가기 (존재하지 않으면 새로 생성, 존재하면 파일의 마지막부터 쓰기)
w+: 쓰기 또는 읽기 (존재하지 않으면 새로 생성, 존재하면 지우고 다시 생성)
r+: 쓰기 또는 읽기 (존재하지 않으면 오류, 존재하면 처음부터 읽고 쓰기)
a+:이어나가기 또는 읽고 쓰기 (존재하지 않으면 새로 생성, 존재하면 파일의 마지막부터)
- char fgetC(FILE*fp);
fp로부터 다음char를 1개 읽는다. fp를 1만큼 뒤로 이동한다
- char* fgets(char* buff, int size, FILE* fp);
fp로부터 다음 char*를 size만큼 읽는다. fp를 size만큼 뒤로 이동한다.
- int fscanf(FILE* fp, const char* str,…);
우리가 익히 알고 있는 scanf함수와 동일하게 사용. 단 fp를 처음 파라미터로 넘겨야한다. fp는 읽은 만큼 뒤로 이동
몇개의 argument가 성공적으로 assign 되었는지를 반환, 모두 실패시 EOF반환
- int fseek(FILE* fp, long pos, int origin);
fp를 origin 기준으로 pos 만큼 이동한다. (origin:SEEK_SET,SEEK_CUR,SEEK_END)
SEEK_SET: fp의 최초 시작점
SEEK_CUR: fp의 현재 지점
SEEK_END: fp의 마지막
성공시 0, 실패시 0이 아닌 값을 반환
- int fputc(FILE* fp,char c);
fp에 c를 출력, fp를 1만큼 뒤로 옮긴다
성공시 c를 그대로 반환 실패시 EOF 반환
- int fprintf(FILE* fp, char* str,…);
printf 함수와 동일한 동작. fp를 먼저 입력한다.
몇개의 arguments를 성공적으로 fp에 출력했는가를 리턴, 오류시 0보다 작은 값을 리턴
- int fclose(FILE* fp);
fp 파일 포인터 종료
성공시 0리턴, 실패시 EOF 리턴
- int feof(FILE* fp);
fp 파일이 현재 EOF 인지 확인, EOF 라면 1 아니라면 0을 리턴
- EOF
매크로 상수. stdio.kr에 정의됨
- fwrite(void* target, size_t each_size, size_t total_size, FILE* fp);
target : void* (어떤 포인터라도 상관없다)
each_size : target의 각각 byte 사이즈 (배열사이즈)
total_size: target의 each_size의 크기를 기준 단위로 한 전체 사이즈
- fopen(…)
fopen을 사용할 때에는 꼭 습관처럼 fclose(…)먼저 아래쪽에 만들어두고 그 사이에 코딩을 하는 것이 가장 좋다. 이는 다른 프로그래밍에서도 마찬가지인데 다른 프로그래밍 언어에서도 똑같이 파일을 열고 파일을 닫고 하기 때문이다.
fclose(…)
- fclose(…)함수는 nullptr을 파라미터로 받았을 경우 런타임 오류가 발생하게 된다. fopen을 “r”옵션으로 열었을 경우에 파일이 없다면 nullptr을 리턴하게 되는데 이 경우를 생각해서 프로그래밍 해야 한다.
메모리 관리
문자열 동적 할당 :
malloc (…)
메모리를 동적할당할 수 있는 함수, cstdlib 헤더파일에 선언되어 있음Type
v = (Type
*)mallock(sizeof(Type
));와 같은 방법으로 Type의 크기만큼 1개의 공간을Heap
영역에 할당한다.
free (…)
cstdlib에 포함된 함수동적할당된 변수, 배열 등등은 함수의 라이프사이클에 관계없이 계속 살아있는데 이것은 자동으로 메모리가 반환되지 않는 것. 이것을 free()라는 함수를 이용해 해제하지 않으면 메모리 누수의 원인이 된다.
동적 메모리 할당 - 배열
Type
* v = (Type
*)malloc(sizeof
(Type
) * size);이는 근본적으로 배열이 포인터와 같은 원리이기 때문에 malloc으로 할당될 메모리의 양만 더 크게 잡아주면 동적으로 ‘배열’을 선언할 수 있다. size에는 자신이 원하는 배열의 크기가 지정되면 된다.
전처리기와 매크로
전처리기 # :
전처리는 모두 #으로 시작한다.#include
또한 #으로 시작하기 때문에 전처리 즉 매크로.#define_CRT_SECURE_NO_WARNINGS
구문도 마찬가지 #으로 시작했기 때문에 전처리 이다.전처리기 역할 :
전처리기는 소스 코드를 읽고, 매크로를 치환하고, 조건부 컴파일을 수행하며, 필요한 헤더 파일을 포함시키는 등 여러 가지 작업을 수행한다.전처리기 단계가 끝나면, 컴파일러는 수정된 소스 코드를 사용하여 실제 컴파일을 진행한다.
매크로 함수 :
전처리기에서 처리되는 코드 조각.#define
매크로(전처리) 함수를 호출하면 어떤 실행이 컴퓨터 연산에 의해 일어나는 것이 아닌 아예 소스코드가 바뀌어버리게 됨.매크로 상수 :
#define
매크로(전처리)를 사용하면 상수(Constants)를 지정할 수도 있다.상수는 프로그램의 시작부터 종료까지 절대 변하지 않는 값을 상수라고 한다.
#define
매크로와 연계하여 매크로(전처리) 조건을 넣을 수 있다.#if
<매크로 조건
\> //if(…) {} 구문과 비슷실행하고 싶은 구문
#elif
<매크로 조건
\> // else if(…) {} 구문과 비슷실행하고 싶은 구문
#else
// else {} 구문과 비슷실행하고 싶은 구문
#endif
많이 사용하는 헤더파일:
#include <cstdio> 표준 입출력 헤더, 콘솔 입출력과 파일 입출력에 관한 함수들이 포함된다. (printf, scanf, fseek, fopen…)#include <cmath> 수학 연산 관련 함수를 포함한다. (sin, cos, pow, log)
#include <cstdlib> 각종 활용할 수 있는 라이브러리 함수들을 포함. (malloc, free, rand)
#include <ctime> 시간 관련 함수를 포함한다. (clock, time ..)
사용자 정의 헤더:
일반적으로 프로그램은 하나의 소스코드로 만들어지지 않고, 여러개의 소스코드로 만들어지는데 이런 소스코드들을 묶어줄 수 있는 게 헤더파일이다. 사용자 정의 헤더는 <> 기호가 아닌 “” 기호로 include한다.
Subscribe to my newsletter
Read articles from realhwi directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by