작성된 코드 중에 컴파일러에 의해 무시되는 부분.
필요한 설명이나 메모를 작성하거나 실행되면 안되는 코드를 주석으로 설정.
개발자가 만들어주는 이름으로 변수, 함수 등을 구분하기 위해 사용
c언어 자체에서 미리 사용하기위해 정해놓은 단어들 (용도가 고정)
- C 언어에서 사용할 수 있는 데이터 타입
- 개발자가 OS에게 사용할 값의 타입을 전달하는 목적
- 기본자료형: 정수, 실수
- 열거형: 연속적으로 나열된 값을 단어로 정의하여 사용
- Void: 아무 의미가 없는 값
- 참조형: 기본 자료형은 값을 의미하지만 참조형은 메모리의 주소값을 의미
- 컴퓨터에 설치되어 있는 메모리는 OS에 의해 byte 라는 단위로 구분
(bit 는 0이나 1을 기억할 수 있는 기억공간. 8bit = 1byte)- 이렇게 구분되는 공간을 기억장소로 부르며 기억장소는 0부터 시작하는 인덱스 번호를 가짐
OS에 의해 구분되어 있는 byte 단위의 기억공간은 C언어에 의해 여러 공간을 하나로 묶어 사용하기도 함
Byte 단위의 기억공간 하나는 숫자를 총 256개를 관리 가능
2개의 공간을 묶어 하나라로 사용하면 총 65536개를 관리할 수 있음
1~4개의 기억공간을 하나로 묶어서 사용
정수 값을 표현하기 위한 자료형
Char(1byte): 문자 1개를 위한 값. 컴퓨터는 모든 글자를 숫자로 기억 / 문자 코드 값을 관리
Unsigned char(1byte): 부호가 없는 char
Signed char (1byte): 부호가 있는 char
Int (2 or 4 byte): 정수. -32768~32767 or -2147483648~214748364
cf. 보통 개발할 때 int(인티저)를 가장 많이 사용
Unsighned int: 부호가 없는 정수 0~65535, 0~4294967295
Short(2byte)
Unsigned short(2byte)
Logn(4byte)
Unsigned long(4byte)
printf("char : %d \n", sizeof(char));
printf("unsigned char : %d \n", sizeof(unsigned char));
printf("signed char : %d \n", sizeof(signed char));
printf("int:%d\n", sizeof(int));
printf("unsigned int:%d\n", sizeof(unsigned int));
printf("short:%d\n", sizeof(short));
printf("unsigned short:%d\n", sizeof(unsigned short));
printf("long:%d\n", sizeof(long));
printf("unsigned long:%d\n", sizeof(unsigned long));
실수 값을 표현하기위한 자료형
printf("float:%d\n", sizeof(float));
printf("double:%d\n", sizeof(double));
printf("long double:%d\n", sizeof(long double));
기본 자료형은 정수, 실수를 의미하지만 참조 자료형은 메모리의 주소 값을 의미
메모리의 주소 값은 기억장소의 위치를 의미. c 언어는 기억장소의 위치를 기억했다가 필요할 때 접근하여 원하는 작업 가능
개발자가 코드에 직접 작성하는 값.
종류: 정수 리터럴, 실수 리터럴, 문자열 리터럴, 문자 리터럴
printf("정수 리터럴 : %d\n ", 100);
printf("실수 리터럴 : %f\n ", 11.11);
printf("문자 리터럴 : %c\n ", 'c');
printf("문자열 리터럴 : %s\n ", "안녕하세요");
어떠한 값을 저장 및 사용하기 위해서는 변수를 사용해야 함
메모리에 기억공간을 마련하면 값을 저장하고 불러오는 것이 가능
변수: 개발자가 자유롭게 사용할 수 있는 기억 공간. 개발자가 이름 부여
변수를 사용하기 위해서는 변수를 먼저 선언해야함
(변수는 선언과 동시에 생성)
자료형 변수명;
자료형 변수명 = 값; (초기화)
자료형 변수명, 변수명, 변수명; (같은 타입의 변수 여러 개 선언)
프로그램 실행 중에 변하지 않음.
const int a1 = 1000;
printf("a1 : %d\n", a1);
전처리 명령어 #define을 사용하면 상수를 정의 가능
코드 상단 부분에 #define 으로 상수를 정의
전처리 명령어 #define 으로 정의된 상수를 사용하게 되면 컴파일 전에 전처리기에 의해 정의된 값으로 코드가 변경됨
컴파일 시에는 변경된 값으로 컴파일 됨
#define은 변수를 선언하지 않기 때문에 기억공간이 마련되지 않아 메모리를 사용하지 않음
#define DATA1 100
#define DATA2 11.11
#define DATA3 'a'
#define DATA4 "안녕하세요"
int main()
{
printf("DATA1 : %d\n", DATA1);
printf("DATA2 : %f\n", DATA2);
printf("DATA3 : %c\n", DATA3);
printf("DATA4 : %s\n", DATA4);
}
산술 연산자: 더하기, 빼기 등 일반적인 산수에서 사용하는 연산자
관계 연산자: 같다, 다르다 등의 관계를 계산하는 연산자, 참은 1, 거짓은 0을 반환
논리 연산자: 연산자 좌우의 관계 연산자 값에 따라 0이나 1을 반환하는 연산자
비트 연산자: 2진수로 숫자에 대한 연산자
대입 연산자: 우측에서 연산된 수식의 값을 좌측의 변수에 저장하는 연산자
기타 연산자: sizeof, &, *, ? :
+, -, *, /, % (나머지)
++ (변수에 들어가는 값 하나 증가)
-- (변수에 들어가는 값 하나 감소)
int d = 10;
int e = 10;
int f = 10;
int g = 10;
c = d;
d = d + 1;
printf("c, d : %d, %d\n", c, d);
d = 10;
c = d++;
printf("c, d : %d, %d\n", c, d);
c = ++e;
printf("c, e : %d, %d\n", c, e);
c = f--;
printf("c, f : %d, %d\n", c, f);
c = --g;
printf("c, f : %d, %d\n", c, g);
==, !=, >, <, >=, <=
int a = 21;
int b = 10;
int c;
c = a == b;
printf("a == b :%d\n", c);
c = a != b;
printf("a != b :%d\n", c);
c = a < b;
printf("a < b :%d\n", c);
c = a > b;
printf("a > b :%d\n", c);
c = a <= b;
printf("a <= b :%d\n", c);
c = a >= b;
printf("a >= b :%d\n", c);
두 관계식의 논리 값
&&(and), ||(or), !(not)
*참고: |는 shift+\
int a = 10 > 2; //1
int b = 10 > 20; //0
int c = 10 > 3; //1
int d = 10 > 30; //0
int e;
e = a && b;
printf("a && b : %d\n", e);
e = a && c;
printf("a && c : %d\n", e);
e = b || c;
printf("a || c : %d\n", e);
e = b || d;
printf("b || d : %d\n", e);
e = !a;
printf("!a : %d\n", e);
e = !b;
printf("!a : %d\n", e);
int f = 10;
e = f >= 5 && f <= 20;
printf("e : %d\n", e);
- &(and), |(or), ^(exist or), ~(not), <<(left shift), >>(right shift)
- 일반적인 프로그램에서 거의 사용하지 않음
^는 같을 때 0, 다를 때 1 출력
int a = 60; // 0011 1100
int b = 13; // 0000 1101
int c;
c = a & b; // 0000 1100
printf("a & b : %d\n", c);
c = a | b; // 0011 1101
printf("a | b : %d\n", c);
c = a ^ b; // 0011 0001
printf("a ^ b : %d\n", c);
c = ~a; // 1100 0011
printf("~a : %d\n", c);
c = a << 2; // 1111 0000
printf("a << 2 : % d\n", c);
c = a >> 2; //0000 1111
printf("a >> 2 : % d\n", c);
=: 오른쪽 값을 왼쪽에 대입
+=: ex. C+ = A → C = C+A
-=: ex. C- = A → C = C-A
=: ex. C = A → C = C*A
/=: ex. C/ = A → C = C/A
%=: ex. C% = A → C = C%A
<<=: ex. C<<=2 → C=C<<2
>>=: ex. C>>=2 → C=C>>2
^=: ex. C^=2 → C=C^2
|=: ex. C|=2 → C=C|2
- Sizeof(): 괄호 안 변수나 자료형의 바이트 수 확인
- &: 변수 앞에 붙을 경우 해당 메모리 상 주소값 확인
- *: 포인터 변수
- ?: : 좌측 수식 값이 참이면 좌측 값이, 거짓이면 우측 값이 최종 결과 값이 됨
- 조건에 따라 코드 수행 여부를 결정하는 구문
- 위에서 아래로 흐르는 코드의 수행을 변경하고자 할 때 사용
- 주로 주어지는 조건식의 결과에 따라 어느 부분을 수행할지 결정하는데 사용
- If문과 switch 문 사용
If 문은 조건에 만족하는 곳이 수행되는 분기문
주어지는 조건식이 0이 아니면 구문이 수행되고, 0(거짓)이면 수행되지 않음
{
int a =30;
int b = 20;
if(a > b){
printf("a는 b보다 큽니다\n");
}
else {
printf("a는 b보다 크지 않습니다\n");
}
int a = 100;
if (a < 0) {
printf("a는 0보다 작습니다\n");
}
else if (a < 5) {
printf("a는 0보다 작습니다\n");
}
else if (a < 10) {
printf("a는 10보다 작습니다\n");
}
else if (a < 20) {
printf("a는 20보다 작습니다\n");
}
else {
printf("a는 0, 5, 10, 20보다 작지 않습니다\n");
}
주어진 수식이나 변수의 값에 해당하는 부분이 수행
Break 문을 생략하면 하단 코드가 모두 수행됨
Break가 있다면 아웃, 없다면 하단 부분 모두 실행, 일치하는 부분이 없으면 디폴트 실행
int a = 100;
switch (a) {
case 0:
printf("a는 0입니다\n");
case 5:
printf("a는 5입니다\n");
case 10:
printf("a는 10입니다\n");
case 20:
printf("a는 20입니다\n");
default :
printf("a는 0, 5, 10, 20이 아닙니다\n");
}
int b = 10;
switch (b) {
case 0:
printf("b는 0입니다\n");
break;
case 5:
printf("b는 5입니다\n");
break;
case 10:
printf("b는 10입니다\n");
break;
case 20:
printf("b는 20입니다\n");
break;
}
- 코드의 일부분을 필요한 만큼 반복하는데 사용
- 개발자가 원하는 만큼, 혹은 반복할 조건이 있을 때 원하는 코드를 반복시킬 수 있음
- For, while, do~while 3가지 구문 제공
주어진 조건식이 거짓이 될 때까지 반복
반복횟수를 결정하지 못하지만 반복한 조건이 있을 경우 사용
조건식이 처음부터 거짓이면 한번도 수행되지 않음
While(조건식){
코드
}
int a = 10;
while (a < 20) {
printf("a : %d\n", a);
a++;
}
printf("while 문 종료\n");
주어진 조건이 만족하는 동안 반복하는 반복문
반복 횟수가 결정 되어 있을 때 사용
초기식은 최초에 한 번만 수행되고 조건식을 검사한 후 참이면 구문을 실행하고 증감식을 실행한다. 그 후 다시 조건식을 검사하고 참이면 구문을 실행하고 증감식을 실행한다. 조건식이 거짓이 될 때 까지 이 과정을 반복한다.
For(초기식; 조건식; 증감식){
구문
}
int i;
for (i = 0; i < 10; i++){
printf("%d\n", i);
}
printf("for문 종료\n");
주어진 조건식이 거짓이 될 때 까지 반복
반복횟수를 결정하지 못하지만 반복한 조건이 있을 경우 사용
조건식이 처음부터 거짓이라도 한번은 수행 (조건 검사가 하단에 위치하기 때문에)
Do{
코드
} while (조건식);
int a = 10;
do {
printf("a : %d\n", a);
a++;
} while (a < 20);
- 프로그램 작성 시 자주 사용하는 코드가 있다면 함수로 만들어 사용하는 것이 편리함
- 함수는 하나의 작업을 위해 필요한 코드들을 그룹화 시켜놓은 것으로써 함수를 만들어 코드를 작성한 후에 작성 코드의 동작이 필요할 때 함수 내부의 코드를 동작시켜 필요한 결과를 얻을 수 있도록 함
- 함수 내부의 코드를 동작시키기 위해 함수를 지정하는 것을 '함수를 호출한다'라고 부름
- 함수를 호출하게 되면 코드의 흐름은 함수 쪽으로 이동하고 함수의 코드가 모두 수행이 되면 다시 함수를 호출한 쪽으로 돌아감
- Main도 함수이며 이 함수는 프로그램이 시작되면 OS가 자동으로 호출하는 함수이며, 개발자는 main 함수에서부터 코드를 작성하게 됨
- 반복해서 사용하는 코드가 있다면 함수로 만들어서 사용하면 생산성 향상에 도움이 됨
함수를 호출 할 때 값을 전달할 수 있으며, 이는 매개변수로 받음
반환값 함수이름 (매개변수){
코드
}
반환값: 함수의 수행이 모두 완료되면 함수를 호출한 곳으로 되돌아가는데 이 때 값 하나를 전달할 수 있음.
함수의 수행이 끝난 후 전달값이 있다면 그 값의 자료형을 작성함. 만약 없다면 void를 작성
함수 이름: 식별자 규칙에 맞게 함수를 구분할 수 있는 이름을 작성
매개 변수: 함수를 호출하여 함수 내부의 코드가 수행될 때 필요한 데이터가 있다면 매개 변수를 통해 받을 수 있음
만약 작성된 함수가 호출하는 코드보다 아래에 있다면 경고가 나타나거나 C언어의 종류에 다라서 오류가 발생할 수 있음
함수가 호출하는 코드보다 아래에 있다면 함수의 존재를 알려주기 위해 함수를 선언해줘야 함
- 반환타입 함수명(매개변수);
void test1() {
printf("test1 함수가 호출되었습니다\n");
}
void test2();
int test3(){
return 100;
}
int test4(int v1, int v2) {
int v3 = v1 + v2;
return v3;
}
int main()
{
test1();
test2();
int a1 = test3();
printf("a1 : %d\n", a1);
int a2 = test4(100, 200);
printf("a2 : %d\n", a2);
}
void test2() {
printf("test2 함수가 호출되었습니다\n");
}
- 개발자가 변수를 선언할 때 선언한 위치에 따라 변수를 사용할 수 있는 범위가 결정됨
- 변수를 사용할 수 있는 범위라면 자유롭게 사용이 가능하지만 그렇지 않다면 변수를 사용할 수 없음
- 변수의 사용 범위가 같은 변수 끼리는 동일 이름을 가질 수 없으며 사용 범위가 다르면 변수 이름이 동일해도 됨
특정 영역 내부에서만 사용할 수 있는 변수
- 함수 내부에서 선언한 변수
매개 변수
분기문, 반복문 내에서 선언한 변수
어느 영역에 포함되어 있지 않는 변수
어디서든 사용이 가능
만약 전역 변수와 지역 변수명이 동일할 경우 지역 변수를 사용
기본 자료형 변수: 기본 자료형 값을 담는 변수 (일반 변수)
참조 자료형 변수: 참조 자료형 값을 담는 변수 (포인터 변수)
포인터 변수에 담을 기억공간의 주소 값은 & 연산자로 알아올 수 있음
포인터 변수는 *(애스터리스크)를 붙혀서 선언
일반 변수에는 실제로 사용할 값들이 들어있으므로 변수를 사용하면 값이 나옴
포인터 변수는 기억공간의 주소 값이 들어 있으므로 변수를 사용하면 기억 공간의 주소 값이 나옴
포인터 변수를 사용할 때 *연산자를 사용하면 포인터 변수에 들어 있는 주소 값을 가져오고 주소 값을 통해 기억장소에 접근하여 해당 변수를 사용할 수 있음
포인터 변수를 선언하고 기억공간의 주소 값을 넣지 않을 때는 NULL 값을 넣어준다. 포인터 변수 선언 후 주소 값을 넣지 않으면 예상하지 못한 값들이 들어 있을 수도 있기 때문이다.
NULL은 의미가 없는 값을 의미
기본 자료형에서는 void, 참조 자료형에서는 NULL을 사용
포인터 변수는 특정 기억공간의 주소 값을 가지고 있는 변수
만약 포인터 변수가 가지고 있는 주소에 해당하는 기억공간이 포인터 변수라면 그 포인터 변수도 다른 기억공간의 주소값을 가지고 있게 된다. 이 때 최종 기억 공간이 접근할 경우 다중 포인터를 사용한다.
int a1 = 100;
int *a2 = &a1;
int a3 = a1;
printf("a1 : %d\n", a1);
printf("a2 : %d\n", a2);
printf("*a2 : %d\n", *a2);
a3 = 1000;
printf("a1 : %d\n", a1);
printf("a3 : %d\n", a3);
*a2 = 2000;
printf("a1 : %d\n", a1);
printf("*a2 : %d\n", *a2);
int *pt = NULL;
printf("pt :%d\n", pt);
if (pt) {
printf("포인터 변수 pt에는 주소 값이 설정되어 있습니다\n");
}
int a10 = 100;
int *a20 = &a10;
int**a30 = &a20;
printf("a10 : %d\n", a10);
printf("*a20 : %d\n", *a20);
printf("**a30 : %d\n", **a30);
개발자가 만든 함수의 주소 값을 가지고 있는 포인터를 함수 포인터라고 함
개발자가 만든 함수들도 메모리의 기억공간에 저장되는데 함수가 저장된 기억공간의 주소 값을 포인터 변수에 담아놓고 이를 이용해 함수를 호출할 수 있는 개념임
int test1() {
return 100;
}
void test2(int *v1) {
*v1 = 100;
}
void test3(int *v1, int *v2) {
*v1 = 1000;
*v2 = 2000;
}
int main()
{
int a1 = test1();
printf("a1 : %d\n", a1);
int a2;
test2(&a2);
printf("a2 : %d\n", a2);
int a3;
int a4;
test3(&a3, &a4);
printf("a3 : %d\n", a3);
printf("a4 : %d\n", a4);
}
void test1(int a, int b) {
printf("a : %d\n", a);
printf("b : %d\n", b);
}
void add(int a, int b) {
printf("add: %d\n", a + b);
}
void minus(int a, int b) {
printf("add: %d\n", a - b);
}
void process(void (*fn)(int, int), int a, int b) {
fn(a, b);
}
int main()
{
void (*pt)(int, int); //pt: 포인터 변수의 이름, 매개변수는 int와 같이 타입만 작성해주면 함수 포인터 선언 가능
pt = test1;
pt(10, 20);
process(add, 100, 200);
process(minus, 100, 200);
}
소프트캠퍼스, 처음 시작하는 C언어, 구름EDU, URL, 2021년 8월 23일 수강