Sister Nosilv story

[쉽풀C] 9장 함수와 변수_중간점검 도전문제 Exercise Programming 해설

by 노실언니

쉽게 풀어쓴 C언어 Express - 천인국 - 생능출판사 - 개정3판

Chapter 09 함수와 변수
9.1 변수의 속성
9.2 지역 변수
9.3 전역 변수
9.4 생존 시간
Lab 은행 계좌 구현하기
Lab 한번만 초기화하기
9.5 연결
9.6 어떤 저장 유형을 사용하여 하는가?
Lab 난수 발생기 작성
9.7 가변 매개 변수 함수
9.8 순환 호출


Chapter 9. 함수와 변수

- #변수선언위치 & #저장유형지정자 → 범위 생존시간 연결 알기

- #순환호출 이해하기

"변수 Variable"
변수 사용가능범위

* 내 식대로 챕터정리

# 자동저장 + 레지스터 > 지역정적 > 전역, 전역정적 > 외부 참조 변수

 일반적: 자동저장 - 알아서 생성소멸 / 메모리 효율적 / 블록단위이므로 독립성 보존

 

# 범위(scope, visibility)

 └ 전역변수(global variable) : 프로그램 내 모든 공간에서 접근가능 

 └ 지역변수(local variable) : 특정 지역(함수, 블록)에서 접근가능

* 매개변수도 일종의 지역변수

# 메모리 저장시간 ≒ 생존시간(lifetime)

 └ 정적할당(static allocation) : 쭉 ∋ {전역 변수, static 지역변수(자동초기화: 0 )}

 └ 자동할당(automatic allocation) : 깜빡 ∋ {(auto) 지역변수, register 지역변수}(자동초기화: 쓰레기값)}

* register는 주소가 없으므로 &를 사용할 수 없다.

# 연결(linkage)

 └ 무(no) = 지역변수 : 해당 지역에서만 접근가능

 └ 내부(internal) ⊂ 전역변수 : 해당 소스파일 속 전 지역에서 접근가능

 └ 외부(external) ⊂ 전역변수 : 한 프로젝트 속 모든 소스파일에서 접근가능

* 전역/지역변수 ≠ 정적/자동할당

* 전역변수=블록외선언→정적할당됨

 

# 변수선언(Declaration): 자동초기화→ 전역변수(0) & 지역변수(쓰레기값)

 └ 1. 키워드 ❌

  └ 블록/함수 內 선언=블록 內 이용가능: (예) for(int a; ; ), 파라미터(인수값 복사)

    ① 범위: local 지역변수 (블록은 local)

    ② 생존시간: automatic allocation 자동할당 (stack 내 블록의 실행~종료)

    ③ 무연결 no linkage

  └ 함수 外 선언=프로그램에 쓰인 모든 소스파일이 이용가능

    ① 범위: global 전역변수

    ② 생존시간:  정적할당(프로그램실행~종료)

    ③ 외부연결 external

* 전역 비추: 어디서 값을 고쳐버릴지 모름(꼬임)≒ 프로그래밍 지향점(모듈화, 독립성)에서 어긋남

∴ 전역변수를 값복사(call by value)해서 이용하는 것은 괜찮

 └ 2. 키워드 사용: auto / resister / static / extern / volatile

  └ (auto): 키워드 없이 블록/함수 內 변수 선언시 Default로 적용되는 키워드: 지역변수|자동할당|무연결 

  └ 블록/함수 內 resister :지역변수|자동할당|무연결

  └ 블록/함수 內 static : 지역변수|정적할당|무연결

  └ 함수 外 static : 전역변수|정적할당|내부연결(해당소스파일)

  └ 함수 外 extern : 전역변수를 외부에서 이용할 때 사용|정적할당|외부연결(프로그램의 모든소스파일)

  └ volatile(변수)선언: 현 코드 컴파일러의 최적화 방지

예) a=1; while(a!=0){···} → while(true){···}

원래는 무한루프로 최적화하는데 이런 최적화를 안하게 만듬

 

# 가변매개변수

 ① #include <stdarg.h> // standard argument 헤더 ∋ { va_list, va_start, va_end, va_arg }

 ② 반환형 함수명 ( int 인자의 갯수를 담아둘 변수, ... ) {

   va_list 포인터명;  // 인자리스트를 가리키는 포인터 생성

   va_start ( 포인터명, 인자갯수담은변수 ) ;  // 위 두 변수를 묶어주어 개변매개변수 사용시작 Macro

   va_arg ( 포인터명, 인자자료형 ); // 가변인자리스트로 가서 인자호출, 호출할 때마다 자료형크기만큼 이동 Macro

   va_end ( 포인터명 );  // 해당 리스트 사용종료 Macro

   }


p383. 중간점검

⒈ 변수의 범위는 대개 무엇으로 결정되는가?

 🢩 해당 변수를 선언하는 위치 (374p)

⒉ 변수의 범위에는 몇 가지의 종류가 있는가?

 🢩  2종류; 전역(ALL)&지역(그곳만) (375p)

⒊ 파일 범위를 가지는 변수를 무엇이라고 하는가?

 🢩 전역변수 global variable (375p)

⒋ 블록 범위를 가지는 변수를 무엇이라고 하는가?

 🢩 지역변수 local variable (375p)

⒌ 지역 변수를 블록의 중간에서 정의할 수 있는가?

 🢩 네 (376p)

⒍ 똑같은 이름의 지역 변수가 서로 다른 함수 안에 정의될 수 있는가?

 🢩 네 (로컬차이) (376p)

⒎ 지역 변수가 선언된 블록이 종료되면 지역 변수는 어떻게 되는가?

 🢩 특정 블록 내에서 선언된 지역 변수는 시스템 스택(stack)에 자동할당(automatic allocation)된다.

  그 블록이 종료되면 지역변수가 할당되어있는 메모리공간도 자동으로 회수되어 지역 변수가 소멸한다.(377p)

⒏ 지역 변수의 초기값은 얼마인가?

 🢩 쓰레기 값 garbage value (378p)

⒐ 함수의 매개 변수도 지역 변수인가?

 🢩 네 (378p) 인수의 값을 복사하여 자동할당된 지역변수 

⒑ 전역 변수는 어디에 선언되는가?

 🢩 함수외부 (380p)

⒒ 전역 변수의 생존 기간과 초기값은?

 🢩 프로그램실행~종료, Default값: 0 (380p)

⒓ 똑같은 이름의 전역 변수와 지역 변수가 동시에 존재하면 어떻게 되는가?

 🢩 특정 지역 내 사용시엔 해당 지역변수가 참조됨 (≒홈어드벤티지)

  해당 지역 외 사용시 전역변수 사용 (382p)

 

p387. 중간점검

⒈ 저장 유형 지정자에는 어떤 것들이 있는가?

 🢩 auto, resister, static, extern, volatile

⒉ 지역 변수를 정적 변수로 만들려면 어떤 지정자를 붙여야 하는가?

 🢩 static

* 지역(범위내이용가능) / 전역(전범위사용가능) /// 자동(생존=범위생성소멸) / 정적(생존=프로그램생성소멸)

⒊ 변수를 CPU 내부의 레지스터에 저장시키는 지정자는?

 🢩 resister

⒋ 컴파일러에게 변수가 외부에 선언되어 있다고 알리는 지정자는?

 🢩 extern; '이 변수는 외부에 있다 그것을 사용할 것이다.'

⒌ static 지정자를 변수 앞에 붙이면 무엇을 의미하는가?

 🢩 # 정적

   ① lifetime: 쭉; 프로그램시작시 생성~ 종료시 소멸

   ② link: 내부연결 (현 소스코드 내에서만 사용가능=extern불가)

 

p389. 도전문제

⒈ 정적 변수 balance를 전역 변수로 변경하여 프로그램을 다시 작성해보자.

 🢩

/*
	저금액->전역변수
	잔고변동 save(int amount);
*/

#include <stdio.h>

unsigned int balance = 0;	// 정적static→전역global
void save(int amount);

int main(void) {
	printf("================================================\n");
	printf("입금\t\t출금\t\t잔고\n");
	printf("================================================\n");
	save(10000);
	save(50000);
	save(-10000);
	save(-60000);
	save(30000);
	printf("================================================\n");
	return 0;
}

void save(int amount) {
	if ((long) balance + amount < 0)
		printf("\t\t%d\t\t출금불가\n", -amount);
	else if (amount<0){
		balance += amount;
		printf("\t\t%d\t\t%d\n", -amount, balance);
	}
	else {
		balance += amount;
		printf("%d\t\t\t\t%d\n", amount, balance);
	}
}

정적과 전역을 헷갈리지않도록 

p390. 도전문제

⒈ 위의 프로그램은 정적 지역 변수를 사용한다. 전역 변수를 사용할 수도 있다. 전역 변수를 사용하도록 위의 프로그램을 변경하여 보자.

 🢩

/*
	생존주기가 프로그램과 동일하여
	초기화를 한번만 한다는 특징을 가진 전역변수를 이용하여
	네트워크 초기화를 <한번만> 하는 유사 프로그램 제작
*/

#include <stdio.h>

void init(void);

int main(void) {
	for(int i = 0; i<3; i++)
		init();
	return 0;
}

void init(void) {
	static counter = 0;
	if (counter == 0) {
		printf("init(): 네트워크 장치를 초기화합니다.\n");
		counter++;
	}
	else
		printf("init(): 이미 초기화되어 초기화하지 않습니다.\n");
}

 

p394. LAB 난수발생기

⒈ 난수 발생기 작성

 🢩

// random.c

#define SEED 17
int MULT = 25173;
int INC = 13849;
int MOD = 65536;

static unsigned int seed = SEED;	// 정적 전역 0~ 변수

// 정수형 난수 생성 함수
unsigned random_i(void) {
	seed = (MULT * seed + INC) % MOD;
	return seed;
}

// 실수형 난수 생성 함수
double ramdom_f(void) {
	seed = (MULT * seed + INC) % MOD;
	return seed / (double)MOD;
}
// main.c

#include <stdio.h>

extern unsigned random_i(void);	// 함수도 호출가능하네
extern double random_f(void);
extern int MOD;

int main(void) {
	MOD = 10;
	for (int i = 0; i < 10; i++)
		printf("%d ", random_i());
	return 0;
}

 

p396. 가변 매개 변수 함수

/* 9.7 가변 매개 변수 함수
*  함수의 매개 변수의 갯수를 호출할 때마다 달라지게 하는 기능!
*  예) printf() - 가변적이다
*/

#include <stdio.h>
#include <stdarg.h>	//standard argument?

int sum(int, ...);
// 가변 매개 변수를 만들기위해서는 stdarg 와 ...를 사용해야 함

int main(void) {
	int answer = sum(4, 4, 3, 2, 1);
	printf("합 : %d\n", answer);

	return 0;
}

int sum(int num, ...) {	// num: 변수갯수
	int answer = 0; // auto
	va_list argptr;	// stdarg의 new type, name: argument pointer

	va_start(argptr, num);	// 리스트의 첫 포인터인 argptr과 num값 이용해서 가변매개변수도입
	for (; num > 0; num--)
		answer += va_arg(argptr, int);	// 리스트의 인수호출(자동으로 호출하고 그다음으로 넘어가나보ㅗㅁ)

	va_end(argptr);	// 가변매개변수기능 종료
	return answer;
}

 

p397. 순환호출Recursion

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

long factorial(int n);

int main(void) {
	int n;
	printf("팩토리얼 계산기: ");
	scanf("%d", &n);

	printf("%d! = %ld\n", n, factorial(n));
	return 0;
}

// 1. 기존
long factorial(int n) {
	long result = 1;
	for (; n > 0; n--)
		result *= n;

	return result;
}

// 2. recursion
long factorial(int n) {
	printf("%d!\n", n);
	if (n <= 1)
		return 1;		// n=0 일 때, n! = 1;
	else				// n>=1 일 때, n! = n * (n-1)!
		return n * factorial(n - 1);
}

 

p399. ★2진수 형식으로 출력하기★

C언어는 정수를 2진수로 출력하는 기능이 없다.

이 기능을 순환 호출을 이용하여 구현하여 보자.

이해하는데 3시간 걸림

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

void print_binary(int decimal);

int main(void) {
	int number;
	printf("10진수: ");
	scanf("%d", &number);

	print_binary(number);

	return 0;
}

void print_binary(int decimal) {
	if (decimal > 0) {
		print_binary((int) decimal / 2);
		printf("%d ", decimal % 2);
	}
}

이 다음은 분책된 책이 다 젖어버려서 STOP...ㅜㅜ

 

태그: Study UNIV C 쉽풀C

반응형

블로그의 정보

노력하는 실버티어

노실언니

활동하기