2011년6월7일_C과제7마무리(코드오류수정, 2의 보수, 1로 세트된 비트의 갯수, 교대급수), C과제7풀이
C과제7 |
1. 잘못된 부분을 찾아서 고치고, 이유를 설명하세요.
1) int num = 128;
char* pNum = #
printf(“%d”, *pNum);
답: int num = 128; //128을 입력
unsigned char *pNum = (unsigned char *)# //unsigned char *형으로 캐스팅
printf(“%d”, *pNum);
<이유>
변수num은 int형이나 포인터변수 pNum은 char형의 변수를 가리키는 char형 포인터변수임.
int형이든 char형이든 포인터변수의 크기는 CPU단위 처리량과 동일하게 32bit CPU의 경우 4Bytes라서..
컴파일시 int형 변수를 char형 포인터변수가 가리키더라도 에러는 아님.
그러나 이는 잘 못 된 코드이므로 컴파일러는 경고메세지를 아래와 같이 출력함.
그러므로 num을 char형으로 캐스팅해야 함. &num앞에 (char*)를 붙여 컴파일 해보니..
결과가 -128이 나옴. 128을 num에 넣었는데 -128이 나온다는 것도 잘 못된 부분이라 생각되어,
왜 이런 결과가 나왔나 분석을 하여 보니 printf( ) 함수 호출 시 작은 자료형인 char형에서 큰 자료형인 메모리의 Stack에 적재될 때 예전 CM선생님 수업시간 때 조사한 부호확장이 일어나,
128 -> 1000 0000 -> Stack영역(32bit)에 push되기 전 부호가 확장되어 상위 3Byte는 1로 채워짐 -> 2의 보수취함 -> -128이 됨.
양수 128이 음수가 되니 잘 못 되었음. 해결하려면 부호확장이 아닌 제로확장이 되도록 부호가 없는 정수형인 unsigned char로 캐스팅해야함.
unsigned char형 포인터를 선언하지 않고 int형을 가리키는 포인터 변수를 선언하여 num의 주소값을 넣으면 캐스팅할 필요도 없고 부호도 변하지 않으니 쉽게 해결할 수 있음.
int *pNum = #
2) int arr[4] = {0, 1, 2, 3};
int* ptr = arr[1];
답: int* ptr = arr + 1;
배열 arr의 두 번째 원소를 참조하려면 배열이름인 arr에 1을 더해야 한다.
arr[1]은 배열의 두 번째 원소의 값이지 주소가 아니라서 값인 1이 포인터변수에 들어가 1번지를 참조하게 된다. 1번지는 대부분의 컴퓨터에서 운영체제가 특별히 사용하는 공간일 확률이 매우 높다.
아래와 같은 런타임에러가 발생한다.
3) int Arr[5] = {3, 4, 5};
int* pArr = Arr;
답: Arr[3] = {3, 4, 5} 또는 Arr[5] = {3, 4, 5, 6, 7} <- 왠지 연속한 값을 넣어야 될 것 같음.
Arr[ ] 배열의 원소의 수가 5개로 많아
Arr[ ] 배열의 모든 값들의 초기화가 이루어지지 않아 4번째와 5번째 원소는 0이 된다.
4) char Al[4] = {‘A’, ‘B’, ‘C’, ‘D’};
char* pAl = Al;
printf(“%c %c %c %c \n”, pAl[0], Al[1], *(pAl+2), *(Al+3));
답: printf(“%c %c %c %c \n”, Al[0], Al[1], Al[2], Al[3]); -> 배열식 표기법
printf(“%c %c %c %c \n”, *(pAl + 0), *(pAl + 1), *(pAl + 2), *(pAl + 3)); -> 포인터식 표기법
배열식 표기와 포인터식의 표기를 교대로 하므로 가독성이 떨어지니 한 가지 표기법으로 통일해야함.
5) char str[6] = “System”;
printf(“%s \n”, str);
답: char str[7] = “System”;
str[ ] 배열의 크기가 문자열 “System”을 저장하기엔 1Byte작음.
6) char* pStr = “Control”;
printf(“%s \n”, *pStr);
답: printf(“%c \n”, *pStr); 또는
printf(“%s \n”, pStr);
%c형식지정자로 출력하면 문자열”Control”중 첫 글자인 ‘C’만 출력되고,
%s형식지정자로 출력하려면 주소값을 인자로 넘겨 줘야함.
주소값이 아닌 *pStr로 문자열”Control”중 첫 글자 ‘C’를 넘겨주면 ‘C’를 주소로 간주하여,
운영체제가 보호하는 영역의 값을 읽어 오는 시도를 하여 아래와 같은 런타임에러가 발생한다.
2. 입력된 정수의 2의 보수를 구하여 10진수, 16진수 형태로 출력하세요.
출력) Input Number : 1
2’s complement(10진수) : –1
2’s complement(16진수) : ffffffff
<힌트?>
N = ~N + 1;
10진수는 %d형식지정자로 출력.
16진수는 %x형식지정자로 출력.
<소스코드>
1: /*********************************************************************
2:
3: 2. 입력된 정수의 2의 보수를 구하여 10진수, 16진수 형태로 출력하세요.
4:
5: 출력) Input Number : 1
6: 2’s complement(10진수) : -1
7: 2’s complement(16진수) : ffffffff
8:
9:
10: 작 성 자: 김수만
11: 작성일자: 2011년 6월 6일
12:
13: *********************************************************************/
14: #include <stdio.h>
15:
16: int Conv_Two_Complement(int);
17:
18:
19: int main(void)
20: {
21: int iNum;
22:
23: printf("Input Number : ");
24: scanf("%d", &iNum);
25:
26: iNum = Conv_Two_Complement(iNum);
27:
28: printf("2's complement(10진수) : %d\n", iNum);
29: printf("2's complement(16진수) : %x\n", iNum);
30:
31: return 0;
32: }
33:
34:
35: int Conv_Two_Complement(int iNum)
36: {
37: iNum = ~iNum + 1; //비트를 반전한 후 1을 더 함.
38: //반전만 하면 1의 보수임.
39: //iNum = -1 * iNum; //-1을 곱하여 주어도 결과는 같음.
40:
41: return iNum;
42: }
int형 변수의 최대값인 2147483647보다 1만큼 큰 값은 음수 영역으로 최소값인 –2147483648이 된다.
80 00 00 00(십진수 2147483648)을 반전하면 7F FF FF FF이고 여기에 1을 더 하면 80 00 00 00이 된다.
80 00 00 00은 최상위비트가 1이므로 음수이고 얼마인지 알려면 2의 보수를 취해 2147483648이 되고 최상위 비트가 1이었으니,
-2147483648이 된다.
2147483649은 int형 변수의 최대값 보다 2가 큰 수로 -2147483647이다.
이를 반전한 후에 1을 더하면 7F FF FF FF이 되어 최상위비트가 0이다. 음수가 양수가 되어 버린다.
상기 32비트 자료형 unsigned int와 int형의 범위에 관한 그림을 보면,
0에서 1을 뺀 -1은 모든 비트가 1로 채워진 FF FF FF FF(십진수 4294967295)로 unsigned int형에선 최대값이다.
FF FF FF FF에 1을 더 하면 0이 되고 그림과 같이 화살표방향으로 이어져 있다.
정수의 최대값은 2^31 -1로 2147483647이고 여기에 1을 더한 2147483648은 int형에서는 음수의 최대값인 -2147483648로 다룬다.
음수도 알고 보면 양수인 것이다. (허접한 결론)
3. 1바이트는 8개의 비트로 이루어집니다. 255는 8개 비트 전체가 1인 경우이고, 1은 최하위 비트만 1인 경우입니다. 문자 ch의 켜진 비트 개수는 몇 개입니까? 켜진 비트는 1로 설정된 비트의 다른 표현입니다.
대문자 ‘A’의 값은 65이고 6번째와 1번째 비트가 켜져 있습니다.
출력) 문자 입력 : A
켜진 비트의 개수 : 2
값 : 65
<해법찾기>
입력된 값과 1을 AND연산하여 결과가 0이 아니면 카운터값 1증가.
즉, 1로 세트된 비트의 수를 카운트.
<해법표현>
값을 num에 입력.
명령어 | 결과 |
(num & 0x01) | num의 0번 비트 (LSB) |
num = num >> 1; | num / 2 |
(num & 0x01) | num의 1번 비트 |
num = num >> 1; | num / 4 |
(num & 0x01) | num의 2번 비트 |
num = num >> 1; | num / 8 |
(num & 0x01) | num의 3번 비트 |
num = num >> 1; | num / 16 |
(num & 0x01) | num의 4번 비트 |
num = num >> 1; | num / 32 |
(num & 0x01) | num의 5번 비트 |
num = num >> 1; | num / 64 |
(num & 0x01) | num의 6번 비트 |
num = num >> 1; | num / 128 |
(num & 0x01) | num의 7번 비트 |
<소스코드>
1: /*********************************************************************************
2:
3: 3. 1바이트는 8개의 비트로 이루어집니다. 255는 8개 비트 전체가 1인 경우이고,
4: 1은 최하위 비트만 1인 경우입니다. 문자 ch의 켜진 비트 개수는 몇 개입니까?
5: 켜진 비트는 1로 설정된 비트의 다른 표현입니다.
6: 대문자 ‘A’의 값은 65이고 6번째와 1번째 비트가 켜져 있습니다.
7:
8: 출력) 문자 입력 : A
9: 켜진 비트의 개수 : 2
10: 값 : 65
11:
12: 작 성 자: 김수만
13: 작성일자: 2011년 6월 6일
14:
15: *********************************************************************************/
16: #include <stdio.h>
17:
18: unsigned char Detect_SetBit(unsigned char);
19:
20:
21: int main(void)
22: {
23: unsigned char cCh;
24: unsigned char cNum;
25:
26: printf("문자 입력 : ");
27: scanf("%c", &cCh);
28:
29: cNum = Detect_SetBit(cCh);
30:
31: printf("켜진 비트의 개수 : %d\n", cNum);
32: printf("값 : %d\n", cCh);
33:
34: return 0;
35: }
36:
37: //1Byte 정수를 받아 1로 세트되어 있는 비트의 갯수를 반환하는 함수.
38: unsigned char Detect_SetBit(unsigned char ucNum)
39: {
40: char cLoop;
41: char cCnt = 0;
42:
43: //0번째 비트 부터 7번 비트까지 검사.
44: for(cLoop = 0 ; 8 > cLoop ; ++cLoop)
45: {
46: if(0 != ((ucNum >> cLoop) & 0x01)) //1로 세트된 경우?
47: {
48: ++cCnt; //세트된 비트 카운트
49: }
50: }
51:
52: return cCnt;
53: }
[도전] 두 개의 정수를 입력받고 두 정수 사이의 부호를 다음과 같이 바꿔가면서 계산한 식과 결과를 출력하라.
출력) 정수(2개) 입력 : 1 10
X = 1 – 2 + 3 – 4 + 5 – 6 + 7 – 8 + 9 – 10
X = -5
정수(2개) 입력 : 2 6
X = 2 – 3 + 4 – 5 + 6
X = 4
<이 론>
교대급수: (-1)^n+1
멋진 수열이구나!
수식을 사용하지 않고 조건문으로만 코딩...
<소스코드>
1: /*********************************************************************************
2:
3: [도전] 두 개의 정수를 입력받고 두 정수 사이의 부호를,
4: 다음과 같이 바꿔가면서 계산한 식과 결과를 출력하세요.
5:
6: 출력) 정수(2개) 입력 : 1 10
7: X = 1 - 2 + 3 - 4 + 5 - 6 + 7 - 8 + 9 - 10
8: X = -5
9:
10: 정수(2개) 입력 : 2 6
11: X = 2 - 3 + 4 - 5 + 6
12: X = 4
13:
14:
15: 작 성 자: 김수만
16: 작성일자: 2011년 6월 6일
17:
18: *********************************************************************************/
19: #include <stdio.h>
20:
21: void Swap(int *, int *);
22:
23: int main(void)
24: {
25: int iMax;
26: int iMin;
27: int iLoop;
28: int iSum;
29: int iStatus;
30:
31: iSum = 0;
32: iStatus = 0;
33:
34: printf("정수(2개) 입력 : ");
35: iStatus = scanf("%d%d", &iMin, &iMax);
36:
37: if(2 != iStatus) //입력한 수의 갯수가 2개 아니면 종료.
38: {
39: printf("입력 오류!");
40: return 0;
41: }
42:
43: if(iMin > iMax) //최소값이 최대값보다 크면 맞교환.
44: {
45: Swap(&iMin , &iMax);
46: }
47:
48: iSum = iMin; //최소값 + 1부터 루프를 도니 최소값은 저장.
49: iStatus = 0; //홀수번째인가 짝수번째인가 확인용 변수를 초기화.
50: printf("X = %d", iMin);
51:
52: //수식을 출력하는 반복문 ex)1 - 2 + 3 - 4 + 5 - 6 + 7 - 8 + 9 - 10
53: for(iLoop = iMin + 1 ; iMax >= iLoop ; ++iLoop)
54: {
55: if(0 == (iStatus % 2)) //짝수번째이면 -연산
56: {
57: iSum = iSum + (-1 * iLoop);
58: printf(" - %d", iLoop);
59: }
60: else //홀수번째이면 +연산
61: {
62: iSum = iSum + iLoop;
63: printf(" + %d", iLoop);
64: }
65:
66: ++iStatus;
67: }
68:
69: printf("\nX = %d", iSum); //합 출력.
70:
71:
72: return 0;
73: }
74:
75: //두 정수를 바꾸는 함수.
76: void Swap(int *iNum1, int *iNum2)
77: {
78: int iTemp;
79:
80: iTemp = *iNum1;
81: *iNum1 = *iNum2;
82: *iNum2 = iTemp;
83:
84: return ;
85: }
C과제7 풀이 |
1. 잘 못 된 부분을 찾아 고치고, 이유를 설명하시오.
1) int num = 128;
char *pNum = #
int와 char형이 달라 시작주소에서 1Byte만 읽거나 쓸 수 있다.
0x10 0x11 0x12 0x13
num 128 0 0 0
↑
*pNum은 여길 가리킴. (1Byte만 다룸)
다른 방법으로…
char num = 128;
int *pNum = #
포인터가 가리키는 곳의 자료형보다 커서 다른 영역을 침범함.
128만 읽어 들이는 것이 아니라 쓰레기값도 같이 읽어 들임.
0x10 0x11 0x12 0x13
num 128 xx xx xx
↑
*pNum
2) int arr[4] = {0, 1, 2, 3};
int *ptr = arr[1];
int *ptr = &arr[0] 또는 *ptr = arr; (배열명 = 주소)
3) 틀린 점은 없고 배열 공간이 넉넉하다는 것.
4) 틀린거 없고 포인터식 표기법과 배열식 표기법이 섞여 있다.
5) char str[6] = “System”;
배열의 크기가 1Byte부족하여 엉뚱한 문자까지 같이 출력된다.
문자열은 끝에 NULL문자가 같이 딸려온다. 6자를 넣으려면 7자를 넣을 공간이 확보되어야 한다.
6) char *pStr = “Control”;
printf(“%s\n”, *pStr);
printf(“%s\n”, pStr); 또는 printf(“%s\n”, &pStr[0]);
포인터도 배열과 같이 이름 자체가 주소이고 배열 첫 번째 원소의 주소가 시작주소이므로 위와 같이 둘 다 가능하다.
2. 2의 보수처리 후 출력
2의 보수를 만드는 법…
1) 비트연산 (~N + 1)
2) 산술연산 (N * –1)
3) 산술연산 + 비트연산 -(N) + 1
%d - 10진수 출력
%x - 16진수 출력(소문자), %X - 16진수 출력(대문자)
3. 1로 세트된 비트의 갯수 구하기
N = 65 (문자 ‘A’를 입력)
반복문을 돌려 각 비트를 AND연산한 결과가 0이 아닌 횟수를 세면 됨.
1: for(i = 0 ; 8 > i ; i++)
2: {
3: if((ch & (1 << i)) != 0)
4: {
5: nON = nON + 1;
6: }
7: }
다른 방법으로 입력받은 N을 시프트…
1: for(i = 0 ; 8 > i ; i++)
2: {
3: if((1 & (ch >> i)) == 1) nON = nON + 1;
4: }
5:
결과는 0또는 1이 나오므로 다음과 같이 결과값을 더하기만 해도 됨. (단, nON값은 0으로 초기화된 후)
1: for(i = 0 ; 8 > i ; ++i)
2: {
3: nON = nON + (1 & (ch >> i));
4: }
[도전] 두 개의 정수를 입력받고 두 정수 사이의 부호를 다음과 같이 바꿔가면서 계산한 식과 결과를 출력하라.
출력) 정수(2개) 입력 : 1 10
X = 1 – 2 + 3 – 4 + 5 – 6 + 7 – 8 + 9 – 10
X = -5
정수(2개) 입력 : 2 6
X = 2 – 3 + 4 – 5 + 6
X = 4
홀수번째는 (-) 짝수번째는 (+)
1: //N1와 N2를 입력 받음. (N1은 최소값, N2는 최대값)
2:
3: result = 0;
4:
5: for(i = 0 ; i <= N2 - N1 ; i++)
6: {
7: if(i == 0) { //처음
8: val = N1 + i;
9: printf("X = %d", val);
10: }
11: else if(i % 2 == 1) { //홀수
12: val = -(N1 + i);
13: printf("%d", val);
14: }
15: else if(i % 2 == 0) { //짝수
16: val = N1 + i;
17: printf("+%d", val);
18: }
19: }