350 likes | 917 Views
비트 논리 연산자. 비트 논리 연산자 0 또는 1 만 저장할 수 있는 비트 (bit) 단위로 연산을 하는 연산자. 1 bit. 전체는 1 byte. 두 비트의 내용이 서로 다르면 1, 아니면 0. 논리곱 비트 연산. 연산 3 & 5 피연산자인 3 과 5 를 이진수로 표현된 각각의 비트에 대해 and 연산을 수행함 3 00000000 00000000 00000000 00000 011 5 00000000 00000000 00000000 00000 101
E N D
비트 논리 연산자 • 비트 논리 연산자 • 0 또는 1만 저장할 수 있는 비트(bit) 단위로 연산을 하는 연산자 1 bit 전체는 1 byte 두 비트의 내용이 서로 다르면 1, 아니면 0
논리곱 비트 연산 • 연산 3 & 5 • 피연산자인3과 5를 이진수로 표현된 각각의 비트에 대해 and 연산을 수행함 3 00000000 00000000 00000000 00000011 5 00000000 00000000 00000000 00000101 3&5 00000000 00000000 00000000 00000001 • 3 && 5 와 비교 3과 5 둘 다 논리값으로 전환되면 참에 해당하므로 결과는 참 논리값참은 정수 1에 해당하므로 1이 출력된다. 거짓은 0에 해당 printf(“%d”, 3&&5); 1 4바이트(4*8 비트)
예제 • 각 비트 연산자의 결과 • 사용자 입력 정수 x와 y를 표준입력으로 두 개의 변수에 저장 • 두 개의 변수에 대하여 AND, OR, XOR, NOT 비트 연산을 수행한 결과를 다음 그림과 같이 출력하는 프로그램을 작성
이동(Shift) 연산자 한 비트씩 왼쪽이나 오른쪽으로 이동시키는 연산자 이동 연산자
이동 연산 예 6 21 6 22 6 / 21 6 / 22
비트 마스크 • Bit Mask • 원하는위치의 비트 값을 얻어내거나 변경하는 데 사용되는 상수나 변수 • 변수 arm에 저장된 오른쪽 끝에서 세번째 비트의 값을 출력하려면? 오른쪽세번째 비트만 1인 정수와 & 연산을 함 결과가 0이라면 0, 아니라면 1임을 알 수 있다. arm mask=4; // 22 arm & light printf(“%d”, arm & mask)
지역과 전역 변수 • 변수의 선언 위치에 따른 구분 • 지역 (local) 변수와 전역 (global) 변수로 나누어 짐 1. 지역 변수 • 함수 또는 블록({}) 안에서 선언되어 그 안에서만 사용되는 변수 • 내부 변수라고도 함 2. 전역 변수 • 함수 외부에서 선언되어 사용되는 변수로선언된 이후로 어떤 함수에서도 사용 가능한 변수 • 함수에서 전역변수를 수정하면 다른 함수에도 영향을 미침 • 외부 변수라고도 함 • 변수의 스코프(유효 범위, scope) • 변수를 사용할 수 있는 영역 • 일반적으로 자신이 선언된 함수 또는 블록이 스코프임
내부 블록의 변수/노트 int main() { int i, sum=0; for(i=1; i<=10; i++) { int number; printf(“정수 한 개 : “); scanf(“%d”, &number); sum += number: } printf(“마지막 정수 값 : %d“, number); } 전역변수와 동일한 이름의 지역변수가 선언되면? 지역변수와 동일한 이름의 내부 블록 변수가 선언되면? • { } 안에서 새롭게 변수를 선언할 수 있다. • 시작부분에만 가능 • 자신이 선언된 블럭 { } 을 벗어나면 사용할 수 없다.
변수가 정의되는 위치에 따른 전역변수와 지역변수를 정리 전역, 지역 변수 정리
변수의 종류에 따른 생존기간과 유효 범위만을 정리 전역변수와 정적 변수는 모두 생존 기간이 프로그램 시작 시에 생성되어 프로그램 종료 시에 제거 다만 자동 지역변수와 레지스터 변수는 함수가 시작되는 시점에서 생성되어 함수가 종료되는 시점에서 제거 위 표에서 변수의 유효 범위는 O, X로 구분되어 있다. 즉 그 지역에서 참조 가능하면 O, 아니면 X 생존기간과 유효범위
전역 / 지역 변수 • 프로그래머 관점에서는 • 프로그램 전체에서 공동으로 이용할 변수가 필요한 경우, 전역 변수로 선언한 후 이용 인수 전달과 결과 리턴이 필요 없다.그러나, 전역 변수의 수정이 모든 함수에 영향을 미치므로(side effect) 디버깅이 어려움 • 단지 함수(블록) 안에서만 이용할 변수는 그 함수(블록)의 지역 변수로 선언하여 사용 동일 변수명을함수별로 독립되게 사용할 수 있다. 함수간 정보 전달을 위해 인수 전달이 필요하다. • 전역 변수 • 성적처리 과목수, 학교명, 공제대상 자녀수, 학기당 최대 신청 가능한 학점수 • 지역 변수 • 변수명sum이 여러 함수안에서 각자의 의미로 사용 가능 • for 제어변수, 각종 카운팅 변수, 임시 저장소…
예제 소스 #include <stdio.h> int gVar = 100; int main(void) { int count = 10; count += gVar; printf("count = %5d, gVar = %5d\n", count, gVar); { int count = 100; count += gVar++; printf("count = %5d, gVar = %5d\n", count, gVar); } printf("count = %5d, gVar = %5d\n", count, gVar); return 0; }
자동 변수 대 정적 변수 • 자동 변수 • 함수(블록)가 실행될 때 메모리(stack 영역)를 확보,함수(블록)가 종료되면 자동으로 제거됨 • 지역 변수들이 자동 변수에 해당하며 선언시 “auto”를 앞에 명시해야 하나 이 경우엔 생략 가능 auto int total; int total; 지금까지의 지역 변수는 모두 auto 변수 주) 전역변수는 프로그램 실행시 메모리 확보 프로그램 종료시 메모리가 제거됨 즉 auto가 아님
자동 변수 대 정적 변수 • 정적 변수 • 지역 변수와 같이 변수의 스코프는 자신이 선언된 함수(또는 블록)이지만, 지역 변수와 달리 프로그램 실행시 메모리가 할당되고 프로그램이 종료되면 메모리에서 제거된다. 즉, 일반 함수의 auto 변수와 달리 함수가 종료되더다로 메모리가 계속 확보되어 있음 • 선언시 “static”을 명시 • 초기 값을 지정하지 않으면 자료유형에 따라 0이나 \0(null) 값을 갖는다. • 선언시 초기 값을 명시하더라도 이 초기값은 처음 한번만 정적 변수에 저장되며 그 다음부터는 초기화가 수행되지 않음
정적변수와자동변수 int main() { int count=0; for ( ; count < 5; count++) increment(); } void increment(void) { static int sindex=1; //정적 지역 변수 auto int aindex=1; //자동 지역 변수printf("정적 지역변수 sindex=%2d,\t", sindex); printf("자동 지역변수 aindex=%2d\n", aindex); sindex++; aindex++; }
변수 이용 기준 • 변수 이용 원칙 • 일반적으로 변수는 전역변수의 사용을 자제하고 지역변수인 자동변수로 이용 • 다음의 경우에는 그 특성에 맞는 변수를 이용 • 함수나 블록 내부에서 계속적으로 값을 저장하고 싶을 때는 정적인 지역변수를 이용 • 프로그램의 모든 영역에서 값을 공유하고자 하는 경우는 전역 변수로 이용 • 가능하면 전역변수의 사용을 줄이는 것이 프로그램의 이해를 높일 수 있으며, 프로그램 문제를 줄일 수 있음 • 함수의 형식인자는 지역변수와 같이 함수 내부에서만 유효
전역/지역 예제 : 두 변수의 값을 교환 (권장) • 두 변수 a와 b의 값을 교환하는 함수 작성 void swap(int ___, int ____); //원형 선언 void main() { int n1=10, n2=5; printf(“교환전: %d, %d”, n1, n2); swap(___n1, ___n2); printf(“교환후: %d, %d”, n1, n2); } void swap(int ___ a, int ____ b) //함수 정의 { int temp; temp=___a; ___a=___b; ___b=temp; } * * & & * * * * * *
전역/지역 예제 :두 변수의 값을 교환 (권장하지 않음) • 인수를 사용하지 않고 global 변수를 활용하기 void swap(void); //원형 선언 int n1, n2; //전역 변수 선언 void main() { n1=10; n2= 5; printf(“교환전: %d, %d”, n1, n2); swap(); printf(“교환후: %d, %d”, n1, n2); } void swap(void ) //함수 정의 { int temp; temp=n1; n1=n2; n2=temp; } • swap()함수를 재활용하기 어렵다. • n1, n2가 아닌 다른 두 변수의 값을 교환하고자 한다면?
메모리 할당 • 변수 메모리의 정적(static) 할당 • 프로그램 실행 전에 이미 변수를 위한 메모리의 크기가 결정되어 있다. • 사용할 메모리 공간의 크기를 정확히 예측할 수 없는 경우충분한 메모리를 확보할 수 있도록 해야 하므로 메모리 낭비가 발생할 수 있다. 예) 성적 처리 대상 학생의 수는? 가장 많은 학생 수로 배열의 크기를 결정해야 한다. • 메모리의 동적(dynamic) 할당 • 프로그램 실행 중에 필요한 메모리를 할당하는 방법 • 함수 malloc()을 이용 메모리 공간을 확보
동적 메모리 할당 함수 malloc() • malloc() 함수; • #include <stdlib.h> • 사용방법 형 *포인터변수; 포인터변수 = (형 *) malloc(메모리크기); 인수인 “메모리크기” 만큼의 메모리가 할당되고 이 메모리의 시작주소가 반환되어 “포인터변수”에 저장됨 • 예) int *pi; pi = (int *) malloc( sizeof(int) ); *pi = 3; int형 자료 한 개 저장할 공간 int * pi
메모리 해제 함수 free() • free(포인터변수); • “포인터변수”가 가리키는 동적할당 메모리가 해제됨& 성공적으로 해제되면 포인터변수엔 NULL이 저장됨 • 동적으로 할당된 메모리 공간은 프로그램에서 해제하기 전까지는 다른 프로그램에서 사용할 수 없다.-> 더 이상 필요없을 때 해제함으로써 다른 프로그램 또는 자신의 프로그램에서 다른 동적 할당에 사용할 수 있게 해야 함-> 메모리 누수 방지 pi = (int *) malloc( sizeof(int) ); : free(pi); 이후로 pi가 가리키던 메모리는 다른 용도로 사용될 수 있게 됨
배열과 같이 연속된 메모리의 동적 할당 • int 형 자료를 3개 저장할 수 있는 메모리 공간의 동적 할당 • 포인터 ary를 이용하여 다음과 같이 배열처럼 이용할 수 있다. • 메모리 해제 int *ary; ary = (int *) malloc( sizeof(int)*3 ); int * ary ary[0] = 10; ary[1] = 11; ary[2] = 12; free(ary);
예제 • 함수 malloc()을 이용하여 할당 받은 메모리 공간을 배열로 사용 가능 • ary= (int *) malloc( sizeof(int)*3 ); • ary[0] = 10; ary[1] = 11; ary[2] = 12; • for (i = 0; i < 3; i++) • printf("ary[%d] = %d ", i, *(ary + i) ); • // ary[i]와 동일 • printf("\n"); • free(ary);
동적 할당을 이용한 성적 처리 / 노트 성적 처리 대상 학생 수가 다른 경우 배열의 원소수는 최대학생수로 지정해야 한다. 메모리 낭비가 생길 수 있다. void main() { int * score, students, i, sum=0; double ave; printf(“학생수는?”); scanf(“%d”, &students); score = (int *) malloc(sizeof(int) * students); 직접 4를 사용할 수도 있다. for(i=0; i < students; i++) scanf(“%d”, &score[i]); for(i=0; i < students; i++) sum += score[i] ; free(score); ave=(double) sum/students; }
문자형 포인터의 동적 할당 • 문자열을 처리하기 위해 char형 포인터를 사용할 때 반드시 포인터에 메모리를 동적으로 할당한 후 문자열 저장에 사용해야 함 • 예) char *temp; temp = (char *) malloc( sizeof(char) * 9 ); printf(“이름은?”); scanf(“%s”, temp); // 이후temp=“홍길동”; 과같은 대입문이 가능함 char * temp chartemp[9]; printf(“이름은?”); scanf(“%s”, temp); // 이후에도 temp=“홍길동”은불가능하며 // strcpy(temp, “홍길동”);으로 함수를 사용해야 함
포인터 배열 • 길이가 서로 다른 SIZE 개의 주소 문자열을 처리하기 char * addr[SIZE]; addr[0] addr[1] addr[2] addr[SIZE-1] • 포인터 addr[i] 마다 각자 필요한 • 문자열 길이만큼 동적으로할당받아 사용하기 • addr[i] = (char *) malloc(주소의길이+1);
포인터 배열 Q) char * addr[SIZE]로 선언했다면? 각각에 대한 동적할당이 필요 #define SIZE 3 #define LENGTH 30 void print( char * string[] ); void main() { int i, k; char * addr[SIZE]; char tempAddr[LENGTH];//주소를 입력받기 위한 임시장소로 최대 길이로 정해둠 for ( i=0; i<SIZE; i++) { printf("주소? "); gets(tempAddr); addr[i] = (char *) malloc(strlen(tempAddr)+1); strcpy(addr[i], tempAddr); }
포인터 배열 for (k=0; k<SIZE-1; k++) { for (i = 0; i < SIZE-1-k; i++) if (strcmp(addr[i], addr[i+1]) > 0) { char * temp; temp=addr[i]; addr[i]=addr[i+1]; addr[i+1]=temp; } } print(addr); return 0; } void print( char * string[] ) { // void print(char ** string) 도 가능 int i; for ( i = 0; i < SIZE; i++) puts(string[i]); } 문자열 내용을 변경하는 것이 아니라 포인터안의 주소를 변경
포인터 배열의 정렬 /1 Q) char * name[SIZE]로 선언했다면? 각각에 대한 동적할당이 필요 #include <stdio.h> #include <string.h> #include <stdlib.h> #define SIZE 3 voidprint(char * std[]) { // void print(char ** std) 도 가능 inti; for ( i = 0; i < SIZE; i++) puts(std[i]); } void main() { int i, k; char * name[SIZE]; char *temp; for ( i=0; i<SIZE; i++) { name[i] = (char *) malloc(sizeof(char)*10); printf("이름 : "); gets(name[i]); }
포인터 배열의 정렬 /2 for (k=0; k<SIZE-1; k++) { temp = (char *) malloc(sizeof(char) * 10); for (i = 0; i < SIZE-1-k; i++) if (strcmp(name[i], name[i+1]) > 0) { //주소를 변경하는 경우 // temp=name[1];name[1]=name[0]; name[0]=temp; strcpy(temp, name[i]); strcpy(name[i], name[i+1]); strcpy(name[i+1], temp); } }
줄의 연결을 표시하는 \ • 줄의 연결을 표시하는 \ • 한 줄에 코딩이 불편한 경우, 계속되는 줄 끝에 역슬래쉬\를 넣어 줄이 계속되고 있음을 표시해야 함 #define MSG “프로그램 언어의 학습은 일반 \ 언어의 학습과 마찬가지로 반복학습이 중요하다”