기억부류
storage class specifier
C 언어에서는 기억 부류 지정자(storage class specifier)를 사용하여 변수와 함 수의 생명주기(life time)와 가시성의 범위(scope)를 지정
auto, static, extern, register 등 4개의 키워드를 사용
C11 표준에서 새롭게 도입된 _Thread_local
- _Thread_local은 스레드별 지역 변수(thread-local variable)를 정의하기 위한 키워드
- _Thread_local로 선언된 변수는 다른 스레드와 공유되지 않으며, 각 스레드에서 고유한 값을 가짐
지역(local) 변수와 전역(global) 변수
변수의 유효범위(scope) : 프로그램 내에서 변수가 사용될 수 있는 범위
- C 언어에서는 지역(local)과 전역(global) 이라는 개념으로 변수가 사용될 수 있는 범위를 지정
지역 변수 : 해당 함수나 블록({ }) 내에서만 사용
전역 변수 : 소스 전체에서 사용
선언 위치에 따라서 결정
- 함수나 블록 안에서 변수를 선언하면 그 변수는 지역 변수
- 함수 (보통 main()함수)밖에서 선언하면 전역 변수
#include <stdio.h>
void show(void);
int x; //전역 변수
//프로그램 어디서나 사용 가능
//정적데이터영역에 저장
int main(void)
{
int y;//지역 변수
//스택에 저장
}
void show(void)
{
int z;//지역 변수
//해당 블록 내에서만 사용 가능
}
코드 영역과 데이터 영역
소스와 프로그램에서 사용할 데이터로 구성
소스는 컴파일과 링킹 과정을 거쳐 기계어로 번역
기계어로 번역된 코드는 메모리의 코드(code)영역에 저장
- 코드 영역 : 저장된 내용을 읽기만 가능하고 쓰기가 불가능한 메모리 영역
프로그램에서 사용할 변수 등의 데이터는 읽기 쓰기가 가능한 데이터 영역에 저장
- 데이터 영역은 스택(stack) 영역, 힙(heap) 영역, 정적 데이 터 영역으로 나누어져 있음
- 스택과 정적 데이터 영역은 크기가 정해져 있지만 힙 영역은 malloc() 같은 함수를 통해 원하는 크기로 할당 가능
- Visual Studio에서는 디폴트 값으로 스택의 최대크기 1MB
- auto 지역 변수는 스택 영역을 사용하고 전역 변수는 정적 데 이터 영역을 사용
컴퓨터의 프로그램 메모리 레이아웃

지역 변수의 유효범위

자료형과 기억 부류 지정자
변수 선언시에 이용하는 예약어와 선언된 위치에 의해서 결정
기억 클래스는 변수의 값이 어떤 종류의 메모리에 저장 되는지를 지정
| 자료형 | 기억 부류 지정자 |
| 자료의 크기 결정 | 자료의 기억 위치(메모리의 종류) 스택, data영역, CPU의 레지스터 등 |
| int, char, float, double | auto, static, extern, register, _Thread_local |
auto int x;
static double y;
기억클래스 자료형 변수명;
기억 클래스
기억 클래스 : 자동(auto)
가장 많이 사용하는 기억 클래스
int형 변수 a를 auto변수로 선언
- auto int a; //auto는 생략이 가능하여 이 선언문은 int a;와 같은 문장임
- 지금까지는 그냥 "int a;"형태로 자료형만 명시했는데 자료형인 int앞에 기억 클래스를 나타내는 auto를 더 쓰면 됨
auto변수의 특징
- 함수 또는 블록의 내부에서 선언
- 해당 함수나 블록 내에서만 유효한 지역(local)변수
- 기억 클래스가 명시되지 않고 선언된 변수는 모두 자동 변수
즉, auto는 생략이 가능
그래서 지금까지 기억 클래스를 쓰지 않고 사용한 모든 변수는 auto변수 - 스택(stack) 공간을 일시적으로 사용
- 함수나 블록을 진입하면 기억 영역이 확보되고, 벗어나면 기억 영역은 바로 소거됨
단, return문으로 리턴된 값은 스택에 복사되어 외부로 전달 - 초기화는 실행시 이루어지며, 초기화하지 않으면 임의의 값(쓰레기 값, garbage value)을 가짐
기억 클래스 : 레지스터(register) 변수
지금을 잘 쓰지 않음
레지스터 : CPU가 연산시 데이터를 임시로 저장하는데 사용하는 작고 빠른 기억 장소
int형 변수 a를 register변수로 선언
- register int a;
register변수는 auto변수와 동일한 특징
- 다만 스택이 아니라 CPU 내의 레지스터(register)에저장하여 고속 처리가 가능
정수형, 포인터 등 4바이트 데이터만 저장이 가능 (8바이트도 사용)
두 개까지만 선언 가능, 초과된 변수는 auto로 지정
레지스터에는 주소가 없으므로 주소 참조가 불가능
매우 단순하면서 엄청난 횟수를 반복할 필요가 있는 루프의 변수를 register변수로 지정하면 고속 처리 가능
register 변수는 컴파일러에 해당 변수를 레지스터 메모리에 할당하겠다고 요청하는 것이지, 절대적으로 해당 변수가 레지스터 메모리에 할당됨을 보장하지는 않음
컴파일러가 최적화 과정에서 적절한 변수를 레지스터 변수로 만들기 때문에 일부러 지정할 필요는 없음
기억 클래스 : 정적(static) 변수
int형 변수 a를 정적(static)변수로 선언
- static int a;
static변수의 특징
- 프로그램이 종료될 때까지 값을 유지
- 처음 실행시 한번만 초기화되고 초기화 값이 없으면 0으로 초기화됨
- 스택이 아닌 정적 데이터 영역을 사용
- 지역 static변수는 해당 블록 내에서만 접근 가능
static변수는 윈도우즈 프로그래밍할 때 많이 사용
전역 변수는 기본적으로 static
기억 클래스 : extern 변수
전역 변수는 함수 외부에 선언되는 변수이며 프로그램의 모든 부분에서 사용가능
전역 변수는 다음과 같이 extern을 쓸 수 있음
- extern int x;
- 이렇게 선언된 extern변수는 해당 파일에 실제로 있는 변수가 아니라 프로그램의 다른 파일에 선언된 변수인데 해당 파일에서 사용할 수 있게 함
덩치가 큰 상용 프로그램은 하나의 소스 파일로 관리하지 않고 여러 개의 모듈 소스로 나누어 관리하는데 각 모듈 소스는 각각 컴파일을 한 후에 하나의 실행파일로 합쳐짐
기억 부류 지정자 정리
기억 부류 지정자(auto, register, 지역 static)에 따른 변수들의 선언위치, 유효 범위, 파괴 시기, 초기값, 저장 장소
전역 변수의 특징도 비교를 위해서 함께 표시
| auto | register | (지역)static | 전역 | |
| 선언 위치 | 함수(블록) 내부 | 함수(블록) 내부 | 함수(블록) 내부 | 함수 외부 |
| 유효 범위 | 함수(블록) 내부 | 함수(블록) 내부 | 함수(블록) 내부 | 프로그램 전체 |
| 파괴 시기 | 함수(블록) 종료시 | 함수(블록) 종료시 | 프로그램 종료시 | 프로그램 종료시 |
| 초기값 | 초기화되지 않음 | 초기화되지 않음 | 0으로 초기화 | 0으로 초기화 |
| 저장 장소 | 스택 | CPU의 레지스터 | 데이터 영역 | 데이터 영역 |
배열
변수와 배열
변수
- 단독주택
- int x; //4byte짜리 단독주택 1가구
배열
- 같은 평수로 구성된 아파트
- int x[5]; //4byte짜리 5가구
배열이란 연속적인 항목들이 동일한 크기의 순서를 갖고 나열되어 있는 데이터의 집합
일차원 배열
배열명 다음에 구두점 대괄호([])를 쓰며 대괄호 안에 배열의 크기를 나타내는 수를 하나 씀
자료형 배열명[첨자];
int score[7];
- 배열명 만드는 규칙은 변수명 만드는 규칙과 동일
- 배열 선언문에서 사용하는 첨자는 양의 정수이며 배열의 크기
- score배열은 정수형 자료 7개를 저장할 수 있음
- 7개의 공간을 배열의 원소(element)라 함
일차원 배열 초기화
일차원 배열 초기화 방법
- int score[5]={10,20,30,40,50};
- 변수 초기화와 달리 원소가 많으므로 중괄호({})로 묶어 줌
- score[0]에는 10이, score[1]에는 20 등이 차례대로 초기화됨
문자형 배열 초기화 방법
- char name[ ]={'H','a','n',' ','S',' ','H','\0'};
- 배열 선언과 함께 초기화까지 할 경우 원소의 개수 8은 생략 가능
- 문자 배열의 마지막 원소는 반드시 널(NULL) 문자인 '\0'
- 문자형 배열은 문자열과 관련이 있는데 C 언어에서 문자열은 항상 널 문자로 끝나기 때문
문자 배열은 문자열 형태로 초기화할 수도 있음
- char name[ ]="Han S H";
- 널 문자가 자동으로 마지막 원소에 할당됨
- 원소의 개수는 8개
- 주의:char name[7]="Han S H";//널 문자 저장 안되므로 제대로 된 문자열 아님
배열 초기화 방법
배열 원소가 초기화 데이터 수보다 많으면 나머지 원소들은 0으로 초기화됨
10개의 원소를 갖는 배열을 2개만 초기화하면 나머지 8개는 모두 0으로 자동 초기화

배열 원소의 수가 초기화한 데이터 수보다 적으면 “초기화 데이터가 너무 많다”는 에러가 발생
- int han[2]={10,20,30}
- error C2078: 이니셜라이저가 너무 많습니다.
배열을 선언과 동시에 초기화한 경우와, 선언 먼저하고 값을 대입한 경우인데 각 원소에는 같은 값이 할당됨
- int han[3]={10,20,30}; // 배열 선언과 초기화
- int han[3]; // 배열 선언
han[0]=10; // 대입
han[1]=20; // 대입
han[2]=30; // 대입

주소는 매번 바뀜
다차원 배열
다차원 배열은 2차원 배열, 3차원 배열 등 n차원 배열이 가능
일반적으로 3차원 이상의 배열은 이해하기 어려워서 잘 사용하지 않음
- int x[3]; // 1차원 배열
- int x[4][3]; // 2차원 배열, int x[3]이 4개
- int x[5][4][3]; // 3차원 배열, int x[4][3]이 5개
2차원 배열 x의 초기화는 두 가지 방법
- int x[3][2]={1,2,3,4,5,6};
- int x[3][2]={ {1,2}, {3,4}, {5,6} };

2차원 배열도 물리적으로는 1차원적으로 저장
배열의 전체 크기는 선언시 사용한 첨자를 곱(3x2)하면 되고 마지막 원소는x[3][2]가 아니라 x[2][1]인 것을 명심
문자열과 문자형 배열
문자열(string)은 두 개 이상의 문자 묶음
문자열 상수는 문자열 양쪽을 큰 따옴표(" ")로 감싸야 함
- abc는 "abc"
- 대한민국은 "대한민국"
문자열 상수는 각 문자가 한 바이트 씩 저장되므로 문자열 "ABC"는 ASCII코드 값(65 66 67 0)에 해당하는 2진수로 메모리에 저장됨
| A | B | C | \0 |
| 0100 0001 | 0100 0010 | 0100 0011 | 0000 0000 |
문자열은 문자형 배열에 두 가지 방법으로 초기화할 수 있음
- char eng[4]={'A', 'B', 'C','\0'} // 문자의 모임
- char eng[4]="ABC"; // 문자열
문자열 상수에서는 문자열의 끝을 의미하는 널 문자가 제일 뒤에 자동적으로 붙여짐
'a'는 문자로 1바이트에 저장되고, "a"는 문자열로 뒤에 널 문자가 있으므로 2바이트에 저장됨
배열을 이용한 성적처리 소스
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int i, num[5], kor[5], eng[5], mat[5], c[5], total[5];
double avgkor = 0.0, avgeng = 0.0, avgmat = 0.0, avgc = 0.0;
double avg[5];
printf("┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓\n");
printf("┃ 성적 처리 프로그램입니다. ┃\n");
printf("┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛\n");
for (i = 0; i < 5; i++) {
printf("%d번째 학생의 학번을 입력하고 Enter를 누르세요=", i + 1);
scanf("%d", &num[i]);
printf("국어 점수를 입력하고 Enter를 누르세요=");
scanf("%d", &kor[i]);
printf("영어 점수를 입력하고 Enter를 누르세요=");
scanf("%d", &eng[i]);
printf("수학 점수를 입력하고 Enter를 누르세요=");
scanf("%d", &mat[i]);
printf("C언어 점수를 입력하고 Enter를 누르세요=");
scanf("%d", &c[i]);
total[i] = kor[i] + eng[i] + mat[i] + c[i];
avg[i] = total[i] / 4.0;
avgkor = avgkor + kor[i];
avgeng = avgeng + eng[i];
avgmat = avgmat + mat[i];
avgc = avgc + c[i];
}
printf("┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓\n");
printf("┃ 성 적 처 리 결과입니다. ┃\n");
printf("┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛\n");
printf("학번 총점 평균\n");
for (i = 0; i < 5; i++) {
printf("%3d %6d %7.2f\n", num[i], total[i], avg[i]);
}
printf("국어 평균: %g\n영어 평균: %g\n수학 평균: %g\nC언어 평균: %g", avgkor / 5., avgeng / 5., avgmat / 5., avgc / 5.);
return 0;
}
