Part 03 상수, 변수, 자료형
이 장의 목표 상수와 변수의 특성에 대해서 이해한다. 변수는 기억 상자다! 식별자에 대해서 알아본다. C 언어의 기본 자료형을 이해한다.
자료형이란 무엇인가? 자료형(data type) C 언어의 자료형 프로그램에서 표현 혹은 저장하는 데이터의 종류 혹은 유형 크기에 따라 문자형 char 정수형 short int long 부동소수형 float double long double 표 3.1 C 언어의 자료형 분류
변수 변수(variable) 사용 전 변수 선언 자료형 변수이름; 데이터를 저장하는 데 사용되는 기억 장소의 이름이다. 변수 이름과 저장할 데이터 값의 유형(자료형)을 지정해야 한다. 자료형 변수이름; char c; short sum;
메모리와 변수 변수를 위한 메모리 할당 문자형 변수 c를 위해서는 1 바이트를 할당 short 변수 sum에 대해서는 2 바이트를 할당 1278 1279 1280 1281 1282 1283 1284 1285 1286 sum 10011010 c 그림 3.1 메모리와 변수
상수 상수(constant) 프로그램이 실행되는 동안 값이 변하지 않는 데이터 정수형 상수, 문자형 상수, 부동소수형 상수로 구분할 수 있다. 예 'A', 'x'는 문자형 상수 -10, 0, 55는 정수형 상수 3.14, 300.25는 부동소수형 상수
식별자 식별자 x25 X age01 sum _jumsu address_01 25x X# 01age sum* -jumsu 변수, 상수, 함수 등에 대한 이름 문자와 숫자로 구성되며 첫 번째 글자는 반드시 문자 밑줄문자’_’도 하나의 문자로 사용될 수 있음 대소문자를 구별 바른 이름 잘못된 이름 x25 X age01 sum _jumsu address_01 25x X# 01age sum* -jumsu address-01 표 3.2 바른 식별자, 잘못된 식별자
변수의 초기화 변수를 선언하면서 그 초기값을 정할 수도 있다 변수 초기화는 반드시 해야 하는 것은 아니고 선택사항 int sum = 0; sum
변수의 초기화 실행결과: 합이 0 입니다. 합이 10 입니다.
대입문을 이용한 변수 값 변경 대입문(assignment statement) sum = 10; 변수에 새로운 값을 저장함으로써 기존의 변수 값을 변경할 수 있다. 대입 연산자(=)의 오른쪽 식이 계산되고, 그 결과 값이 왼쪽 변수에 저장된다. sum = 10; 10 sum
score.c 실행결과: 첫 번째 점수는 7 두 번째 점수는 10 세 번째 점수는 8 네 번째 점수는 9
변수 값 변경 과정 초기화 후 첫 번째 대입 후 두 번째 대입 후 7 10 8 score score score 그림 3.4 변수 값 변경 과정
변수 값 증가 대입문 오른쪽과 왼쪽에 나타난 변수는 다른 의미 변수에 접근(읽기)은 그 값을 변경시키지 않음. 대입문 오른쪽 변수는 그 변수로부터 읽어온 값을 의미 대입문 왼쪽 변수는 그 변수에 저장(쓰기) 의미 변수에 접근(읽기)은 그 값을 변경시키지 않음. 변수에 대입(쓰기)은 이전 데이터를 새로운 데이터로 대체 +1 sum = sum + 1; 10 읽기 11 저장 10 11 sum
scoreSum.c 실행결과: 첫 번째 점수: 7 현재 합: 7 두 번째 점수: 10 현재 합: 17 첫 번째 점수: 7 현재 합: 7 두 번째 점수: 10 현재 합: 17 세 번째 점수: 8 현재 합: 25 네 번째 점수: 9 현재 합: 34
const int MAX_STUDENT = 20000; 이름 상수 이름 상수(named constant) 상수에 이름이 부여되어 있는 것. 이름상수에는 식별자가 부여되어 있으므로 이름상수는 변수와 유사하지만 상수이므로 값을 변경할 수 없다. 예약어 const const int MAX_STUDENT = 20000;
표 3.3 C의 기본 정수형의 범위 예(Microsoft Visual C/C++ 6.0) 크게 4개의 정수형 char, short, int, long 자료형 기억장소 최소값 최대값 char 8 비트 -27 = -128 27-1 = 127 short 16 비트 -215 = -32,768 215-1 = 32,767 int 32 비트 -231 = -2,147,483,648 231-1 = 2,147,483,647 long long long* 64 비트 -264 = -9,223,372,036,854,775,808 263-1 = 9,223,372,036,854,775,807 * long long은 MS Visual C/C++ 6.0에서는 지원되지 않음. 여기서 long long은 gcc 컴파일러의 예 표 3.3 C의 기본 정수형의 범위 예(Microsoft Visual C/C++ 6.0)
정수 표현 8 비트를 이용한 양수와 음수 표현 예 음수 표현 첫 번째 비트는 부호 비트로서, 0은 양수 1은 음수를 나타낸다. 나머지 7개의 비트들은 수의 크기를 나타낸다. 음수 표현 연산의 효율성을 위해 수의 크기를 2의 보수 형태로 나타낸다. 수의 표현 이진수 +5 00000101 5의 1의 보수 11111010 5의 2의 보수 11111011 -5 표 3.4 +5, -5의 표현
양수, 음수 표현 양수 이진수 음수 +127 01111111 -128 10000000 +126 01111110 -127 10000001 ... +5 00000101 -6 11111010 +4 00000100 -5 11111011 +3 00000011 -4 11111100 +2 00000010 -3 11111101 +1 00000001 -2 11111110 00000000 -1 11111111 표 3.5 양수, 음수 표현
변수에 기억공간 할당 정수형 종류에 따라 해당 변수에 다른 크기 기억공간 할당 자료형의 크기를 나타내는 sizeof() 연산자 실행결과: char 크기: 1 short 크기: 2 int 크기: 4 long 크기: 4
korea.c 실행결과: 대한민국에 대한 데이터입니다 대학수: 276 인구: 48295000 예산: -590360576 원
정수형 오버플로우 정수형 오버플로우 예 32비트 int 질문 long budget = 237000000000000L; 정수형에서 저장할 수 있는 수보다 더 큰 수나 더 작은 수를 저장 예 32비트 int 가장 큰 수(231-1 = 2,147,483,647)에 1을 더하면 어떻게 될까? 01111111 11111111 11111111 11111111 + 00000000 00000000 00000000 00000001 -------------------------------------------------- 10000000 00000000 00000000 00000000 질문 long budget = 237000000000000L;
unsigned 정수형 0과 양의 정수만을 나타낼 수 있는 unsigned 정수형 음수를 표현할 수 없는 대신에 나타낼 수 있는 양의 정수가 두 배 자료형 기억장소 최소값 최대값 unsigned char 8 비트 28-1 = 255 unsigned short 16 비트 216-1 = 65,535 unsigned int 32 비트 232-1 = 4,294,967,295 unsigned long 64 비트 264-1 = 18,446,744,073,709,551,615 표 3.6 C의 unsigned 정수형의 표현범위
표 3.7 부동소수형의 예(Microsoft Visual C/C++ 6.0) float, double, long double 부동소수점(floating-point)이란? 부동소수점에서 점(point)은 소수점을 말하며, 이 소수점이 수의 어느 위치에도 올 수 있으므로 부동소수점이라고 한다. 자료형 기억장소 최소값 최대값 float 4 바이트 7개의 유효숫자를 가지며, -1.0E+38의 근사값 7개의 유효숫자를 가지며, 1.0E+38의 근사값 double 8 바이트 15개의 유효숫자를 가지며, -1.0E+308의 근사값 15개의 유효숫자를 가지며, 1.0E+308의 근사값 long double 표 3.7 부동소수형의 예(Microsoft Visual C/C++ 6.0)
부동소수형 표현 float 형 자료의 저장과 표현 범위 double 형 자료의 저장과 표현 범위 부호 지수부 가수부 8바이트(32비트)
float.c 실행결과: 0.1234567910432815600 0.1234567890123456800 float 크기: 4 double 크기: 8 long double 크기: 8
문자형 각 문자에 고유번호를 부여한 코드를 사용하여 표현한다. C에서는 ASCII 코드를 사용한다. ASCII(American Standard Code for Information Interchange) 128개의 문자를 7 비트를 사용하여 표현(8비트로 확장됨) 대문자(A, B, C 등등) 소문자(a, b, c 등등) 구두점(punctuation)(마침표, 세미콜론, 쉼표 등등) 숫자(digit)(0에서 9까지) 공백 문자(‘ ’) 특수 문자(&, |, \ 등) 제어 문자 열복귀(carriage return), 널(null), 문서-끝-표시자(end-of-text) 액센트(accent)가 있는 문자
ASCII 코드표 표 3.1) ASCII 코드 표
code.c 실행결과: a 97 A 65 1 49 $ 36 + 43
문자형 관련 입출력 함수 문자형 관련 입출력 함수 int getchar() int putchar(int c) 매개변수가 없으며 한 문자를 읽어 그 문자의 ASCII 값을 반환한다. 파일 끝에 도달하면 EOF 를 반환한다. <stdio.h> 파일을 #include 해야 한다. int putchar(int c) 한 글자를 받아 화면에 출력하고 출력한 문자의 ASCII 값을 반환한다. 오류 시에는 EOF 를 반환한다. <stdio.h> 파일을 #include 해야 한다. 다음 예제 프로그램 char.c ① 키보드로부터 한 개 문자 읽기 ② 문자로 출력 ③ ASCII 값을 보기 위해 정수로 출력
char.c 실행결과: 한 문자를 입력하시오 : A 읽은 문자 : A 읽은 문자의 ASCII 값 : 65
putchar.c 실행결과: Hello!
이스케이프 시퀀스 이스케이프 시퀀스(escape sequence) 백슬래쉬 문자(\)로 시작하고, 다음 문자는 특별한 방식으로 해석 예를 들어 이중 인용부호 문자(") 출력: \" 이스케이프 시퀀스 의미 \b 백스페이스 \t 탭 \n 개행 \r 열복귀 \" 이중 인용부호 \' 단일 인용부호 \\ 백슬래쉬
escape.c
▶ Key Point C 언어는 크게 1개의 문자형, 3개의 정수형, 3개의 부동소수형을 제공한다. 변수는 데이터를 저장하는 데 사용되는 저장 장소의 이름이다. 변수에 대한 접근(읽기)은 메모리에서 그 값이 변경되지 않으나, 변수에 대한 대입(쓰기)은 이전의 데이터가 새로운 데이터로 대체된다. 상수 값은 변경할 수 없다. 음수는 연산의 효율성을 위해 수의 크기를 2의 보수 형태로 나타낸다. C 언어는 8-비트 ASCII 문자 집합을 사용하여 문자를 표현한다.
▶ 프로그래밍 실습 1. ASCII 코드표의 일부를 프린트하는 프로그램을 작성한다. 다음 선언을 가정하여 프로그램을 작성한다. int main( ) { char c; } (1) 각 알파벳 대문자와 그 코드 값을 프린트한다. 변수 c에 문자 'A'를 대입한 후에 1씩 증가시키면서 26개를 차례로 프린트한다. (2) 각 알파벳 소문자와 그 코드 값을 프린트한다. 변수 c에 문자 'a'를 대입한 후에 1씩 증가시키면서 26개를 차례로 프린트한다. (3) 숫자와 그 코드 값을 프린트한다. 변수 c에 문자 '0'을 대입한 후에 1씩 증가시키면서 10개를 차례로 프린트한다.
▶ 프로그래밍 실습 2. 5의 배수를 순서대로 계산하여 프린트하고 그 합을 계산하여 프린트한다. (1) int 변수 i를 선언하고 0으로 초기화한다. 이 변수를 5씩 증가하면서 50까지의 5의 배수를 프린트하는 프로그램을 작성하라. int main( ) { int i; } (2) 실습 1의 프로그램을 확장하여 0부터 50 사이의 5의 배수들의 합을 계산하여 프린트하는 프로그램을 작성하라.
Part 04 입출력과 전처리 이 슬라이드는 부산대학교 우균이 작성하였습니다. 오류나 수정할 사항 있으면 woogyun@pusan.ac.kr로 연락 주세요.
전처리기 지시자 전처리기(preprocessor) 전처리기 지시자(preprocessor directive) 컴파일러가 프로그램을 번역하기 '전'에 소스 프로그램을 '처리'하는 프로그램 전처리기 지시자(preprocessor directive) 전처리기에게 특정 작업을 지시하는 가짜 명령어(의사명령어) #으로 시작함 중요한 전처리기 지시자 #include: 다른 파일의 내용을 현재 파일에 포함시킴 #define: 특정 단어를 다른 문자열로 바꿈
#include 파일 포함 지시자 헤더파일(header file) 다른 파일에 포함시킬 목적으로 작성된 파일 인클루드 파일(include file)이라고도 함 헤더파일 a.h 소스파일 Source.c 전처리된 전처리기
헤더파일 종류 <표준 헤더파일> "일반 헤더파일" 시스템의 특별한 위치에 존재 소스파일과 같은 위치에서 찾을 수 있는 파일
변수 값 출력 프로그램 표준 헤더파일 이 부분을 별도의 헤더파일로 만들자 실행결과: X = 101
직접 작성한 헤더파일 #include 예 일반 헤더파일이므로 defineX.h는 include.c와 같은 디렉터리에 있어야 함 실행결과: X = 101
#define 다른 문자열로 대치될 단어(매크로; macro)를 정의함 전처리기가 매크로 MESSAGE를 "Have a nice day !"로 바꾸어 준다. 실행결과: Have a nice day !
매크로 함수 함수 형태의 매크로 실제로 함수는 아니지만 인수를 받는 형태임 문자열 리터럴을 나란히 두면 접합된다. DUP("Hello? ")에서 MESSAGE는 "Hello? "이므로 DUP("Hello? ")는 다음과 같이 바뀐다. "Hello? " "Hello? " 실행결과: Hello? Hello?
매크로 정의가 길 때 백슬래시(backslash)는 매크로 정의가 다음 줄에 계속됨을 의미함 실행결과: La La La La La La La La La La
표준입출력 표준입출력(standard input/output) 에코출력(echo output) 거의 모든 컴퓨터가 기본적으로 수행하는 입출력 입출력 모두 문자열로 구성되어 있다고 간주함 표준입력은 통상 키보드, 표준출력은 통상 모니터를 나타냄 에코출력(echo output) 키보드로 입력된 내용을 모니터에 그대로 보여주는 것 에코출력은 운영체제 서비스일 뿐이며, 프로그램이 출력한 것은 아님 에코출력과 표준출력을 구별해야 함
표준 라이브러리 라이브러리(library) 표준 라이브러리(standard library) 미리 작성해 둔 프로그램 표준 라이브러리(standard library) 컴퓨터 시스템이 달라도 같은 기능(same functionality)을 같은 방식(same interface)으로 사용할 수 있도록 작성해 둔 라이브러리 표준입출력 라이브러리(standard input/output library) 표준입출력을 다루는 프로그램(함수) 집합
printf: 형식에 맞는 출력 %d 2 정수 i "2" 문자열 %f 3.14 실수 f "3.140000" %c '5' "5" printf 포맷 스트링은 데이터 변환을 지시함 실행결과: i = 2 f = 3.140000 c = 5
printf 포맷 스트링
printf 출력 폭 지정 포맷 스트링에서 % 다음에 양의 정수를 기입하여 출력 폭 지정 실행결과: i = 2 c = 5 10칸
printf 정밀도 지정 포맷 스트링에서 %와 f 사이에 (출력폭이 지정된 경우, 출력폭 다음에) ".양수" 형태로 정밀도 지정 실행결과: pi = 3.141593 pi = 3.14 pi = 3.141592653590 10칸
scanf: 형식에 맞는 입력 %d 27 정수 n "27" 문자열 scanf 포맷 스트링은 문자열을 데이터로 변환함 주소연산자 &를 사용한 점에 주의! 실행결과: 27 entered n = 27 double of n = 54 triple of n = 81
scanf에서 주의할 점 주소 연산자(address-of operator) & 프롬프트(prompt) scanf("%d", &n); 표준입력에서 변환한 값을 저장할 장소를 알아야 하기 때문에 변수 n의 주소(&n)을 두 번째 인수로 사용함 주소 연산자를 누락시키면 실행시간 오류 프롬프트(prompt) 입력할 내용을 설명해 주는 안내문 프롬프트가 없으면 "프로그램이 죽은" 것으로 오해할 수 있음 앞 슬라이드의 프로그램은 프롬프트가 없다!
scanf: 프롬프트를 사용한 버전 바로 이것이 프롬프트! 입력할 내용을 설명해 준다. 실행결과: 정수 n을 입력해 주세요. n = 27 입력한 n = 27 n의 두 배 = 54 n의 세 배 = 81
scanf 포맷 스트링 TIP: double 타입으로 입력 받아야 할 경우에는 %f 대신 %lf를 사용한다.
getchar, putchar: 문자 입출력 표준입력 putchar 'A' 문자 .....A 표준출력 대문자로 변환하는 함수 toupper() 실행결과: 소문자를 입력하세요. a a의 대문자는 A입니다. 안내문구(prompt) putchar가 출력한 문자들
gets, puts: 줄 단위 입출력 C99에서는 const int로 배열 크기 선언이 가능함 실행결과: 사용자가 입력한 \n puts가 출력한 \n line에는 여기까지만 저장됨(\n은 제외됨)
gets, puts 주의점 개행문자 처리 이전 예에서 gets 처리 후 line[256] 상황 gets는 \n을 떼고 \0을 붙여 준다. puts는 \n을 문자열 출력 끝에 자동으로 붙여 준다. 주의: fgets, fputs는 다르게 동작한다. 이전 예에서 gets 처리 후 line[256] 상황 버퍼 오버런(buffer overrun) 버퍼(임시저장소)의 범위를 넘어섬 gets와 puts는 버퍼 오버런의 위험이 있음 fgets, fputs를 사용하는 것이 권장됨.
입력 버퍼 실행결과: 정수 n과 문자 c를 입력해 주세요. 250 a n = 250 c = a는 어디로?
scanf가 공백을 건너뛰는 습성 포맷 스트링 %c의 특이함 %s로도 공백을 건너뛰어 문자를 읽을 수 있다 포맷 스트링에 공백이 하나라도 있으면 여러 공백문자에 대응함 scanf("%d%c", …) ≠ scanf("%d %c", …) 포맷 스트링 %c를 제외하고는 모두 공백을 건너뜀 scanf("%d%d", …) ≡ scanf("%d %d", …) %s로도 공백을 건너뛰어 문자를 읽을 수 있다 %s로 입력을 받되 입력 폭을 1로 지정 char c; scanf("%1s", &c); // scanf(" %c", &c);와 같음 %s로 공백을 건너뛰지 않으려면? 불가능함. gets를 사용하세요!
입출력 방향 재지정 입력 재지정(input redirection) 출력 재지정(output redirection) 합쳐서… 표준입력 대신 파일로부터 입력 받음 program.exe < inputFile.txt 출력 재지정(output redirection) 표준출력 대신 파일로 내용을 저장 program.exe > outputFile.txt 합쳐서… program.exe < inputFile.txt > outputFile.txt program.exe > outputFile.txt < inputFile.txt <를 로, >를 로 생각하면 기억하기 쉽다.
전처리기 지시자 복습 매크로 정의가 길 경우에는 행 끝에 \를 넣어 다음 줄에 계속 정의한다.
매크로 함수의 한계? 실행결과: x = 250 x = 747 이 x를 y로 바꾸고 싶지 않습니까?
매크로 인수 연산 #x 매크로 인수 x를 문자열로 바꿈 실행결과: x = 250 y = 747 # 하나만 넣었을 뿐인데… PR(y) printf(#y " = %d\n", y) printf("y" " = %d\n", y) printf("y = %d\n", y)
매크로 인수 연산 ## 매크로 인수 연산자 ## ## 사용 예 매크로 인수 이름을 붙여 줌 문자열을 붙이는 것이 아니라 인수 이름 자체를 붙임 ## 사용 예 #define MERGE(x, y) x ## y ... MERGE(x, 1) = 25; // x1 = 25; 로 확장됨 MERGE(blue, berry) *= MERGE(x, 1); // blueberry *= x1; 으로 확장됨
Key Point 1 전처리기 지시자 표준입출력 표준입출력 라이브러리 #include: 다른 파일을 포함시킴 #define: 매크로(다른 문자열로 바뀔 문구)를 정의함 표준입출력 키보드(std. in)와 모니터(std. out)를 추상화함 문자 단위 입출력을 수행함 표준입출력 라이브러리 형식에 따른 입출력: printf(), scanf() 문자 단위 입출력: getchar(), putchar() 줄 단위 입출력: gets(), puts()
Key Point 2 버퍼 입출력 방향 재지정 매크로 인수 활용 입출력 속도 차이를 메워주기 위한 임시 저장소 버퍼오버런: 저장 범위를 넘어 다른 영역에 침범하는 현상 버퍼오버런을 방지하기 위해 gets, puts보다 fgets, fputs를 사용하자. 입출력 방향 재지정 표준입출력 대신 파일을 사용하도록 지정함 매크로 인수 활용 #x: 매크로 인수 x에 해당하는 문자열 x ## y: 매크로 인수 이름 x와 y를 접합함
주의사항 Key Point 사용자로부터 입력 받을 때에는 안내문구(prompt)를 사용하는 것이 좋음 scanf의 인수로는 변수의 주소를 주어야 함 int x; scanf("%d", x); // 잘못됨 scanf("%d", &x); // 올바름 scanf로 double형 데이터를 읽으려면 "%lf"를 이용함 scanf 포맷 스트링 "%c"는 공백을 건너뛰지 않음 " %c"를 이용하거나 "%1s"를 이용함
▶ 프로그래밍 실습 1 세 정수 a, b, c를 입력 받은 후 a와 b의 합에서 c의 거듭제곱을 빼는 프로그램을 작성하라. 거듭제곱은 c * c로 계산하면 된다. 단 다음과 같은 내용의 헤더파일 myLang.h를 만들고 이를 다음과 같이 #include하여 사용하라. 여러분의 프로그램에는 int, main, scanf, printf, return 등을 사용해서는 안 된다. 대신 myLang.h에 정의된 #define 상수를 이용하여 작성해야 한다.
▶ 프로그래밍 실습 2 텍스트 내용을 간단히 암호화 하는 방법 중 ‘k-증가’라는 알고리즘이 있다. 이는 각 문자를 k문자 뒤의 문자로 변경하는 알고리즘이다. 이 경우 문자들의 배열은 원형 형태로 배열되어 있다고 가정한다. 즉 문자 z 다음에는 a가 나타나고 문자 Z 다음에는 A가 나타난다고 가정한다. 정수 k와 문자 하나를 입력받은 후, k-증가 알고리즘대로 해당 문자를 암호화 하는 프로그램을 작성하라. 예: 2 Z B 6 i o 9 p y