목표) 어떤 수를 입력받으면 각 자리수를 교체해서 출력하기
예) 72451 입력받으면
15427 출력이 목표임
어떻게 할까 생각하다가 이걸 문자열로 바꾸고
배열을 활용해서 출력하면 자리수가 바뀌겠거니 해서 할려했음
예를들면 int num=72451; 이 있다면
num을 char a라는 배열로 바꾸면
char a[0]=7 , char a[1]=2 , char a[2]=4, char a[3]=5, char a[4]=1 이니까
그대로 출력하면 15427이 되서 원하는 값이 출력이 되야되는데
위 사진처럼 아무런 값도 출력안하고 종료되버리네.. 왜그런걸까??
47개의 댓글
무분별한 사용은 차단될 수 있습니다.
뱀고기
change 함수안의 sum은 함수실행이 끝나면 사라진다
함수안에서 malloc 하고 밖에서 free를 하던지 아니면 main 함수에 배열을 선언하던지
아니면 전역으로 배열 선언하던지
자바잡아
sum이 아니라 sprintf 말하는거야?
sum은 없는데..
뱀고기
아 str임 sum이 아니고
귀여운알파
답답해서 그냥 답을 알려주면
10으로 나눈 나머지=마지막 자릿수임을 이용해서 하면됨
312의 경우
int a = 312
int digit_1 = 312 % 10
int digit_2 = (312 / 10) % 10
int digit_3 = (312 / 100) % 10
자바잡아
a가 3의자리 수라 가정하고
너의 말처럼
while(1)
(if count==3) break;
int i =1;
digit=0;
digit=digit+((a/i)%10)*i
count++;
이렇게 코딩하는 방법도 있는데
내가 하는 방법중에서 뭐가 잘못됐을까 이게 궁금해서..
귀여운알파
change함수의 리턴형이 int로 되어있네 char*가 되어야함
자바잡아
자세히좀 말해줄수 있을까??
귀여운알파
너가 return 하려는게 char형 배열의 주소잖아. 그러면 함수에서 그걸 리턴해준다고 해야지.
int change() 라고 하면 int형을 리턴해줄거다~라는 뜻인데 그래놓고 char* 형을 리턴중이잖아
귀여운알파
저번 질문글에다가도 달았지만 문제풀이보다는 제대로 된 기초 책으로 처음부터 다지는게 더 급해보임
자바잡아
do it c언어라는 책으로 처음 해봤는데
기초를 잡으라해서 다른 문제집 사서 풀고있는데
이것도 아니고 저것도 아니면 어떤 기초책을 사서 처음부터 다져야할까?
책 추천좀해줘 아님 인강을 들어야할까?
귀여운알파
영어 가능하면 k. N. King C programming 추천하고 영어가 부담스러우면 c기초 플러스 추천 할께
알파스트라이크
이부분도 일부러 생각해보라고 미리 말 안해준건데 반환형은 함수타입이랑 같아야 함. 사실 네 경운 int랑 포인터형이 둘다 4바이트라서 우연히 리턴이 되기는 하는데 저렇게 하면 다른 타입 같은경우엔 바로 오류나고 int라도 논리적 오류내기 딱 좋은 상황임.
알파스트라이크
문자열 (즉 char 배열)을 크기 지정 안하고 {0, }으로 선언 및 초기화하면 배열의 사이즈가 어떻게 될지 생각해봐. 그리고 int와 str 변환에 대해서 잘못 이해하고 있는거 같은데 그건 다음 문제고.
자바잡아
이것도 묻고싶은것중에 하나였어
char[ ] = {0,} 인데
[ ] 괄호한에 있는걸 뭔 수로 지정해야될지 모르겠음
어떤사람은 10을 대입할수도있는거고
어떤사람은 10만을 대입할수도있는거고
어떻게 정해야될지 모르겠어..
알파스트라이크
문자열은 미리 크기를 지정하는 경우는 당연히 프로그래머가 상정한 '적당한 크기'임. 네가 무한대로 입력받고 싶은게 아닌 이상 어디까지는 가능하게 해야지 하는 선이 있을거 아니야. 그 크기를 잡으면 됨. 문자나 트위터가 140자 제한 있듯이.
예를 들어 int 값이면 21억 조금 넘게, unsigned int면 그 두 배인 42억 조금 넘게 표현 가능하니 양수만 처리한다는 기준 하에 11자리(마지막에 문자열의 끝인 0이 들어가야하니까. 참고로 저 0은 '0'이 아님.) 설정 한다든지 그건 네가 '잘 결정해야' 하는 사안임.
그게 싫으면 동적할당을 하여 가변적으로 만들든가.
알파스트라이크
그게 아니면 리스트를 구현하는 등 다른 자료구조를 이용하는 방법도 있을 수 있지만 그 단계는 아닌거 같고
자바잡아
그럼 100의 자릿수 이상은 대부분 대입안하니
char a[100] ={0,}이라고 해도 오류가 나는데
어디가 잘못된걸까?
scanf로 숫자를 입력받아서
sprintf 로 char a[100]에 문자열로 변경했고
마지막 return 으로 a[100]의 주소값을 반환해서
return a;
printf("%s",magic(num)) 했는데 뭐가 잘못된거야??
잉텔
C를 하기전에 프로세스 메모리 구조부터 알아야 할 필요가 있어보인다.
str[] 이놈은 magic 함수 스택에 만들어 진놈이고 magic 함수가 끝나면 str은 사라짐
근데 너는 사라진 str메모리 주소를 접근하려니 오류가 뜨는거임
자바잡아
int change(char* ptr) {
char a[100] = { 0, };
ptr = a;
sprintf(a, "%d", *ptr);
int count = 0;
while (1) {
if (*ptr == 0) {
break;
}
*ptr = *ptr / 10;
count++;
}
for (int i = 0; *ptr < count - 1; i++) {
a[i] = a[count - i];
}
return ptr;
}
int main() {
int num;
scanf("%d", &num);
printf("%s", change(&num));
}
이렇게 고쳐봤음 일단 포인터를 이용해서 지역변수 문제를 어떻게 해결해볼려했는데
값을 대입해도 그냥 종료되네
while 을 통해 count를 구하는데
count는 자릿수 +1 이 나오게함
72351은 5자리니 count는 6이나오고 그래서 count -1 함
궁금한게 ptr을 반환해서 printf("%s",ptr); 하는게 잘못된건가??
ptr 자체가 주소값인이니 맞지않을까??
ptr이 주소값이니 int 형을 반환하는거고
알파스트라이크
그건 첫번째 애가 대답해 줬잖아 a는 지역변수라 이미 사라지고 없어. 그거 말고도 문제가 남아있으니 전체적으로 좀 더 차분히 생각해봐.
한가지 미리 말해주자면 이부분을 포함해서 문법오류를 모두 고쳐도 니가 원하는 역순 출력이 아니라 정상순서로 출력될거임.
자바잡아
int change(char* ptr) {
char a[100] = { 0, };
ptr = a;
sprintf(a, "%d", *ptr);
int count = 0;
while (1) {
if (*ptr == 0) {
break;
}
*ptr = *ptr / 10;
count++;
}
for (int i = 0; *ptr < count - 1; i++) {
a[i] = a[count - i];
}
return ptr;
}
int main() {
int num;
scanf("%d", &num);
printf("%s", change(&num));
}
이렇게 고쳐봤음 일단 포인터를 이용해서 지역변수 문제를 어떻게 해결해볼려했는데
값을 대입해도 그냥 종료되네
while 을 통해 count를 구하는데
count는 자릿수 +1 이 나오게함
72351은 5자리니 count는 6이나오고 그래서 count -1 함
궁금한게 ptr을 반환해서 printf("%s",ptr); 하는게 잘못된건가??
ptr 자체가 주소값인이니 맞지않을까?
ptr이 주소값이니 int 형을 반환하는거고
잉텔
일단 sprintf 쓰는 방법부터 잘못됨
포인터를 좀 공부해야할듯
포인터는 그냥 주소값이지 사람에게 있어서 의미있는 정보가 아님
*ptr이렇게 한 의도는 ptr의 문자를 int로 바꾸고 싶어하는것 깉은데 일단 이렇게 쓰는것도 아니고
애초에 int랑 char[]랑 완전 다른데... *ptr을 하는것부터 이해가 안된다.
참고로 C언어 팁이라면 쉽게하려고 사람이 생각하는대로 하려고 하면 안됨
그냥 무작정 안되면 안되는걸 깨닫고 되게 만들어야됨
자바잡아
문제가 숫자를 입력하면 각 자리수를 바꿔서 표현이 문제임
예) 13579 입력하면 97531 출력되도록
그래서 scanf로 13579를 입력받고
change라는 함수를 따로 만듬
그리고 내 생각은
"각 자리수를 변경하는 방법이 여러가지가 있겠지만 배열을 이용해서 바꿔보자
13579는 정수(숫자)니 이걸 문자열로 바꾼뒤
문자열의 값들을 변경 하는게 어떨까?"
예) char a [5] 가 있으면
char[0]=1 , char[1]=3, char[2]=5, char[3]=7, char[4]=9 이걸
char [0]=9, char[1]=7, char[2]=5, char[3]=3, char[4]=1 로 바꾸는거지
그래서 sprintf로 정수인 num을 배열 a로 바꿀려고 했던건데
잘못 접근한걸까?
잉텔
ㅇㅇ change에서 부터 틀렸음
change 함수내에서는 작동되겠지만 change 함수 스택내에 있던 메모리의 포인터를 반환하는게 잘못됨
아까전부터 말하지만 change 함수에서 str주소를 아무리 반환한다 한들 change 함수가 끝나면 str의 주소는 의미 없음
왜냐면 str이 가르키는 메모리공간은 사라졌거든
메모리가 언제 사라지고 언제 생기고 Life cycle부터 공부해야될듯
http://tcpschool.com/c/c_memory_stackframe
자바잡아
해결했어 답변 고마워
라이프 사이클 이해는 했는데
익숙하지 않아서 그런거같네
그리고 안되는건 안되는거고
되게 만들어야된다는 말 뜻이
이제 감이 오네 ㅋㅋ
알파스트라이크
늦게봐서 방금 왔는데 답변이 달려 있네.
일단 지금 댓글에 단 코드 특히 change 함수는 작동이 말그대로 전혀 제대로 동작하지 못하는 코드임.
한단계씩 살펴보자.
1. int change(char* ptr) 함수 선언을 했고 반환형은 int 매개변수는 char* ptr임. char형 포인터를 매개변수로 받아서 int형 결과를 밖에다 내 놓아야겠지? 이부분 기억해 두고
2. char a[100] = {0, }; 크기 100짜리 char 배열을 선언하고 0으로 초기화했어. 그리고 나서 넌 ptr = a;로 ptr가 a를 가리키게 만들었지.
그리고 나서 sprintf(a, "%d", *ptr);을 했어. 뭔 의미가 있을지 생각해봐. ptr은 a를 가리키고 있고 a는 0(null값)이 들어가 있는 문자열이야.
a에 (a를 가리키고 있는)ptr이 가리키고 있는 곳의 첫번째 원소의 값(이게 왜 이렇게 되는지는 내가 예전에 설명한적 있을거야)을 %d(즉 10진수 int형)으로 배열화 해서 쓰세요 라고 한거. ptr은 a를 가리키고 있고 그 첫번째 값은 뭐지? 0이겠지?
이미 네 의도랑은 전혀 다르게 흘러가고 있지?
3. while(1) { if (*ptr ==0) {break;} *ptr = *ptr/10; count++;}
*ptr은 2번에서 말했듯이 현재 a 첫번째 원소야 0이지? 바로 빠져나오게 돼.
4. for문 역시 for문 루프의 조건이 0 < 0-1 가 되니 전혀 하는 것 없이 빠져나옴.
3, 4번의 알고리즘도 심각한 문제가 더 있지만 일단 흐름만 보자.
5. return ptr 이미 위에서 다 말했듯이 return형을 지켜야돼. 1번에서 말했듯이 반환형은 int인데 char*를 반환하고 있어
그리고 여기서 ptr은 a를 가리키고 있고 a는 여기를 지나면서 '사라짐' 즉 ptr이 가리키고 있는 대상은 이시점을 수행하는 순간 사라져서 허공을 가리키고 있는 상태가 됨.
6. main을 보자. scanf로 num에 숫자를 입력받겠지? change(&num)으로 num의 주소를 넘겨주겠지? 문제는 num은 int야 change는 1번에서 말했듯이 매개변수로 char*를 받아야 돼. 주소값을 넘겨준거니 4바이트로 동일하지만 이미 형식을 무너뜨린거야.
그래도 넘어가긴 하겠지만 이후 나오는 결과는 허공의 주소값을 값으로가진 num 변수와 같은 값을 가진 임의변수임. 당연히 원하는 결과가 안나올 수 밖에 없어.
알고리즘은 아직 지적을 안했는데 왜냐면 형식문제가 너무 많이 나온상태라 그래.
자바잡아
다시 생각하고 정리해서 물어볼게
알파스트라이크
ㅇㅇ 변수의 범위(수명주기)와 실제 무슨 값을 가리키고 있을지 같은 것들 중심으로 곰곰히 잘 생각해봐
자바잡아
다르게 수정하고 코딩해서 해결함 원하는값나왔어
3. 4 번에 관련하여 알고리즘 문제 해결했어
6번의 메모리의 Life cycle 도 이해했음 이해는 됐는데 막상 적용할때마다 잘 안되네 많이 익숙해져야할듯
문법관련해서 이해가 안되서 질문할게
1. int change(char* ptr) 함수 선언을 했고 반환형은 int 매개변수는 char* ptr임. char형 포인터를 매개변수로 받아서 int형 결과를 밖에다 내 놓아야겠지?
5. return ptr 이미 위에서 다 말했듯이 return형을 지켜야돼. 1번에서 말했듯이 반환형은 int인데 char*를 반환하고 있어
그리고 여기서 ptr은 a를 가리키고 있고 a는 여기를 지나면서 '사라짐' 즉 ptr이 가리키고 있는 대상은 이시점을 수행하는 순간 사라져서 허공을 가리키고 있는 상태가 됨.
이렇게 말했는데
궁금한점이 함수를 int형으로 선언했으니 return 값도 int형이여야된다 이 이야긴데
주소 자체도 int값이 아닌가
예를들면 int a=1; int b=&a; print("%p",b); 하면
32비트니 8자리 숫자로 0AF01231 이렇게 나오고
주소값 자체는 숫자와 문자로 되어있으니 결국 int 형으로 반환되는거 아닌가??
--------------------------------------------------------------------------------------------
알파스트라이크
원하는 값까지 내 놓는거에 성공했다니 축하해 그런식으로 한단계씩 나아가면 됨.
int는 명확하게 정해진 하나의 타입임. 자료형 char*는 char의 포인터형인거고 애초에 컴퓨터는 메모리 상에서 2진수의 집합으로 저장을 하는 거고 그걸 유의미한 데이터로 분류를 한 거니 주소값도 int가 아니야? 라고 묻는다면 답은 '아니야'
int는 부호 있는 정수 자료형이고 그 값을 1워드의 크기로 보장하고 있는 형태의 자료형이야.
포인터는 메모리 위치를 표시하기 위해 비트주소를 표현한 값이고.
둘 다 1워드 크기라는 것은 동일하지만 전혀 다른거지. 저장은 비트단위로 되고 크기가 32비트로 같으니 저장된 비트를 특정한 포맷으로 표현을 하니 같은 수치로 환산이 되는거지 둘은 자료형이 엄연히 달라.
둘의 공통점은 4바이트(32비트)란 점 빼곤 같은게 하나도 없어.
%p, %d 같은 포맷 지정자는 단순히 비트를 해당 형식으로 변환해서 '보여준다'는 것 밖에 없어(scanf에서는 반대로 해당 입력이 해당 형식으로 생각해서 비트단위로 변환하란 의미고) 즉 저 포맷 지정자는 float같은데서 써도 여전히 오류메시지 안띄우고 묵묵히 역할을 수행해. 의도한 값과 다른 값이 '보일' 뿐이지.
어차피 메모리에 들어가 있는건 비트일 뿐이거든. 그걸 유의미하게 형식와 크기와 연산형태에 따라 나누어 관리하기 위해 종류를 나누는거지. 그래서 int와 포인터는 똑같이 4바이트 크기의 비트 정보라도 담고 있는 의미는 서로 다를 수 밖에 없어.
출력할 때 %p를 붙이는 건 단순히 그 비트를 포인터 표현형으로 보여주란거지 대상을 알아서 구분하는게 아니야.
자바잡아
즉 포인터와 int값은 서로 다른 유형의 값이지만
그 값을 표현하는데 있어서
int라는 표현형식이 똑같을 뿐이지
실제로는 다른 유형의 값이고
단지 햇갈렸던 이유는
표현 방식이 같아
포인터와 int형 값의 유형이 같다고
착각한거지?
그리고 궁금한게 내가한 코딩처럼
int 로 함수선언했는데
return 은 주소값이면 어떻게 되지?
오류나거나 이상한 값이 반환되나?
알파스트라이크
표현방식도 엄밀히는 다르지. 포인터주소는 정수나 실수 같은 게 아니기 때문에 int란 표현 자체가 틀렸어.
그냥 16진법으로 00 00 00 0A에 해당하는 비트(비트 전체를 풀면 0000 0000 0000 0000 0000 0000 0000 1010)가 int면 32비트 정수니까 10진법으로 10에 해당하는 값(2진법으론 1010, 16진법으론 A, 8진법으론 12)이 되는거고
그 비트가 포인터면 메모리상에서 앞에서 11번째 자리(0에서부터 세면 10은 11번째니까) 위치라는 것일 뿐임.
후자는 int나 정수가 아니야 이 경우 둘이 같은건 저장되있는 공간의 크기가 같고 비트 값이 0000 0000 0000 0000 0000 0000 0000 1010로 같다 딱 그거 뿐임. 전자는 int(32비트 부호 있는 정수) 후자는 그냥 주소값임. %p를 붙였을 때 보이는 0000000A도 사실 그걸 보기 좋게 16비트로 표현한 것 뿐이고
자바잡아
아.. 와 ..
진짜 특수한 경우 아니면
리턴 값의 형태와
함수의 데이터형태를 일치 시켜야겠네
몰랐던 내용인데 답변 고마워
알파스트라이크
말했듯이 둘이 크기가 같기 때문에 경고메시지는 띄우지만 '반환은 돼' 함수를 int를 썼는데 포인터를 리턴했으니 주소값을 int 변수에다 넣어서 반환되는 형태로.
예를 들면
int a = 10;
int sample(char* test)
{
test = &a; <- 실제로는 여기도 char*인 포인터에 int의 주소를 넣는거니 이렇게 하면 안되지만 역시 경고를 무시하고 사용
return test;
}
라고 하자.
만약 a의 변수의 주소값이 000000FF라 한다면
char mystr[10] = "new string";
int b = sample(mystr);
한다면 a의 주소값인 000000FF에 해당하는 값 255가 b에 대입되겠지.
자바잡아
원래는 b에 10을 대입할라고 코딩을 했지만
주소값을 int형으로 반환되버려서
b에 255가 대입되서
의도하지 않는 값이 나온다는거네
이거 맞지?
알파스트라이크
ㅇㅇ 그런 셈이지 사실 의도야 일부러 '주소 값을' 저기 넣기 위해서 할 수도 있긴 한데 바람직한 상황이 아니지
저게 달랑 몇줄이니 보면 혼동없이 이게 이렇게 되겠군 바로 예상이 되는거지 코드가 수만줄이 넘어가는데 반환타입 틀려버리거나 포인터의 주소, 간접 참조값, 실제 값 이런 걸 혼동해버리면 바로 메모리 오염발생해서 뻗어버리는거지. 그런 걸 방지하기 위해서라도 반환타입은 지켜주는게 좋음. 저게 통과된건 순전히 크기가 4바이트로 같아서 통과된 것 뿐이니까.
자바잡아
답변 고마워
알파스트라이크
ㅇㅇ 이번에 스스로 알고리즘 부분에서 수정후 원하는 결과를 얻어내기까지 했으니 한단계 발전한거야 열심히해.
한가지 팁을 또 주자면 프로그래밍 중간중간 변수가 이부분에서 어떤 값인가 잘 생각해보고 디버깅모드에서 변수를 추적하거나 아니면 실행시 원하는 위치에서 원하는 변수값이 별도로 출력되도록 해서 그 위치에서 니가 예상한 값이랑 일치하는지 체크해보는 것도 상당히 도움이 돼.
돈없는공대생
자바잡아
1번이 이해가안됨
저 사진에서 보면
int change (){
char str[100]={0,};
...
return str;
}
로 끝났는데 str 자체가 주소값이니
return 은 주소값을 반환하는거 아니야??
돈없는공대생
자바잡아
int change(char* ptr) {
char a[100] = { 0, };
ptr = a;
sprintf(a, "%d", *ptr);
int count = 0;
while (1) {
if (*ptr == 0) {
break;
}
*ptr = *ptr / 10;
count++;
}
for (int i = 0; *ptr < count - 1; i++) {
a[i] = a[count - i];
}
return ptr;
}
int main() {
int num;
scanf("%d", &num);
printf("%s", change(&num));
}
여기 보면 주소값을 다시 포인터로 반환하지않았어?
char*ptr 은 num의 주소로 할당받아서
그리고 주소값은 숫자나 문자로 이루어진건데
char형이랑 int형의 관계가 있음??
잉텔
change함수가 호출되면서 change의 스택프레임을 만드는데 char str[100]을 하면서 이 공간도 같이 만듦
근데 문제는 change함수가 끝나고 ret을 하게되면 그 공간 자체가 사라진다고
쉽게 설명하자면 집주소만 주고 집 철거해버리면 주소를 아무리 찾아간들 없을텐데
자바잡아
저 안에서 생성된 함수니까 사라진다 이건 이해가되는데
그래서 포인터를 활용해서 지역변수의 한계를 없앨려고 하거든
포인터는 메모리 접근방식이니 지역변수의 한계에서 벗어나잖아
근데 내가 뭘잘못하고있는지 이게 이해가안돼
잉텔
포인터는 메모리 접근방식이니 지역변수의 한계에서 벗어나잖아 -> 이거부터 틀렸음
배열 str 자체가 이미 change 함수가 끝나면서 메모리에서 사라졌는데?
주소가 유효하던 안하던 포인터는 주소를 담는 그릇일 뿐이고
알파스트라이크
포인터를 사용해도 그 포인터가 지역변수를 가리키게 만들면 그 지역변수가 사라지는 순간 허공을 가리키는 게 되어버려.
포인터냐 아니냐 문제가 아니라 사용하려고 하는 놈이 살아있는가를 봐야해.
뱀고기
> 포인터는 메모리 접근방식이니 지역변수의 한계에서 벗어나잖아
이게 틀림. 포인터는 주소이지만 그 주소에 해당하는 메모리 공간은 함수 실행이 끝나면 없어짐
자바를 알고 있나본데 자바의 new랑 대응하는 건 C에선 malloc이다. 자바에서 객체 선언과 생성은 엄격하게 구분되지만 C는 그렇지 않아