출처 : cafe.naver.com/laydigital/
안녕하세요, 강좌를 연습하다 C언어 문법에 대해 궁금증이 생겨 질문드립니다.
아래 올린코드는 제가 연습한 코드인데요. 아래 코드에서 void buzzer(int hz, int cnt); 부분을 늘 해보던데로
해보던데로 void buzzer(); 이런식으로 안에 받는 인수명을 안적고 그냥 했었습니다.
실제로 void buzzer(); 했을 경우 원하는 소리음이 나오지 않아 다른 변수들의 변수형을 바꿔가며 하다
void buzzer(int hz, int cnt); 으로 해보고 나서 아래 코드로 이상없이 원하는 형태로 구현 되는것을
확인했습니다.
두 구문 모두 컴파일에러 없이 실행됐는데 안에 인수명을 적어 놓은것과 않적은 두함수의 차이가 어떤점에서
문제가 생긴것인지 궁금합니다.
void buzzer(int hz, int cnt); 과 void buzzer(); 의 차이점이 궁금합니다.
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#include <avr/io.h>
#define F_CPU 16000000UL
#include <util/delay.h>
void buzzer(int hz, int cnt); // 함수선언시 받는 인자의 변수의 수형도 같이 선언
void init_buzzer();
float hz[] = {1046.6, 1174.6, 1318.6, 1397.0, 1568.0, 1760.0, 1795.6, 2093.2};
float loop[] = {1046.6, 1174.6, 1318.6, 1397.0, 1568.0, 1760.0, 1795.6, 2093.2};
int main()
{
int i;
init_buzzer();
while(1)
{
for (i=0; i<8; i++) // 도~도 반복
buzzer(hz[i], loop[i]);
_delay_ms(1000); //1sec
}
}
void init_buzzer()
{
DDRB = (1<<PB4);
}
void buzzer(int hz, int cnt)
{
float t=0 ;
int i;
t = 1000/(2.0f*hz); // float
for(i=0; i<cnt; i++)
{
PORTB |= (1 << PB4);
_delay_ms(t);
PORTB &= ~(1 << PB4);
_delay_ms(t);
}
}
문제가 되는 코드와 문제가 없는 코드 간에
위 prototype 외엔 없었나요?
소스를 copy 할 수 있도록 해 주세요.
prototype 에서 인자 없는 형태로 하신다음
즉
void buzzer();
buzzer((int)hz[i],(int)loop[i]);
이렇게 casting을 해 보세요.
1. 만약
void buzzer();
로 한경우
buzzer(a,b);
여기서 a,b가 모두 int 타입의 변수라면 위처럼 사용하셔도 문제가 없겠죠.
왜냐하면 buzzer함수를 호출시에 int 타입의 값 a,b를 전달하고
실제 함수에서도 int 타입으로 받았으니, 형태가 잘못 전달 되지 않습니다.
2.
void buzzer();
로 하고
buzzer(a,b)를 호출하면서
a,b가 int 타입이 아닌 경우(float 같은)
함수를 호출하기 위해 레지스터/스택 을 통해 인자를 전달하는데 호출시에는
float 형태(32비트이며 sign , 8 bit 지수, 23비트 가수)를 전달하고
실제 함수에서는 전달 레지스터/스택 을 int 로 받으면 서로 잘못된 형태로 전달 받게 되겠죠.
3.
void buzzer(int a, int b);
이렇게 prototype을 선언하면
buzzer(a,b); 를 호출하고 a,b 가 float 라고 해도
prototype을 보고 a값을 int 로 변환해서 전달하고, b도 int 로 변환해서 전달하니 문제가 없겠죠.
1.네 문제가 됐던건 prototype 의 차이만 두고 실행했을때 였습니다.
2.기본설정이 오른쪽 마우스 비허용이었네요 죄송합니다,, 지금까지 모르고 질문 계속 올렸습니다 ,,
3.void buzzer(); 선언후 ->buzzer((int)hz[i],(int)loop[i]); 했을경우 정상적으로 작동합니다.
아래쪽 답변1. 에서 실제로 int형 자료로 했을경우에는 문제가 알려주신데로 없었습니다.
답변2. 는 이해하기 조금 어렵습니다 아직 float의 수형에 대해 이해도가 부족해서,,
실제로 알려주신데로 제가 이해한바는 prototype의 수형을보고 함수에 전달될 인자를 함수호출전에
함수에서 사용하는 수형으로 먼저 자동변환 시키거나 메인함수에서 전달함수 호출전에 인자값을 함수에서
사용하는 수형으로 변환 시켜야 원하는 작동을 하고 아무런 언급없이 float형 인수를 쓰고 int 인자형 함수를
호출하면 float 인자가 그대로 전달되어 원하는 작동 값을 얻을수없다 입니다만...
알려주신 답변에서 2. 번에서의 설명을 제가 충분히 이해하기 좀 힘들어서 조금더 보충설명이나 설명자료가
있으신다면 알주시면 대단히 감사하겠습니다.
굳이 avr 컴파일러가 아닌 경우에라도 2번의 경우는 다음과 같은 겁니다.
printf("%d", 5.3);
printf 함수에서 %d 를 보고 넘겨온 인자가 int 타입이라고 생각하고 그렇게 해석했는데
float인 5.3 은 전혀 다른 형식으로 넘겨 옵니다.
따라서 5 가 찍히지 않습니다.
즉 함수를 호출할때 인자를 넘기는데, 넘길때 사용하는 포맷과 받는 곳(함수)가
다른 포맷으로 해석하는 경우입니다.
일반용어로는(20여년전에 사용한)
calling convention 이라는 용어로 설명되는 컴파일러/어셈블리간 약속입니다.

The real value assumed by a given 32 bit binary32 data with a given biased exponent e (the 8 bit unsigned integer) and a 23 bit fraction is
where more precisely we have
.


In this example:
thus:
1. prototype에 함수이 인자 타입이 명시 된 경우 assembly 생성(avr 컴파일러로 avr-gcc -S -Os x.c
#include <stdio.h>
void xx(int );
main()
{
xx(5.3);
}
void xx(int a)
{
printf("%d",a);
}
~
.text
.global xx
.type xx, @function
xx:
/* prologue: function */
/* frame size = 0 */
rcall .
rcall .
ldi r18,lo8(.LC0)
ldi r19,hi8(.LC0)
in r30,__SP_L__
in r31,__SP_H__
std Z+2,r19
std Z+1,r18
std Z+4,r25
std Z+3,r24
call printf
pop __tmp_reg__
pop __tmp_reg__
pop __tmp_reg__
pop __tmp_reg__
/* epilogue start */
ret
.size xx, .-xx
.global main
.type main, @function
main:
/* prologue: function */
/* frame size = 0 */
rcall .
rcall .
ldi r24,lo8(.LC0)
ldi r25,hi8(.LC0)
in r30,__SP_L__
in r31,__SP_H__
std Z+2,r25
std Z+1,r24
ldi r24,lo8(5)
ldi r25,hi8(5) // 해당 값 5.3 을 int 5로 변환해서 함수 인자로 전달
std Z+4,r25
std Z+3,r24
call printf
pop __tmp_reg__
2. 함수 prototype에 인자의 수형을 skip 한 경우
#include <stdio.h>
void xx();
main()
{
xx(5.3);
}
void xx(int a)
{
printf("%d",a);
}
~
__tmp_reg__ = 0
__zero_reg__ = 1
.data
.LC0:
.string "%d"
.text
.global xx
.type xx, @function
xx:
/* prologue: function */
/* frame size = 0 */
rcall .
rcall .
ldi r18,lo8(.LC0)
ldi r19,hi8(.LC0)
in r30,__SP_L__
in r31,__SP_H__
std Z+2,r19
std Z+1,r18
std Z+4,r25
std Z+3,r24 // 32비트 float 타입의 일부 16비트를 이렇게 잘라서 사용하면 5가 될리가 없겠죠?
call printf
pop __tmp_reg__
pop __tmp_reg__
pop __tmp_reg__
pop __tmp_reg__
/* epilogue start */
ret
.size xx, .-xx
.global main
.type main, @function
main:
/* prologue: function */
/* frame size = 0 */
ldi r22,lo8(0x40a9999a)
ldi r23,hi8(0x40a9999a)
ldi r24,hlo8(0x40a9999a)
ldi r25,hhi8(0x40a9999a) //5.3 이라는 float(0x40a999a) 형태 32비트를 r22~r25에 담음
call xx
/* epilogue start */
ret
.size main, .-main
.global __do_copy_data
0x40a9999a (위에 5.3 이 32비트로 변환된 걸 한번 해석해 보면)
sign 0 : +
0x40a9999a
=
0100 0000 1010 1001 1001 1001 1001 1010
1)부호
0100 0000 1010 1001 1001 1001 1001 1010 :
부호 비트 0 은 +
2) 지수부
0100 0000 1010 1001 1001 1001 1001 1010 :
10000001 = 129
129 - 127 = 2
3) 가수부
0100 0000 1010 1001 1001 1001 1001 1010 : 가수부
0 1 0 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 1 0
2 4 8 16 32 64 128 256 2048 4096
=
1 + 1/4 + 1/16 + 1/128 + 1/256 + 1/2048 ....
= 1 + 0.25 + 0.0625 + 0.0078125 + 0.00048828125 + ..
= 1.32080078125 + alpa(이건 작아서 일단 무시)
+ 1.32080078125 * 2^2 = 5.283203125
No comments:
Post a Comment