-Summary-
c언어 배경지식, printf 함수, c언어 기본 문법, 변수, 정수-실수-문자 자료형, 상수, scanf 함수
02-15 (Unit 1.1 ~ Unit 6.1)
//Unit 1.1 ~ Unit 3.2까지는 파이썬 코딩도장과 겹치는 부분도 있고, 환경 구성 부분이라 중요한 부분 빼고는 생략했습니다.
-c언어의 역사-
c언어는 1972년 켐 톰슨과 데니스 리치가 유닉스 운영체제를 만들기 위해 고안한 프로그래밍 언어다. 메모리와 하드웨어를 직접 제어할 수 있다는 특징이 있다. 이 때문에 Windows, Linux, Android, iOS 운영체제의 커널은 c언어로 만들어져 있다. 운영체제 말고도 Oracle, MySQL 등의 데이터 베이스도 c와 c++로 만들어져 있고, 에어컨, 세탁기 등의 임베디드의 컴퓨터도 c언어로 구현됐다 한다. iot(internet of things), 자동차 ecu에도 역시 c언어를 많이 사용한다.
솔직히 프로그래밍을 공부했던 때, 도대체 왜 c언어가 프로그래밍 언어 순위 상위권을 지키고 있는지, 이해가 안갔었다. 왜냐하면 1,2위를 다투는 자바 같은 경우, 안드로이드가 java로 작성됐고, 앱, 웹 등 여러 분야에 범용적으로 사용되기 때문에 상위권이지만, c언어 같은 경우 당시 눈에 띄는 c언어로 개발된 것들이 보이지 않았기 때문이다.
하지만 운영체제의 커널, 임베디드 등이 c언어로 구현 됐다는 사실을 아니, c언어가 왜 1위를 차지하고 있는지 이해가 간다. Assembly가 10위?!
c언어의 경우 c++, java, c# 등의 많은 언어에 큰 영향을 준 언어다. 다른 언어들의 문법을 봐도, 세부적인 부분을 제외하면 굉장히 유사하다는 것을 알 수 있다.(조건식, ;, 변수 선언 방식 등등) 따라서 많은 언어의 영향력을 준 c언어를 배운다면 다른 언어를 배우기 쉽고, 또한 하드웨어, 메모리 관리 관련 내용도 다루게 되기 때문에, 컴퓨터의 동작 원리를 이해할 수 있고, 또한 메모리와 연관 된 리버싱, 시스템 해킹을 공부할 때 도움이 될 것 같다.
-printf-
printf는 파이썬의 print와 비슷하게 문자열을 출력해준다.
printf("<문자열>"); 이렇게 사용하면 된다.
또한 뒤에 정리한 내용이지만 서식 지정자를 사용해 줄 때는 printf("<서식>", <값1>, <값2>...) 이런식으로 작성하면 된다.
c언어 코드를 짤 때 중요한 것은 각 명령이 끝날 때 ';'를 꼭 써줘야한다. 파이썬 같은 경우 ';'은 한 줄에 여러개의 명령을 입력할 때만 써줬지만, c언어는 ;로 각 명령을 구분한다.
printf 안에 Hello Word!\n을 입력하고 실행하니 printf가 호출되면서 " "안의 내용을 콘솔에 출력해주는 것을 볼 수 있다.
문자열 뒤쪽에 보면 \n이 붙어 있는 것을 볼 수 있는데, 이 \n은 파이썬과 똑같이 개행의 역할을 해준다.
-#include < >-
include는 파이썬의 import와 비슷하게 생각하면 된다. import는 모듈을 로드했지만, 이 include는 헤더파일을 포함해주는 역할을 한다. 헤더와 모듈은 그냥 비슷한 것이라 생각해도 무방할 것 같다.
사용은 #include <(헤더 파일 명)>으로 하면 된다.
위 코드의 stdio.h 헤더 파일은 printf 함수를 호출할 때 사용되는 헤더 파일이다.
-main()-
위 printf 코드의 3행을 보면 int main()이 있고 5,6행의 코드가 { }로 둘러싸인 것을 볼 수 있는데, 이 부분은 파이썬에서 배웠던 def로 함수를 만든 것처럼 하나의 함수라고 보면된다. 파이썬은 함수 내의 코드를 들여쓰기를 통해 구분했는데, c언어에서는 들여쓰기가 아닌 { }를 통해 구분한다. { } 안의 코드는 해당 함수의 코드라고 보면 된다. 또한 들여쓰기도 필수가 아니라 선택이다. 들여쓰기를 안한다고 해서 오류가 나진 않는다.
본론으로 돌아와서 int main()은 일반 함수가 아닌 특별한 기능을 하는 함수다. c언어로 콘솔 프로그램을 프로그래밍할 때는 이 main코드가 있어야 하는데(없다면 오류 뜬다.), 이 main의 기능은 프로그램을 컴파일하고 실행했을 때, 맨 처음 실행되는 함수다. 즉 파이썬의 경우 그냥 1행부터 순서대로 코드를 짜면 코드가 순차적으로 실행이 됐지만, 이 c언어는 1행부터 적을 수는 없고, 이 int main 함수 안에다가 코드를 적으면, 프로그램이 실행될 때, 이 main 함수 내의 코드들을 순차적으로 실행해 주는 것이다.
참고로 한 프로젝트에 main 함수가 여러 개일 경우 컴파일 오류가 뜬다.
-return-
printf 코드의 마지막 부분에는 return 0이라는게 있는데, 이 return은 파이썬의 return과 똑같이 함수를 끝내주면서 반환 값을 호출한 곳에 전달해주는 역할을 한다. int main 함수에서는 그냥 프로그램을 끝내준다 생각하면 될 것 같다.
필자같은 경우 visual stdio 프로그램을 통해 코드를 실행했는데, 이 코드는 파이썬처럼 바로바로 실행되는게 아닌, 컴파일 과정을 거쳐서 실행된다.
hello.c라는 코드를 작성했다 가정하면, 이 hello.c를 visual stdio에서 실행(빌드)하면 컴파일 과정을 거쳐 hello.c 파일이 hello.obj로 변환됐다가 실행파일인 hello.exe로 바뀌어서 실행되는 것이다.
-%s-
이 %s는 서식 지정자로, 파이썬의 %s와 똑같은 기능을한다. 파이썬과 똑같이 s는 string의 약자로 %s 부분을 지정한 문자열 값으로 바꿔준다.
"%s", "<문자열>" 이런식으로 사용하면 %s 부분이 <문자열>에 지정한 값으로 변경된다.
printf에 %s를 넣어주고 ','후 문자열 값을 넣어주니, %s 부분이 지정한 Hello World!로 바뀌어서 출력되는 것을 볼 수 있다.
이런식으로 여러개의 %s를 사용할 경우 해당 문자열 값들을 ','로 구분해서 넣어주면 된다.
파이썬의 서식 지장자 사용과 똑같이 서식지정자를 넣어준 부분에 다른 문자들을 넣어주면, 문자들은 그대로 나오고 %s 부분만 지정한 문자열 값으로 변경되어 나오는 것을 볼 수 있다.
printf 함수를 사용하기 위해 필요한 해더파일은 stdio.h였다.
답 : b
c언어에서 문자열을 출력하는 방법은 printf("<문자열>");이다.
답 : d
가장 처음 실행되는 함수는 main이다. int는 나중에 배우겠지만 반환 형식에 해당되서 함수 이름에는 해당되지 않는다.
답 : main
문자열을 출력할 때 사용하는 서식 지정자는 %s다.
답 : a
Hello, world!와 1234567890이 각 줄에 출력되게 하는 것이 목적이다. printf안에 문자열을 넣어주고, 첫 번째 printf에서 개행문자 \n을 써주면 될 것 같다.
#include <stdio.h>
int main()
{
printf("%s\n","Hello, world!");
printf("%s", "1234567890");
return 0;
}
앞서 배운 서식 지정자 %s를 활용해서 문제를 풀어봤다. 첫 번째 printf에서 Hello, world!를 출력하게끔 서식 지정자를 썻고 또한 줄바꿈을 위해서 개행문자 \n을 써줬다. 두 번째 printf는 첫 번째와 똑같이 서식 지정자를 사용해 1234567890이 출력하게끔 했고, 개행문자는 필요가 없어서 안써줬다.
Hello, world!를 출력하는 것이 목적이다. printf 함수 안을 보면 이미 Hello, %s\n이 주어져 있다. %s 부분이 world!가 들어갈 자리므로 %s에 world!가 들어가게끔 쉼표 뒤에 작성해주면 될 것 같다.
#include <stdio.h>
int main()
{
printf("Hello, %s\n", "world!");
return 0;
}
쉼표 뒤에 "world!"를 써서 %s 부분에 world!가 들어가도록 작성했다.
Hello, world! 두개가 각 줄에 출력되게 만드는 것이 목적이다. printf 함수를 이용하면 될 것 같다.
#include <stdio.h>
int main()
{
printf("%s\n%s", "Hello, world!", "Hello, world!");
return 0;
}
두 줄로 나눠쓰지 않고 개행문자와 서식문자를 이용해서 한 줄로 작성했다.
Hello, C Language를 출력시키는 것이 목적이다. 서식으로 %s, %s %\n이 지정되어 있으므로 첫 번째 %s에는 'Hello', 두 번째 %s에는 'C', 세 번째 %s에는 Language가 들어가면 될 것 같다.
#include <stdio.h>
int main()
{
printf("%s, %s %s\n", "Hello","C","Language");
return 0;
}
-;(세미클론)-
앞서 정리했듯이 세미클론이 필요없는 파이썬과 다르게 c언어는 한 구문(명령)이 끝날 때 ;를 붙여줘야한다. 만약 세미클론을 쓰지 않는다면 컴파일 과정에서 오류가 발생한다.
다만 뒤에 정리했지만, for, if, while 등의 조건식에는 ;를 붙여주지 않는다.
-주석-
주석은 파이썬과 똑같다. 실행에 영향을 주지 않는 부분을 뜻한다. 파이썬에서는 주석을 #, """ """를 사용했지만, c언어나 java, c#, c++등은 //, /* */를 주석기호로 사용한다.
//<주석처리할 문장> : 파이썬의 #과 같이 한 줄을 주석처리 해준다.
/* <주석처리할 문장 */ : 파이썬의 """ """과 같이 여러 줄을 주석처리 해준다.
위 사진처럼 //은 한 줄을 주석처리해주고, /* */은 /* */ 사이의 모든 문장들을 주석 처리해 주는 것을 볼 수 있다.
-{ }-
앞서 정리했듯이 if, for, 함수 등의 코드 영역은 { }로 구분한다. 파이썬 같은 경우는 코드 블럭을 들여쓰기로 구분하는데, c언어는 들여쓰기에는 아무런 의미가 없고, 오직 { 부터 }까지가 하나의 코드블럭이 된다.
{ } 같은 경우는 대부분의 경우 ;을 붙여주지 않는데, 문법에 따라(구조체 등) ;을 붙여주니 참고하면 된다.
-들여쓰기-
들여쓰기는 파이썬과 달리 c언어에서는 문법적인 의미는 없다. 들여쓰기를 안해줘도 상관이 없지만, 코드 블럭을 구분하는 등 들여쓰기를 해주면 가독성이 높아지기 때문에 해주는 것이 좋다.
들여쓰기 방법은 파이썬과 똑같이 공백 2칸, 공백 4칸, Tab키로 나눠져 있는데, 국룰은 공백 4칸 또는 Tab이다. 따라서 공백 4칸 또는 Tab으로 들여쓰기를 해주는 것이 좋다.
-변수 선언-
c언어에서 변수는 파이썬과 마찬가지로 값을 담아주는 상자같은 역할을 한다. 파이썬은 자료형 없이 변수 이름만 적고, 해당 변수에 들어가는 값의 자료형에 따라 해당 변수의 자료형이 정해졌는데, c언어는 앞에 자료형을 써줘서, 처음부터 그 변수의 자료형이 뭔지 정해줘야한다.
<자료형> <변수 이름> = <값>;
위처럼 선언하면 되는데, 파이썬과 달리 c언어는 변수를 선언할 때 꼭 값을 넣어줄 필요가 없다. 값이 안들어 있는 변수를 선언할 때는 그저 <자료형> <변수 이름>; 만 써주면 된다.
변수 이름 규칙
- 영문 문자, 숫자
- 대소문자 구분
- 문자부터 시작해야함(숫자 시작 X)
- _로 시작할 수 있음
- c언어에서 사용되는 함수나 키워드(for, while, if 등)는 함수 이름이 될 수 없음
-자료형
int, char long, short : 정수(각각 저장할 수 있는 크기가 다름)
float, double : 실수(각각 저장할 수 있는 소수점이 다름)
bool : 참과 거짓
void : 형태가 없는 자료형
char는 문자 자료형인줄 알았는데, 정수 자료형이었을 줄은 몰랐다.
이 밖에 자료형이 더 있지만, 그 부분은 해당 자료형이 등장할 때 정리하는 식으로 하겠다.
-선언
선언 방법으로는 변수만 선언하는 방법 <자료형> <변수명>; , 선언과 동시에 값을 할당(초기화)하는 방법 <자료형> <변수명> = <값>; , 같은 자료형의 변수 여러 개를 동시에 선언만하는 방법 <자료형> <변수명1>, <변수명2>... , 같은 자료형의 변수 여러 개를 동시에 선언하고 동시에 값을 할당 하는 방법 <자료형> <변수명1> = <값1>, <변수명2> = <값2>... ;이 있다.
먼저 예제의 코드들은 int형, 즉 정수 자료형으로 선언했다.
5행은 변수를 선언만 하고 10행에서 값을 할당해 준다. 6행은 선언과 동시에 값 2를 변수에 할당해 준다. 7행은 같은 자료형인 변수 두 개를 동시에 선언을 해주고 11, 12행을 통해 값을 할당해 준다. 8행은 변수 두 개를 동시에 선언을 해줌과 동시에 각각 값을 할당해 준다.
그리고 14행에서 서시 지정자 %d를 사용해서 각각의 변수들을 화면에 출력해주는데, 이때 이 서식 지정자 %d는 파이썬에서의 %d와 똑같이 %d 부분을 정수형 값으로 바꿔준다.
참고로 파이썬에서 같은 값을 할당해 줄때 사용했던 <변수1> = <변수2> = <값>을 c언어에서 써봤지만 안되는 것을 볼 수 있다. 또한 <변수1>, <변수2> = <값1>, <값2>도 안된다. c언어에서 같은 값을 할당해 주려면 <변수1> = <값1>, <변수2> = <값2> 이렇게 따로따로 해주는 방법밖에 없는 것 같다.
변수 선언 방법은 <자료형> <변수명> = <값>;인데 여기서 값을 안 써주면 할당 없이 선언만 할 수 있다.
언뜻보면 c와 d중에서 햇갈릴 수 있지만, c언어에서는 ;이 필요하다는 것을 기억해야한다.
답 : c
사용할 수 없는 변수 이름은 _를 제외한 특수문자들이나 c언어에서 사용되는 키워드, 변수이름이 숫자로 시작 등이다.
c : 숫자로 시작할 수 없다. X
e : 숫자로 시작할 수 없다. X
f : c언어에서 사용되는 키워드는 사용할 수 없다. X
h : c언어에서 사용되는 키워드는 사용할 수 없다. X
변수 여러 개를 한 번에 선언하는 방법은 <자료형> <변수1>, <변수2>... ;이다.
답 : d
변수를 선언하면서 값을 초기화(할당)하는 방법은 <자료형> <변수> = <값>이다.
답 : b
int형 변수 num1, num2, num3를 한번에 선언하고 동시에 각각 값을 10, 20, 30을 할당(초기화)해야한다. 같은 자료형인 여러 개의 변수를 선언과 동시에 초기화하는 방법은 <자료형> <변수1> = <값1>, <변수2> = <값2>..이다.
#include <stdio.h>
int main()
{
int num1 = 10, num2 = 20, num3 = 30;
printf("%d %d %d\n", num1, num2, num3);
return 0;
}
num1에는 10, num2에는 20, num3에는 30을 선언과 동시에 할당해 줬다.
num1은 이미 선언돼 있고, num2, num3를 선언하면 되는데, 각각 값을 10, 20, 30씩 할당해서 10 20 30이 출력되게 하는 것이 목적이다.
#include <stdio.h>
int main()
{
int num1;
num1 = 10;
int num2 = 20, num3 = 30;
printf("%d %d %d\n", num1, num2, num3);
return 0;
}
num1은 이미 선언되어 있으므로 값만 10 할당해줬고, num2와 num2는 한줄로 선언하면서 동시에 20, 30을 할당해 줬다.
-debugger(디버거)-
디버거는 버그(bug)를 제거(de-)하는 도구라는 뜻으로 동적으로 프로그램의 내부 상황을 파악할 수 있어서, 버그를 찾는데 도움을 준다. ollydbg, x64dbg, gdb 등 리버싱을 공부하면서 사용하는 툴들 역시 디버거다.
여기서 버그란 프로그램이 의도하지 않은 동작을 일으키는 것을 말한다.
-break point(중단점, bp)
중단점은 특정 지점에서 프로그램의 실행을 멈추는데 사용한다.
visual stdio 기준으로 중단점은 위에 표시한 빨간 부분을 클릭하거나, 코드 수정기에서 마우스 오른쪽 클릭 -> 중단점 을 통해 중단점을 걸 수 있다.
이렇게 원하는 부분에 중단점을 건 상태로 실행(ctrl + f5)이 아닌 로컬 Windows 디버겅 버튼(f5)을 누르면 프로그램이 실행되면서 중단점을 건 부분에서 실행이 멈춘다. 디버거 모드에서 왼쪽 하단의 뷰를 통해, 현재 선언되어 있는 변수와 할당된 값을 알 수 있는데, 위 사진의 실행 시점에서는 num1만 선언됐으므로 num1만 존재하는 것을 볼 수 있다. 값의 경우 이상한 값이 들어가 있는데, 이는 변수를 선언만 하고 초기화를 안해줬기 때문에 쓰래기 값(garbage value)가 들어가 있는 것이다.
f10을 누른다면 코드를 한 줄씩 실행할 수 있다. f10을 두번 눌러서 2줄의 코드(7~8)을 실행하니, 왼쪽 하단 뷰에 num1의 값이 10으로 변하고, num2, 3가 선언된고 각각 값이 20, 30이 할당된 것을 볼 수 있다.
10행까지 f10을 눌러 실행하면 10행의 printf 함수가 실행되어, 콘솔창에 num1,2,3의 값이 출력되는 것을 볼 수 있다.
디버깅을 끝내려면 shift+f5를 누르면 된다.
VisualStudio 디버거 단축키
- 중단점 삽입/삭제 : F9
- 디버깅 시작 : F5
- 디버깅 중지 : Shift + F5
- 한 줄씩 코드 실행(함수 호출 부분을 만난다면 함수 내부로 들어가지 않고 함수 코드 전체를 실행) : F10
- 한 줄씩 코드 실행(함수 호출 부분을 만난다면 함수 내부로 들어감) : F11
디버거는 변수의 내용뿐만 아니라 프로그램의 동작 과정도 확인할 수 있으니, 디버거를 사용하는 것이 좋다고 한다.
02-16 (Unit 6.1 ~ Unit 11.7)
-정수 자료형-
c언어의 정수 자료형은 크게 char와 int로 나눠진다. 크기에 제약이 느껴지지 않던 파이썬의 int형과 달리, c언어의 정수형 자료형 앞에 부호 키워드인 signed, unsigned와 크기인 short, long을 붙여서 특성과 크기를 정의할 수 있다.
signed : 부호 있는 정수(- ~ +)
unsigned : 부호 없는 정수(0 ~ ???)
위 사진은 c언어 코딩도장 7.0에 있는 자료형 사진이다. 각각 특성과 자료형에 따른 크기가 달라지는걸 볼 수 있다.
singned는 - ~ +인 것을 볼 수 있고, unsigned 같은 경우 0 ~ ??? 즉 -를 사용하지 않는 대신 담을 수 있는 + 값의 크기가 signed보다 더 큰 것을 볼 수 있다. long과 short를 각각 int 앞에 붙여주면(생략 가능하다) 본래 int형만 썻을 때의 크기보다 더 커지거나 작아지는 것을 볼 수 있다. 또한 long 같은 경우 long을 두 번 써준다면(long long) 본래 long int, int의 크기보다 훨씬 더 커지는 것을 볼 수 있다.
새롭게 안 사실인데 long은 운영체제 비트수와 플랫폼마다 크기가 다르다. Windows는 32, 64비트 모두 4로 동일한데, 리눅스 같은 경우 32비트는 4, 64비트는 8이다.
-%d,%ld, %lld, %u, %lu, %llu
%d, %ld, %lld, %u, %lu, %llu는 서식지정자다. 앞서 정리했듯이 %d 같은 경우 파이썬과 똑같이 decimal의 약자지만, 파이썬과 달리 int 뿐만 아니라 char, short까지 나타내준다.
%d : decimal의 약자. int, char, short int 형을 나타냄.
%ld : long decimal의 약자. long int형을 나타냄.
%lld : long long decimal의 약자. long long int형을 나타냄
%u : unsigned decimal의 약자. unsigned (char, int, short int)형을 나타냄.
%lu : long unsigned decimal의 약자. unsigned long int형을 나타냄.
%llu : long long unsigned decimal의 약자. unsigned long long int형을 나타냄.
%d, %ld, %lld, %u, %lu, %llu 서식 지정자로 각각 char, short, int, long, long long, unsigned (char, int, short), unsigend long, unsigend long long 변수의 값을 출력해봤다. 해당 변수의 값대로 잘 출려고디는 것을 볼 수 있다.
참고로 long int, short int, long long int는 int를 생략할 수 있으므로, int는 없이 썻다.
리눅스 c 컴파일러인 gcc의 경우 컴파일 경고가 뜰 수 있는데, 이럴 경우 각 자료형 크기에 맞는 ld, lld를 값 뒤에 붙여준다면 해결된다.
-overflow, underflow
오버플로우는 자료형이 저장할 수 있는 최대값보다 값이 커질 때 일어나는 것이다. 최대값보다 커질 경우 해당 자료형의 최소값부터 시작하게 된다.
언더플로우는 자료형이 저장할 수 있는 최소값보다 값이 작아질 때 일어나는 것이다. 최소값보다 작아질 경우 해당 자료형의 최대값부터 시작하게 된다.
가장 작은 크기를 가지는 char로 실습했다. char가 저장할 수 있는 범위는 -128 ~ 127인데, 5행에서 127을 char a에 할당하고, 6행에서 a에 1을 더해주니 출력 결과 128이 아닌 char 자료형의 최소값인 -128로 변해버렸다. 그 다음 8행에서 이번에는 -1을 해주니 -129가 아닌 char 자료형의 최대값인 127로 변해버렸다.
unsigened 역시 실습해봤다. unsigend char는 0 ~ 255의 값을 저장할 수 있다. 11행에서 unsigend char b에 최대값이 255를 넣어주고 12행에서 1을 더해주니 출력 결과 256이 아닌 최소값인 0이 나왔다. 반대로 최소값인 0인 상태에서 -1을 해주면 -1이 아닌 다시 최대값인 255로 변하는 것을 볼 수 있다.
듣기로는 오버워치라는 게임이 예전에 언더플로우에 대한 처리를 안해줘서, 랭킹전에서 계속 진다면 랭킹 상위로 올라갈 수 있었던 버그가 있었다.
-sizeof-
sizeof는 자료형의 크기를 byte 단위로 구해서 정수형으로 반환해주는 기능을 한다.
sizeof <표현식>
sizeof(<자료형>)
sizeof(<표현식>)
이런 형태로 사용할 수 있는데, sizeof <표현식>의 경우는 별로 못봤고, 보통 sizeof(<표현식>)이나 sizeof(<자료형>)을 많이 사용하는 것 같다.
char형 short형 int형을 sizeof의 3가지 방법으로 각각 크기를 확인해봤다.
char의 크기는 1byte, short의 크기는 2byte, int의 크기는 4byte인데, 출력 결과를 보니 해당 자료형의 크기를 잘 구해준 것을 볼 수 있다.
-<자료형>_MAX, <자료형>_MIN-
char, short, int, long long 자료형의 최대값, 최소값을 구하는 방법은 <자료형(char, short, int, long long)>_MAX 혹은 MIN을 써주면 된다. unsigned는 자료형 앞에 U만 붙여주면 된다. 이 MAX, MIN은 limits.h라는 헤더를 include해야지 사용이 가능하다.
char, short, int, long, unsigned char를 대상으로 _MAX, MIN을 써보니 각 자료형의 최대, 최소값을 할당해주는 것을 볼 수 있다. 참고로 unsigned 같은 경우 최소값이 무조건 0이라 따로 최소값을 제공해주진 않는 것 같다. (사진에는 실수로 모두 서식 지정자 %d를 사용했는데 long, unsigned char의 경우 각각 %ld와 %u를 써줘야한다.)
-크기가 표시된 자료형-
char, short, int 등의 자료형은 처음에는 괜찮았지만 16비트, 32비트, 64비트 CPU, 운영체제가 나오면서 자료형이 담당하는 크기가 달라져 혼란을 가져왔다. 그래서 이를 해결하기 위해 stdint.h라는 헤더파일이 추가됐다.
이 stdint.h를 사용하면 int8_t, int16_t.. 이런식으로 int의 크기를 지정해줄 수 있다. 크기는 8, 16, 32, 64가 있다. int8_t는 8바이트, int16_t는 16바이트다. unsigned 같은 경우 단순히 맨앞에 u를 붙어주면 unsigned로 사용가능하다.
위처럼 int의 크기를 지정해줘서 자료형을 사용할 수 있다. 또한 limits.h 써야만 사용 가능했던 _MAX, _MIN은 stdint.h 안에서 int8 등, 크기가 적혀있는 자료형 한에서만 _MAX, _MIN을 제공해주기 때문에, 위 사진처럼 limits.h를 include해줄 필요는 없다. 같이 쓸 때는 INT8_MAX 이런식으로 최대, 최소값을 할당해줄 수 있다. 마지막으로 int32까지는 서식지정자 %d를 쓰지만 64는 %lld, unsigned는 %u를 써줘야한다.
이 stdint.h를 include해서 사용하는 자료형들은 크기를 정확하게 표현해야 하는 파일 압축, 네트워크 프로그래밍을 할 때 유용하다고 한다. 또한 일반 프로그래밍에서도 처음부터 stdint.h를 사용하는 것이 좋다고 한다.
c : int형은 4바이트다.
a, b의 최솟값이 기억이 안났지만 d를 보니 unsigned인데 최솟값이 0이 아니라서, 고민할 필요도 없이 답을 알아냈다.
답 : d
각 자료형의 크기가 기억이 안나서, 코드를 쳐서 풀었다. CHAR_MAX 부분이 짤렸는데, CHAR_MAX는 127이다.
답 : b
정수 자료형의 크기를 구하는 법은 sizeof(<정수 자료형>) 또는 변수를 하나 만들어서 sizeof <정수형 변수명>, sizeof(<정수형 변수명>)으로 해주면 된다. e는 자료형을 써줬지만, 괄호를 안써줬다.
답 : e
a : char X
b : unsigned short X
d : unsigned long X
e : int X
답 : c
크기가 표시된 자료형을 사용할 때 include 해야하는 헤더 파일은 바로 stdint.h다.
답 : e
오버플로우를 이용한 문제다. 출력결과는 0 0 -9223....이 나와야하므로, num1은 unsigned, num2도 unsigned, num3는 0이 아니므로 signed형을 주면 될 것 같다.
#include <stdio.h>
int main()
{
unsigned char num1 = 256;
unsigned short num2 = 65536;
long long num3 = 9223372036854775808;
printf("%u %u %lld\n", num1, num2, num3);
return 0;
}
오버플로우 되도록 각 값의 크기에 따라 자료형을 넣었다. num1은 char, num2는 short, num3는 long long.
num1의 자료형 크기 + num2의 자료형 크기 + int형의 크기(4) = 14가 나와야한다. 즉 num1의 크기 + num2의 크기가 10이어야한다. char : 1, short : 2, int : 4, long : 4, long long : 8이므로 short와 long long을 사용하면 될 것 같다.
#include <stdio.h>
int main()
{
short num1;
long long num2;
printf("%d\n", sizeof(num1) + sizeof(num2) + sizeof(int));
return 0;
}
num1, num2의 자료형을 각각 short와 long long으로 정해줬다.
최댓값을 찾아서 기입해주거나 limits.h 헤더를 이용해 _MAX를 써주면 될 것 같다.
#include <stdio.h>
#include <limits.h>
int main()
{
char num1 = CHAR_MAX;
short num2 = SHRT_MAX;
int num3 = INT_MAX;
long num4 = LONG_MAX;
long long num5 = LLONG_MAX;
printf("%d %d %d %ld %lld\n", num1, num2, num3, num4, num5);
return 0;
}
먼저 limits.h를 include 했고, short에는 SHRT_MAX, long long에는 LLONG_MAX를 써주었다.
stdint.h 헤더 파일을 include한 후, 크기가 표시된 자료형으로 해당 자료형의 최댓값을 출력하면 된다.
#include <stdio.h>
#include <stdint.h>
int main()
{
int8_t num1 = INT8_MIN;
uint16_t num2 = UINT16_MAX;
int32_t num3 = INT32_MAX;
uint64_t num4 = UINT64_MAX;
printf("%d %u %d %llu\n", num1, num2, num3, num4);
return 0;
}
먼저 stdint.h 헤더 파일을 include 하고, 할당된 최댓값의 자료형이 uin64이므로 uint64_t로 num4를 선언해줬다.
주어진 값을 오버플로우를 일으켜 0, 0, -128을 출력하면 된다.
#include <stdio.h>
int main()
{
unsigned short num1;
unsigned int num2;
char num3;
num1 = 65536;
num2 = 4294967296;
num3 = 128;
printf("%u %u %d\n", num1, num2, num3);
return 0;
}
num1, num2는 오버플로우해서 0이되야하므로 unsigned로 선언해줬고, num3는 -로 값이 나와야하기 때문에, 최솟값이 -128인 char형으로 선언해줬다.
num1의 자료형 크기 + num2의 자료형 크기 + long long의 크기(8)이 11이 나와서 11을 출력하게 하는 것이 목적이다.
#include <stdio.h>
int main()
{
char num1;
short num2;
printf("%d\n", sizeof(num1) + sizeof(num2) + sizeof(long long));
return 0;
}
1 바이트 크기인 char와 2바이트 크기인 short를 num1, num2의 자료형으로 정해줘서 풀었다.
_MIN, _MAX로 출력 결과를 출력해야하는데, 이미 코드가 다 완성되어 있다. 작성할 부분은 헤더 include 부분밖에 없으므로 limits.h 헤더 파일을 include 하도록 작성하면 되겠다.
#include <stdio.h>
#include <limits.h>
int main()
{
char num1 = CHAR_MIN;
unsigned short num2 = 0;
int num3 = INT_MIN;
unsigned long num4 = 0;
long long num5 = LLONG_MIN;
printf("%d %u %d %lu %lld\n", num1, num2, num3, num4, num5);
return 0;
}
자료형의 최댓값, 최솟값이 들어있는 헤더 파일인 limits.h를 include했다.
이 역시 include 부분만 작성하면 된다. 코드를 보면 크기가 표시된 자료형을 사용하고 있으므로, stdint.h를 include하면 되겠다.
#include <stdio.h>
#include <stdint.h>
int main()
{
int8_t num1 = INT8_MIN;
uint16_t num2 = UINT16_MAX;
uint32_t num3 = 0;
int64_t num4 = INT64_MAX;
printf("%d %u %u %lld\n", num1, num2, num3, num4);
return 0;
}
크기가 표시된 자료형을 사용할 수 있도록 해주는 stdint.h를 include했다.
-실수형-
파이썬에서는 실수를 float로 표현했었다. c언어도 마찬가지로 float로 표현하지만 double과 long double이라는 것이 더 있다. float, double과 long double은 정수형의 int long long 등과 마찬가지로 크기 차이다. float의 경우 단정밀도 부동 소수점을, double과 long double은 배정밀도 부동소수점을 저장한다.
위처럼 float는 4바이트, double은 8바이트, long double도 8바이트의 크기를 가진다.
실수를 float, double, long double에 각각 저장했다. 파이썬 같은 경우 실수를 아무런 규칙 없이 저장해줬지만 c언어의 float, long double 같은 경우, 각각 해당 자료형에 값을 저장할 때 끝에 f, l를 붙여줘야한다.
위는 float, double, long double 형 변수에 각각 실수 값들을 저장하는 것이다. 저장한 값들을 출력할 때는 float, double의 경우 %f 서식 지정자를 사용해야하고, long double은 %L 서식 지정자를 사용해야한다.
double과 long double은 겉보기엔 같아보이지만, 정수형의 long int와 마찬가지로 리눅스 환경에서 long double의 크기가 달라진다.
참고로 1.f = 1.0f, .1f = 0.1f 이런식으로 소수점 앞이나 뒤의 0은 생략이 가능하다.
-지수 표기법-
아주 큰 숫자나 아주 작은 숫자를 표기할 때는 지수 표기법을 사용한다.
<실수>e+<지수> 이렇게 사용한다면 <실수> * 10^<지수>다. 예를들어 3.2e+2면 3.2*100으로 320이 된다.
<실수>e-<지수> 이렇게 사용한다면 <실수> * 1/10^<지수>가 된다. 예를들어 3.2e-2면 3.2*1/100으로 0.032가 된다.
이 지수 표기법으로 표현된 실수 역시 float, double, long double과 같은 c언어의 실수 자료형에 저장할 수 있다.
특별한 것 없이 그저 값 할당할 때 지수 표기법으로 써주면 되는 것인데, 조심해야할 점은 지수 표기법 역시 float와 long double은 맨 마지막에 꼭 f, l을 붙여줘야한다. 출력은 서식 지정자 %f와 %L을 사용하면 지수 표기식 연산이 적용된 값으로 출력되는데, 만약 지수 표기식 그대로 출력하고 싶다면 float, double의 경우 %e, long double의 경우 %Le를 써주면 된다.
위 사진은 지수 표기법으로 저장한 float, double, long double형 변수를 %f, %L과 %e, %Le로 각각 출력해본 모습이다. %f, %L은 지수 표기식이 연산된 값으로 출력되고, %e, %Le는 지수 표기식 그대로 출력된다.
참고로 지수가 양수인 경우에는 +를 써주지 않고 생략해서 사용 가능하다.
3.2e+5의 경우 3.2e5로..
-실수의 자료형 크기 및 최댓,최솟값-
자료형 크기 구하는 법과 최댓,최솟값을 사용하는 방법은 정수형과 비슷하다. 자료형의 크기는 sizeof를 사용해서 구할 수 있고, 최댓, 최솟값은 float.h를 include하고 float는 FLT_MAX, FLT_MIN, double은 DBL_MAX, DBL_MIN, 그리고 long double은 LDBL_MAX, LDBL_MIN으로 사용하면 된다.
참고로 실수형의 최댓, 최솟값은 %e, %Le를 사용한다면 지수 표기법으로 출력할 수 있다.
위 사진처럼 sizeof(<실수 자료형>)으로 해당 실수 자료형의 크기를 구할 수 있다. 결과를 보면 float는 4, double, long double은 8byte가 나왔다.
최댓, 최솟값은 limits.h가 아닌 float.h를 include해야지 사용이 가능하다. <실수 자료형 줄임>_MAX, _MIN을 사용하면 해당 실수 자료형의 최댓, 최솟값이 구해진다. 또한 이를 %f, %L 서식 지정자로 출력하면 최댓, 최솟값이 그대로 나오지만 %e, %Le 서식 지정자를 사용한다면 지수표기법으로 출력 해주는 것을 볼 수 있다.
-실수형의 overflow, underflow-
실수형 역시 오버, 언더플로우가 일어날 수 있다. 실수형의 오버, 언더플로우는 조금 특이한게 오버플로우는 큰수를 곱해줘서 저장할 수 있는 범위를 넘어주는 식으로 발생시키고, 언더플로우는 큰 수로 나눠서 아주 작은수를 만들어서 일으킨다. 또한 오버, 언더플로우가 일어나면 최댓값 -> 최솟값, 최솟값 -> 최댓값으로 되지 않고, 오버플로우는 무한이라는 뜻의 infinity 약자인 inf를 출력시켜주고, 언더플로우는 0 또는 쓰래기 값을 출력해준다고한다.
또한 실수의 경우 양수, 음수의 오버, 언더플로우가 따로 있다고 한다. 위에 정리한 것이 양수의 경우고, 음수일 경우에는 오버플로우 발생시 0 또는 쓰래기 값, 언더플로우 발생시 inf가 출력된다.
float의 최댓값에 * 100000, float의 최솟값에 / 100000를 해주니 오버, 언더플로우가 일어났다. 오버플로우 경우 inf가 출력됐고, 언더플로우는 0.000....이 출력됐다.
참고로 파이썬에서도 마찬가지였지만 컴퓨터는 1과 1.0을 정수와 실수로 구분한다.
단정밀도 부동소수점은 float다.
답 : d
-0.001582는 -1.582e-3과 같다.(-1.582 * 1/1000 = -0.001582)
답 : e
float는 뒤에 f를 long double은 뒤에 l, double은 그냥 써주면 된다.
d : 2.97f가 맞다. X
a : double은 8바이트다. X
c : float는 4바이트다. X
d : long double은 8바이트다. X
e : long double은 8바이트다. X
답 : b
float형 변수를 지수표기법으로 출력하는 방법은 %e를 써주면 된다.
답 : c
실수형 변수를 할당된 값에 맞게 각각 선언해주면 될 것 같다.
#include <stdio.h>
int main()
{
float num1 = 1.97f;
long double num2 = 5.524218l;
double num3 = 3792.8e+4;
printf("%f %Lf %f\n", num1, num2, num3);
return 0;
}
num1에 할당된 값에는 f가 붙었으므로 float, num2에 할당된 값에는 l이 붙었으므로 long double, num3에 할당된 값은 f, l 둘다 안붙었으므로 double을 넣어줬다.
자료형의 크기가 8과 4로 출력되게끔 num1, num2의 자료형을 정해주는 것이 목적이다.
#include <stdio.h>
int main()
{
double num1 = 0.4284;
float num2 = 2.7f;
printf("num1의 크기: %d, num2의 크기: %d\n", sizeof(num1), sizeof(num2));
return 0;
}
num1의 크기는 8이 되야하므로 double이나 long double을 써주면 되지만, long double의 경우 운영체제마다 크기의 차이가 있으니 확실하게 double을 써주었다. num2의 크기는 4가되야하므로 4byte 크기의 float를 써주었다.
그냥 초기값으로 주어진 값들의 뒤에 뭐가 붙었는지만 보고도 풀 수 있을 것 같다.
실수 자료형의 최솟, 최댓값을 출력하는 것이 목적이다. 실수 자료형의 최솟, 최댓값은 float.h 헤더파일을 통해 구할 수 있으므로 먼저 float.h를 include하고 각 자료형에 맞도록 최댓, 최솟값을 쓰면 될 것 같다.
#include <stdio.h>
#include <float.h>
int main()
{
float num1 = FLT_MAX;
double num2 = DBL_MIN;
long double num3 = LDBL_MAX;
printf("%.2f\n", num1);
printf("%e\n", num2);
printf("%Le\n", num3);
return 0;
}
언뜻보기에는 출력결과 모두 양수 같지만, 지수 표기식에 주의해야한다. 2번째 실행결과는 -가 안붙었지만 지수 표기 부분에 e-308이 붙었으므로 이 부분에 해당하는 num2는 최솟값을 넣어줘야한다. 3번째는 e+308이므로 최댓값을 넣어주면 된다. 따라서 double형인 num2에는 DBL_MIN을, long double형인 num3에는 LDBL_MAX를 할당해줬다.
num1을 언더플로우 시켜서 0.000000e+00을 출력하게 만드는 것이 목적이다.
#include <stdio.h>
#include <float.h>
int main()
{
float num1 = FLT_MIN;
num1 = num1/1000000000000000000;
printf("%e\n", num1);
return 0;
}
FLT_MIN값이 들어간 num1을 엄청나게 큰 값으로 나눠주면 언더플로우 되서 0.000000e+00이 출력된다.
1.80000..., 2.90000, 3.700000...을 첫 줄에 출력되게 하고, 둘째 줄에 num1, num2의 크기인 4, 8이 출력되게 하는 것이 목적이다.
#include <stdio.h>
int main()
{
float num1 = 1.8f;
double num2 = 2.9;
long double num3 = 3.7l;
printf("%f %f %Lf\n", num1, num2, num3);
printf("%d %d\n", sizeof(num1), sizeof(num2));
return 0;
}
num1은 크기가 4이어야하므로 float형으로 선언한고 1.8를 할당했다. num2의 경우 크기가 8이 나와야하는데, 크기가 8인 실수 자료형은 double, long double이 있다. 하지만 첫 번째 printf를 보면 %f로 num2의 값을 출력하므로 double을 써주고 값은 2.9를 할당했다.
double과 long double로 각각의 최솟값과 최댓값을 담을 변수만 선언했으므로, 나머지 4줄에서 float.h가 제공하는 최솟, 최댓값을 이용해 값을 넣어주면 된다.
#include <stdio.h>
#include <float.h>
int main()
{
double doubleMin;
double doubleMax;
long double longDoubleMin;
long double longDoubleMax;
doubleMin = DBL_MIN;
doubleMax = DBL_MAX;
longDoubleMin = LDBL_MIN;
longDoubleMax = LDBL_MAX;
printf("%e %e\n", doubleMin, doubleMax);
printf("%Le %Le\n", longDoubleMin, longDoubleMax);
return 0;
}
만들어진 네개의 변수에 각각의 자료형에 맞는 최솟, 최댓값을 넣어줬다. double형의 경우 DBL_MIN과 DBL_MAX, long double형의 경우 LDBL_MAX, LDBL_MIN을 사용했다.
-inf 출력하는 것, 즉 실수의 음수 언더플로우를 일으키는 것이 목적이다.
#include <stdio.h>
#include <float.h>
int main()
{
float num1 = -FLT_MAX;
num1 = num1 * 1000.0f;
printf("%f\n", num1);
return 0;
}
주허진 num1에 FLT_MAX가 아닌 -FLT_MAX를 넣어줬다. 이유는 그냥 FLT_MAX를 넣어준다면, 양수 오버플로우가 일어나기 때문에 inf가 출력되지만, -FLT_MAX 즉 음수로 만들어준다면 1000.0f를 곱했을 때 음수 언더플로우가 일어나기 때문에 -inf가 출력된다.
-문자형-
파이썬에서는 str 자료형으로 문자들을 저장했다. c언어의 경우 앞서 정수형에서 배운 char 자료형이 문자 저장에 사용된다. 처음에는 char가 정수형이라 배워서 의아했지만 문자 저장 방식이, 문자 자체를 저장하는게 아닌 문자에 해당하는 정수 값(아스키)을 저장하기 때문에, 어쨌든 정수형 변수인 것이다.
저장할 수 있는 범위는 정수형에서 다뤘던 범위와 똑같다. unsigned char는 바이트 단위 데이터를 저장한다고 하는데, 코딩을 할 때 unsigned char를 사용하는 경우는 거의 못본 것 같다.
위 사진은 코딩도장에 나와있는 10진수와 16진수 아스키 코드표다. 아스키 코드란 각 정수 값이 어떤 문자에 해당하는지 정해놓은 규칙이다. 리버싱을 할 때는 16진수 아스키 코드를 자주 다루게 되므로 알파벳이나 숫자 아스키 정도는 알아두는 것도 괜찮을 것 같다.
-char
char형 변수에는 문자나 아스키 코드 값 10 진수, 16진수를 저장할 수 있다.
char <변수명> = '<문자>'
char <변수명> = <아스키 코드 값(10진수 or 16진수)>
위는 char 형 변수 a, b, c에 문자, 아스키 코드 방식으로 문자 'a'를 넣은 것이다. 변수 a에는 문자 a를 작음 따음표로 감싸서 넣어줬고, b는 a의 아스키 코드 10진수 값인 97을 넣었다. c는 97의 16진수 값인 0x61를 넣었다.
16진수 값을 표현하거나 할당할 때는 앞에 0x를 붙여주면 컴퓨터가 16진수로 인식한다.
-%c, %x, %X
%c는 문자를 표현해주는 서식 지정자다. %x는 16진수를 표현해준다. %X도 마찬가지로 16진수를 표현하지만 %x와는 조금 다르게 16진수의 영문자를 대문자로 표시해준다.
위에서 선언한 char형 변수 a, b, c를 %c, %d, %x로 출력해봤다. %c로 출력하면 문자 그대로나, 해당 아스키값에 대응하는 문자를 출력해주고, %d로 출력하면 해당 문자의 아스키 10진수 값을 출력해준다. %x로 출력하면 아스키 16진수 값을 출력해주는 것을 볼 수 있다.
a, b, c에 들어간 값은 각기 다르지만, 결국 문자 'a'를 가리키기 때문에, %c, %d, %x로 각각 출력했을 때, 결과가 같음을 알 수 있다.
참고로 char a = '0';과 char a = 0;은 다르니 조심해야한다. 첫 번째는 그냥 문자 0을 저장하는 거지만, 두 번째는 아스키 10진수 값 0을 저장하는 것이기 때문에 출력했을 때 0에 해당하는 공백이 나온다.
-문자 연산
문자 그대로 저장하는 파이썬과 달리, c언어는 문자 그대로가 아닌 해당 문자의 아스키 값(정수)으로 저장된다고 정리했었다. 즉 정수로 저장되기 때문에 문자와 정수가 연산이 가능하다.
분명 변수 a에는 문자열 값이 들어 있지만, a + 1이 오류 없이 연산되는 것을 볼 수 있다. a에 들어간 'a'의 아스키 값은 97이므로 97에 1을 더한 98이 결과로 나왔다. char에 저장된 문자만 해당하는게 아닌 그냥 문자 자체도 연산이 된다. 7행은 'a'에 +1을 해줬는데, 6행의 결과와 똑같이 나오는 것을 볼 수 있다. 이 나온 결과를 문자형으로 보고 싶다면 %c를 써줘서 해당 값을 아스키 값에 해당하는 문자로 바꿔서 출력할 수도 있다.
모든 면에서 파이썬이 c언어보다 훨씬 편하다고 생각했지만, 이런 면에서는 c언어도 편한 것 같다. 파이썬 같은 경우 문자를 연산에 사용하려면 ord를 써서 해당 문자를 16진수 아스키 코드 값으로 바꾸고, int(<16진수>,16)을 써서 해당 16진수 값을 10진수로 바꾸고 나서야 위처럼 계산이 가능하다. 또한 계산한 값을 다시 문자로 만드려면 hex 함수를 사용해 해당 값을 16진수로 바꾸고 chr라는 함수를 써서 문자로 바꿔줘야한다.
-제어문자
\n, \r, \t와 같은 제어 문자들도 저장이 가능하다.
위처럼 개행의 역할을 하는 \n을 char형 변수 a에 저장하고 %c 서식 지정자를 이용해 출력을 한다면, 문자 \n이 출력되는 것이 아닌 제어문자로서의 \n의 기능을 수행해준다. 결과를 보면 행이 바뀌어진 것을 볼 수 있다.
위 사진은 코딩도장의 제어 문자 표다. 아스키 값과 기능이 설명되어 있다. 기능은 파이썬과 똑같은 것을 볼 수 있다.
당연하지만 제어 문자를 저장하고 %d나 %x로 출력한다면 제어의 기능을 수행하는 것이 아닌, 해당 제어 문자의 아스키 값이 출력된다.
참고로 16진수 란에는 a가 출력됐는데, 이는 문자 'a'가 아닌 16진수 a다.
문자는 '<문자>' 이런식으로 작음 따음표로 감싸서 저장해야한다.
답 : c
%d 서식 지정자로 'd'의 아스키 값을 출력하도록 했다. 출력 결과를 보니 d의 아스키 값은 100이다.
답 : e
줄바꿈의 기능이 있는 제어문자는 파이썬과 똑같이 \n이다.
답 : b
탭의 기능을 수행하는 제어문자를 c1에 할당해주는 것이 목적이다. 탭의 기능을 수행하는 제어문자는 파이썬과 똑같이 \t이므로 \t를 c1에 넣어주면 될 것 같다.
#include <stdio.h>
int main()
{
char c1 = '\t';
printf("Hello");
printf("%c", c1);
printf("world\n");
return 0;
}
\t만 넣어주면 안되고, 문자이므로 '\t' 이렇게 작음 따음표로 감싸서 넣어줘야한다.
2와 5를 각 줄에 출력하는 것이 목적이다. 문제는 이미 char형 변수 c1, c2에는 2와 5가 들어가 있는데, 이 값들이 문자 값이 아닌 정수 값으로 들어갔다. 출력을 수행하는 코드 부분을 보면 %c로 출력하게 되있는데, 이대로면 2와 5 아스키 값에 해당하는 문자가 출력되기 때문에 문자 2, 5의 아스키 값을 구해서, c1, c2의 값을 바꿔줘야한다.
문자 2, 5의 서식 지정자 %d를 사용해서 아스키 값을 구했다. 50, 53이므로 c언어에서 문자와 정수의 연산이 가능하다는 점을 이용해 c1, c2의 값이 50, 53이 되도록 알맞은 값을 더해주면 될 것 같다.
#include <stdio.h>
int main()
{
char c1 = 2;
char c2 = 5;
printf("%c\n", c1 + 48);
printf("%c\n", c2 + 48);
return 0;
}
2 + 48 = 50, 5 + 48 = 53, 이렇게 각각 48을 더해줘서 출력하고자 하는 문자의 아스키 코드 값으로 바꿔서 출력했다.
제어문자를 이용해 결과 화면 처럼
Hello world
Hello world
이렇게 출력하는 것이 목적이다. 또한 Hello, world 사이는 탭으로 띄운 것이라고 한다.
주어진 변수가 char형 c1, c2, c3이므로 각각의 제어문자를 형식에 맞게 넣어주면 될 것 같다.
#include <stdio.h>
int main()
{
char c1;
char c2;
char c3;
c1 = '\t';
c2 = '\n';
c3 = '\t';
printf("Hello%cworld%cHello%cworld\n", c1, c2, c3);
return 0;
}
주어진 printf 식이 "Hello%cworld%cHello%cworld\n", c1, c2, c3 이므로 첫 번째 %c에 해당하는 c1에는 탭 기능을하는 '\t'를, 두 번째 %c에 해당하는 c2에는 개행의 기능을 하는 '\n'을, 세 번째 %c에 해당하는 c3에는 탭 기능을 하는 '\t'를 넣어줘서 풀었다.
char형 변수 c1에 들어간 'a'를 대문자 'A'로 바꿔서 출력하는 것이 목적이다.
#include <stdio.h>
int main()
{
char c1 = 'a';
printf("%c\n", c1 + ('A'-'a'));
return 0;
}
코드를 한번 더 써서 대문자 A의 아스키 값과, 소문자 a의 아스키 값을 확인하고 비교해, 차이만큼 더해줄 수도 있겠지만, 그 과정이 귀찮아서 c1 + ('A'-'a') 이렇게 대문자 A와 소문자 a를 뺀 값을 c1에 더해줘서 c1의 값을 대문자 A의 아스키 값으로 만들어 출력했다.
-상수-
상수는 변하지 않는 값이다. 앞에서 변수를 선언했을 때, 해당 변수의 값을 얼마든지 바꿀 수 있었지만, 변수를 상수로 선언한다면 처음 선언할 때를 제외한 모든 경우에서 값을 바꿔줄 수 없다.
개발을 하다보면 절대로 값이 변하지 않을 변수가 존재하는데, 이런 변수를 실수로 값을 변경할 수도 있으므로 값을 바꿀 수 없는 상수로 선언하면 좋다.
-리터럴
리터럴은 문자 그대로라는 뜻으로, c언어에서는 값 그 자체를 뜻한다. 이 리터럴을 저장하는 변수가 바로 상수다.
const int a = 10;
위는 상수를 선언하는 코드다. 여기서 변수명 a는 상수고 뒤의 값 10은 리터럴이라 보면 된다.
이런식으로 출력문을 작성했을 때, 값 100 부분이 바로 리터럴에 해당되는 부분이다. 그냥 변수 사용하는게 아닌 값 그대로 입력하는 것이 리터럴인 것 같다.
리터럴도 표기 방법이 있다. 대부분 앞서 배운거지만, 복습 차원에서 한번 더 정리하겠다.
숫자는 숫자 그대로 사용하면 되지만 문자는 앞서 사용했듯이 작음 따음표로 묶어줘야하고, 문자열의 경우는 큰 따음표로 묶어줘야한다.
참고로 문자열을 %c 서식 지정자가 아닌 string의 약자 %s를 사용해야한다.
8진수, 16진수 리터럴 표기는 앞서 조금 언급했지만 8진수는 값 앞에 0을, 16진수는 값 앞에 0x를 붙여주면 된다.
16진수는 서식 지정자 %x로 출력하면 되고, 8진수는 %o를 사용해서 출력하면 된다.
실수 리터럴은 앞서 실수 파트에서 배웠던 것과 똑같다. float 크기는 f나 F를 붙이고, long double 크기는 l 또는 L, 지수 표기법은 e, E로 하면된다.
또한 리터럴에 L, F 같은 접미사를 붙여 값의 크기를 명확하게 표현할 수 있다.
위 사진은 리터럴 접미사인데 별겨 없이 그냥 서식 지정자와 비슷하다. 각각에 해당하는 자료형은 사진에 정리되어 있으므로 따로 정리 없이 실습만 하도록 하겠다.
-const
const는 상수를 선언할 때 사용한다. 선언할 변수의 자료형 앞이나 뒤에 const를 붙여준다면 해당 변수는 상수형이 되어 값의 변경이 불가능하다.
const <자료형> <변수명> = <값> 또는
<자료형> const <변수명> = <값> (1번 방법이 국룰 같다.)
또한 상수는 값 초기화가 필수다. 상수 변수의 값을 선언할 때 넣어주지 않는다면 컴파일 에러가 뜬다.
상수는 const로만 선언하고 값의 사용은 일반 변수와 같다. 값만 변경 못하는 일반 변수라고 생각하면 된다. 위처럼 각 자료형에 맞는 서식 지정자로 상수의 값을 출력 시킬 수 있다. 9행에서 일부러 상수의 값을 변경하려고 시도했는데, 사진처럼 오류가 보여진다. 값의 변경이 안되는 것을 볼 수 있다.
d : 문자열은 " " 이렇게 큰 따음표로 감싸줘야한다.
상수는 변수 선언 시 자료형 앞이나 뒤에 const를 써주면 된다.
a : 일반 변수다. X
b : const는 자료형 앞이나 뒤에 적어줘야한다. X
c : O
d : 일반 변수다. X
e : 상수는 선언시 무조건 값을 초기화 해줘야한다.
답 : c
e : 10ULL은 unsigned long long이다. X
c : float 형은 접미사로 f를 붙여줘야한다. X
리터럴을 사용해서 Hello, world! 8 4.900000e-302가 출력되도록 입력하면 될 것 같다.
#include <stdio.h>
int main()
{
printf("H%cllo, %s %d %Le\n", 'e', "world!", 8, 4.9000000e-302L);
return 0;
}
4.900000e-302는 그대로 써주고 뒤에 L만 붙여줬는데, 이미 long double형으로 서식 지정자를 사용했기 때문에 굳이 0을 안쓰고 4.9e-302만 써줘도 출력할 때 자동으로 0을 붙여서 출력해준다.
상수형으로 con1, con2, con3을 만들고 각각 값을 10, 3.2, 't'를 넣어주는 것이 목적이다. 각 들어갈 값에 따른 자료형을 잘 정해주면 될 것 같다.
#include <stdio.h>
int main()
{
const unsigned long long con1 = 10;
const float con2 = 3.2f;
const char con3 = 't';
printf("%llu %f %c\n", con1, con2, con3);
return 0;
}
자료형은 주어진 printf문의 서식 지정자를 보고 정해줬다. con1은 %llu로 출력하므로 unsigned long long형으로 자료형을 작성했고, con2는 %f이므로 float형으로 만들었다. con3는 %c이므로 char형으로 문자 하나를 작은 따음표로 감싸서 넣었다.
Hello, world! 76 -2147483647 4.528172가 출력되도록 하는 것이 목적이다. 주어진 printf문에 리터럴 값만 추가하면 된다.
#include <stdio.h>
int main()
{
printf("%s %o %lld %Lf\n", "Hello, world!", 076, -2147483647LL, 4.528172L);
return 0;
}
주어진 서식 지정자에 맞게 접미 문자를 붙여줬다. 76은 %o로 출력하므로 8진수를 의미하는 0을 값 앞에 붙여서 076을 써줬고, -2147...는 long long 형으로 출력하므로 뒤에 LL을 붙여줬다. 4.528...은 %Lf 즉 long double 형이므로 뒤에 L을 붙여서 작성했다.
상수형으로 con1 변수를 작성하고 해당 값이 -2.225074e-308이 되도록 할당하면 된다.
#include <stdio.h>
int main()
{
const long double con1 = -2.225074e-308L;
const int con2 = 0x1285;
const long long con3 = 9223372036854775807LL;
printf("%Le 0x%x %lld\n", con1, con2, con3);
return 0;
}
%Le로 con1을 출력하므로 long double형으로 con1을 선언했고 값 뒤에 L을 붙여줬다.
//Unit 11.1은 영상 링크가 잘못 걸려있어서 주어진 코딩도장 자료와 구글링을 통해서 정리했습니다.
-scanf-
scanf 함수는 사용자의 입력을 받는 함수다.
파이썬의 input() 함수와 비슷한 기능을 한다. 하지만 파이썬 input() 함수와 다르게 <변수> = input() 이런식이 아닌, scanf(<서식>, <변수의 주소>); 이런식으로 값을 담을 변수를 인수로 넣어줘야한다. 인수로 전달하는 방식이기 때문에 파이썬처럼 input() 딱 입력만 써줄 수는 없고, 무조건 담을 변수와 같이 써야한다. 또한 서식으로 받을 값의 자료형에 따라 %d, %f 이런식으로 적어줘야하고, 그냥 변수가 아닌 변수의 주소를 인수
로 줘야하므로 &<변수명> 이런식으로 사용해야한다.
scanf(<서식>, &<변수명>);
-정수 입력
정수를 입력받을 때는 정수형 변수 하나를 미리 선언해두고 scanf 서식에 %d를 입력, 그리고 변수의 주소 부분에 &<변수명>을 써주면 된다.
5행에서 입력 값을 저장할 int형 a 변수를 선언하고 6행에서 %d형으로 받은 값을 a의 주소에 저장해준다. 그리고 7행에서 a의 값을 출력한다.
결과를 보면 100을 입력하니 100이 출력되는 것을 볼 수 있다.
참고로 scanf 함수는 찾아보니 버퍼 오버플로우의 위험이 있어서 최신 비주얼 스튜디오에서는 사용을 못한다.(사용하면 컴파일 에러가 뜬다.) 그래서 맨 윗줄에 #define _CRT_SECURE_NO_WARNINGS를 써주거나, 취약점이 패치된 함수인 scanf_s를 써주면 된다. 코딩 도장의 참고란을 못보고 위에서는 scanf_s를 사용했는데, 아래 실습부터는 define을 써줘서 scanf를 사용하도록 하겠다.
scanf로 여러 개의 값을 동시에 입력 받을 수 있다. 파이썬 같은 경우 여러 값을 한 값으로 받아들인 후 split을 통해 값을 구분해서 저장했는데, c언어에서는 그냥 서식 지정자를 여러 개 써주고, 변수의 주소도 여러 개 써주면 된다.
scanf("<서식>",&<변수1>,&<변수2>...);
서식 지정자를 2개 써주고, 변수의 주소도 2개 써주니 두 개의 값을 한 번에 입력 받을 수 있다. 중요한 것은 scanf에서 입력 받을 변수 앞에는 꼭 &를 써줘야한다. scanf가 printf와 너무 비슷해서 &을 까먹고 안넣어주는 경우도 있는데, 이럴 경우 컴파일 에러가 뜨니 주의해야한다.
위 사진처럼 서식 지정자를 원하는 식으로 바꾸면 입력도 그에 맞게 된다. %d,%d를 써줘서 입력 값을 공백이 아닌 콤마로 구분져서 입력 받게 작성하니, 입력시 ,로 구분해서 여러 값을 입력할 수 있다.
-실수 입력
실수 입력도 정수형과 똑같은 방식이다. 그냥 서식 지정자를 실수에 맞게 %f나 %L로 써주면 끝이다.
다만 printf와는 다르게 double형은 %f로 입력 받는 것이 아닌 %lf로 해줘야한다.
%d로 써준 부분만 %f로 바꾸니 실수가 입력된다. 마찬가지로 %f를 여러 번 써주면 여러 개의 실수 값도 입력이 가능하다.
-문자 입력
문자 입력도 마찬가지다. 서식만 %c로 바꿔주면 끝이다.
%c를 사용하면 사용자가 입력한 문자를 char형 변수에 넣어줄 수 있다.
문자 입력은 scanf가 아닌 getchar 함수로도 입력을 받을 수 있다.
위처럼 getchar를 써준다면 scanf보다 좀더 간결하게 작성할 수 있다. 마치 파이썬의 <변수> = input()처럼 사용하는 것이다.(단 문자열은 입력 안된다.)
getchar에 대응하는 함수로 putchar라는 함수도 있다. 이 함수는 문자를 출력해주는 역할을 한다.
이 putchar를 써준다면 서식 지정자를 써줄 필요없이 변수 하나만 적어서 문자를 출력할 수 있다.
scanf 함수로 정수를 입력 받는 방법은 scanf("%d", &<변수명>);로 작성하면 된다.
답 : b
실수 입력은 %d로 써준 부분만 %f로 바꿔주면 된다. 또한 당연하지만 입력 받는 변수가 float 같은 실수형이어야 한다.
답 : e
문자는 %d나 %f로 써준 서식 부분을 %c로 바꿔주거나 getchar 함수를 사용하면 된다. 당연하게도 입력 받는 변수는 char형이어야 한다.
답 : c
int형 정수를 세 개 입력받는 scanf 코드를 작성하는 것이 목적이다.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int num1;
int num2;
int num3;
printf("정수 세 개를 입력하세요: ");
scanf("%d %d %d", &num1, &num2, &num3);
printf("%d\n", num1);
printf("%d\n", num2);
printf("%d\n", num3);
return 0;
}
서식 지정자 %d를 3개 써주고, 입력 받는 변수의 주소도 &을 붙여서 3개를 써주면 정수 입력을 3번 받을 수 있다.
정수, 실수, 문자가 공백을 기준으로 구분되서 입력된다. 이 입력된 값들을 각 줄에 출력하는 것이 목적이다. 1, 2행에서 define과 stdio.h를 include해 scanf, printf를 사용할 수 있게끔 해주고 %d, %f, %c로 정수, 실수, 문자를 scanf로 입력 받아 printf 함수로 출력하면 될 것 같다.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int a;
float b;
char c;
scanf("%d %f %c", &a, &b, &c);
printf("%d\n%f\n%c", a, b, c);
}
1,2줄은 printf, scanf를 사용하기 위해 써줬고, 먼저 입력 받을 변수인 int, float, char형 변수 a,b,c를 각각 선언해줬다. 그리고 scanf의 서식에 "%d %f %c"를 입력해 입력 값을 정수형, 실수형, 문자형으로 받도록 했다. 그리고 입력 받은 변수를 printf를 통해 개행문자 \n를 써줘서 각줄에 출력하도록 작성했다.
'Old (2021.01 ~ 2021.12) > Programming' 카테고리의 다른 글
c언어 코딩도장 Unit 24 ~ 33 (0) | 2021.02.21 |
---|---|
c언어 코딩도장 Unit 12 ~ 23 (4) | 2021.02.17 |
파이썬 코딩도장 Unit 38, 39, 43 (0) | 2021.02.14 |
생활코딩 HTML 의미론적 태그 ~ 검색엔진 최적화 (0) | 2021.02.11 |
생활코딩 HTML 버튼 ~ meta (0) | 2021.02.09 |