Now Loading ...
-
[C언어의 기본]포인터+함수
함수의 인자로 배열 정리
int num;
fct (num);
// => fct함수에 num을 전달한다. ? => no. 복사한다에 가까움
//ㄴ 값이 복사가 되는 것.
int arr[3];
fct (arr[3]);
//=> 통째로 매개변수에 복사해서 전달하는 것인가?
//ㄴ 값이 복사가 되는 것.
//=> 통째로 매개변수를 복사해서 넣는 방법은 없음.
int arr[3];
fct (arr); //복사해서 매개변수에 넣어볼게
void fct(int arr[3]); //배열형태로 받을 수 있나?
//but C언어는 매개변수의 배열선언을 허용하지 않는다.
fct(num)은 int num의 값이 바뀌는 것이 아닌 값이 카피되어 나오는 것이다.
즉, 원래 값의 변화가 아닌 복사 값의 변화다.
두번째를 보면
int arr[3] 선언 후 fct(arr);을 통해서 int arr[3]에는 접근이 불가능하다.
이는 컴퓨터에서 계획.txt파일을 복사 붙여넣기 한 후,
계획.txt(복사본)을 수정하는 것이기 때문에 원래 있던 계획.txt파일은 이상없다.
그러면 void fct(int arr[3]);은 되는가?
C언어는 매개변수의 배열선언을 허용하지 않기 때문에 불가능하다.
int main
{
int age=10;
fct (age); //함수 호출이 끝난 후 age 값이 +1 증가했으면 좋겠는데 가능? 불가능?
// 불가능하다.
//fct에 age를 넣는것이 불가능. 복사가 값이지 원래 age값의 조작은 불가능
//fct는 복사본1로 노는 것이고 age파일은 무해함.
}
int age;
scanf("%d", &age); //접근가능 why? 주소값 이니까.
int age =20;
fct (&age);
void fct(int *ptr) //주소값을 가리키는 포인터
{
*ptr+=1;
}
//이러면 age값은 증가한다.
이 구문을 보면 알수있는 것은 위와 같이 복사본을 통해서 age값을 변할 수 없다는 것
하지만 scanf를 통한 코드를 보면 가능하다. 왜냐하면 & 주소값이기 떄문에.
그리고 배열선언이 아닌 void fct(int *ptr)로 선언하고 *ptr+=1을 하면
이는 주소값의 영역이기 때문에 age값은 증가한다.
인자전달의 기본방식
인자전달의 기본방식은 값의 복사다.
int SimpleFunc(int num) {...}
int main(void)
{
int age=17;
SimpleFunc(age);
}
//age에 저장된 값이 매개변수 num에 복사가 됨. 17(복사본)
상단에 SimpleFunc을 선언 했다고 하고
main함수내에 int age선언후 이후 SimpleFunc를 통해서
main앞에서 int SimpleFun의 int num로 들어가는 순간
이때는 값의 복사를 통해서 들어간다는 것이다.
배열을 함수로 인자를 전달하는 방식
int arr[3]={1,2,3};
int * ptr = arr; //배열 arr은 int형 포인터다.
void SimpleFunc(int * param) {...}
//포인터 변수를 통해서 배열의 형태로 접근했음.
위처럼 배열을 선언 후
이에 대해서 배열 arr을 int형 포인터로 지정한다.
그 후 포인터 변수를 통해서 배열의 형태로 접근시킬 수 있다.
즉 , SimpleFunc의 매개변수는 int *param이 되어야한다.
void ShowArayElem(int * param, int len) //계산 함수 len 배열의 길이
{
int i;
for(i=0; i<len; i++)
printf("%d ", param[i]); //출력을 배열형태로 진행
printf("\n");
}
int main(void)
{
int arr1[3]={1, 2, 3};
int arr2[5]={4, 5, 6, 7, 8};
ShowArayElem(arr1, sizeof(arr1)/sizeof(int)); //arr1,4*3/4
ShowArayElem(arr2, sizeof(arr2)/sizeof(int)); //arr2,4*5/4
//arr1 과 arr2는 길이가 다르지만 ShowArayElem 함수를 사용
return 0;
}
// 123
// 45678
void ShowArayElem(int * param, int len)은 두번째 인자로 배열의 길이정보를 전달받도록 정의하였다.
printf("%d ", param[i]); 은 포인터 변수의 이름을 대상으로 배열형태의 접근을 진행하고 있다.
ShowArayElem(arr1, sizeof(arr1)/sizeof(int));
ShowArayElem(arr2, sizeof(arr2)/sizeof(int));
이 두가지는 길이가 다른 두 배열을 대상으로 ShowArayElem함수를 호출한다.
arr1은 Int *param으로 포인터 값으로 전달, int len은 4x3/4로 = 개수 만큼 출력
arr2는 int *param으로 포인터 값으로 전달, int len은 4x5/4로 = 개수 만큼 출력
값을 출력해주게 되는 것이다.
void ShowArayElem(int * param, int len)
{
int i;
for(i=0; i<len; i++)
printf("%d ", param[i]);
printf("\n");
}
void AddArayElem(int * param, int len, int add) //param에 arr[n]이 들어가는 것, ex) arr[n],3,1
{
int i;
for(i=0; i<len; i++)
param[i] += add; //1씩 증가
}
int main(void)
{
int arr[3]={1, 2, 3};
AddArayElem(arr, sizeof(arr)/sizeof(int), 1); // 배열의 전체길이 / int의 길이 = 배열길이
//주소값 전달, 배열길이, 1 전달하는 것
ShowArayElem(arr, sizeof(arr)/sizeof(int)); //len이 3이 되서 올라감
//param [0][1][2]가 234
AddArayElem(arr, sizeof(arr)/sizeof(int), 2);
ShowArayElem(arr, sizeof(arr)/sizeof(int));
AddArayElem(arr, sizeof(arr)/sizeof(int), 3);
ShowArayElem(arr, sizeof(arr)/sizeof(int));
return 0;
}
이것은 위와 비슷한 구문으로, ShowArayElem과 AddArayElem함수 선언
그리고 메인함수 선언이다.
int *param에는 arr[n]값이 들어가게 된다.
그리고 각 len은 sizeof(arr)/sizeof(type)이다.
각 값을 함수에 넣으면
2 3 4
4 5 6
7 8 9
가 출력된다.
AddArayElem(arr, sizeof(arr)/sizeof(int), 1) 가 진행되면
(주소값, 배열길이, 추가값)이 들어가는데, arr값이 순번대로 +1이 출력되면서 2,3,4의 출력이
AddArayElem(arr, sizeof(arr)/sizeof(int), 2)가 진행되면
기존 2,3,4출력에 +2로 출력된다.
이렇게 되는 코드다.
처음에 있듯
int * ea = int ea[]이다.
그러면 위에
void AddArayElem(int * param, int len, int add)
void ShowArayElem(int * param, int len)
//이 예제를 대신하여
void AddArayElem(int param[], int len, int add)
void ShowArayElem(int param[], int len)
//도 가능하다.
포인터를 배열의 이름으로 사용하는 것이 가능하듯!
int arr[3]={1,2,3};
int * ptr =arr;
그러면 이것은 될까?
int ptr[]=arr로 대체불가능하다.
왜냐하면 이것은 매개변수가 아니기 때문이다.
Call-by-Value vs Call-by-reference
int num=10;
fct (num);
fct(int *ptr)
{
*ptr+=1;
}
위에서 배웠듯 위 같은 코드가 있다면 num값은 변하는가?
맞다. 왜냐면 주소값 *ptr를 썻기 떄문이다.
여기서 Call-by-value는 말그래도 값에 관한것이다. fct(10);
Call-by-reference는 참조에 관한것으로 &num, *ptr과 같은 주소 선언이다.
즉 우리가 지금까지 선언한 것의 대부분은 Value이다.
Reference 예시
void ShowArayElem(int * param, int len) //주소값
{
int i;
for(i=0; i<len; i++)
printf("%d","parm[i]);
printf("\n");
int *param을 통해서 값의 변경시 원래 값도 변경되는 점을 알 수 있다.
void Swap(int n1, int n2) //swap함수를 통해서 n1과 n2를 변경하기를 원함
{
int temp=n1; //임시 저장소 temp
n1=n2;
n2=temp;
printf("n1 n2: %d %d \n", n1, n2);
//temp <- n1 <- n2 <- temp
}
int main(void)
{
int num1=10;
int num2=20;
printf("num1 num2: %d %d \n", num1, num2);
Swap(num1, num2); // num1과 num2에 저장된 값이 서로 바뀌길 기대!
printf("num1 num2: %d %d \n", num1, num2);
return 0;
}
이러한 경우는 어떨까?
최종 결과값은 num1=20 , num2=10으로 나오지만 값 자체는 바뀌지 않는다.
왜냐하면 Call-by-Value기 때문에 값의 복사가 일어나서
원래의 값은 영향을 주지 않기 때문이다.
주소값을 전달하는 호출 Reference
void Swap(int * ptr1, int * ptr2) //위와 다르게 포인터를 사용 (이는 주소를 향해있음)
{
int temp = *ptr1;
*ptr1 = *ptr2;
*ptr2 = temp;
}
int main(void)
{
int num1=10;
int num2=20;
printf("num1 num2: %d %d \n", num1, num2);
Swap(&num1, &num2); // num1과 num2에 저장된 값이 서로 바뀌길 기대!
printf("num1 num2: %d %d \n", num1, num2);
return 0;
}
위와 같은 예시는 ptr에 포인터를 통해서 주소값을 전달하기 떄문에
main함수의 값도 바뀌게 된다.
과거 우리가 scanf함수에 &붙히는 이유를 알 수 있다.
int main(void)
{
int num;
scanf("%d",&num); //변수 num의 주소값을 scanf함수에 전달
//scanf의 할당받은 값을 전해주기 위해서 복사가 아니라 커넥트
}
그러면 str과 같은 문자열을 받을 때는 왜 주소값을 사용하지 않는가?
int main(void)
{
char str[30];
scanf("%s",str); //&str는 잘못됨
}
반복했다싶이, 문자열 그 자체가 주소값이기 때문에 주소 주소값이 되어버려서 불가능하다.
문제풀이
문제1: 변수 num에 저장된 값의 제곱을 제공하는 함수정의
이를 호출하는 main함수를 작성해보자.
두가지 형태 valuve, reference로 한다.
//Call-by-value
int squared_value(int num)
{
return num * num; //제곱
}
//Call-by-referencec
void squared_reference(int* ptr)
{
int num = *ptr;
*ptr = num * num;
}
int main(void)
{
int num = 2;
printf("%d \n", squared_value(num));
squared_reference(&num);
printf("%d\n", num);
//두줄로 나누어서 한다면 void형 함수인 reference부분이 값반환은 없으나
//num변수 값을 변경하는 효과만 나오게 됨.
//printf("%d\n", squared_reference(&num)); //불가능
//void형 이기 때문에 호출 결과를 얻는 값이 없음.
//컴퓨터에게 "아무것도 없는 것"을 출력해
return 0;
}
간단한 주석은 넣었으나 추가적으로 설명하면 일단 Value은 자체값이라서 주소값이 없이 제작하고
Reference는 주소값이 있게 제작한다.
그후 main 함수에 함수를 이용한 출력을 넣어준다.
만약 printf("%d\n", squared_reference(&num));은 가능할까?
불가능하다. why? void형이기 때문에 호출 결과를 얻는 값이 없다.
즉 컴퓨터에게 아무것도 아닌 것을 출력해.라고 하는거다
문제2:세 변수 저장된 값이 서로 바뀌는 함수를 정의하자.
함수는 swap3 swap3(&num1,&num2,&num3); 느낌이다.
num1은 2에 2는 3에게 3은 1에게
void swap(int* ptr1, int* ptr2, int* ptr3)
{
int temp = *ptr3;
*ptr3 = *ptr2;
*ptr2 = *ptr1;
*ptr1 = temp;
}
int main(void)
{
int num1 = 1, num2 = 2, num3 = 3;
swap(&num1, &num2, &num3);
printf("%d %d %d\n", num1, num2, num3);
return 0;
}
*/
서로 바뀌는 함수 swap을 통해서 꼬리물기처럼 한바퀴 돌아준다.
그리고 swap에는 주소값으로 묶어야되기에 맞춰준다.
그리고 출력하면 서로바뀌게 된다.
포인터 대상의 상수(const)선언
Const + int * ptr
ptr -> num을 가리킴
//num이 상수화 되는 것인가?
//ptr이 상수화 되는 것인가?
이럴 때 num이 상수화인가? ptr이 상수화인가?
포인터변수가 참조대상 변경허락하지 않는 상수화
int main(void)
const int * ptr = #
*ptr =30; //컴파일 실패
num=40; //컴파일 성공
포인터 변수 ptr을 이용하여 ptr이 가리키는 변수에 저장된 값의 변경을 허용하지 않음.
즉, Const선언은 값을 변경하는 방법에 제한을 두는 것으로서 상수로 만드는 것은 아니다.
포인터 변수의 상수화
int*const ptr = #
//이러면 ptr이 상수가 된다.
int main(void)
{
int num1=20;
int num2=30;
int * const ptr=&num1;
ptr=&num2; //컴파일 에러
*ptr=40; //컴파일 성공
ptr이 상수고 ptr이 가리키는 대상 값은 상수가 아니라서 변경이 가능하다.
내 생각은 Const ( ) *ptr이면 const 뒤에 *까지 있으니 *ptr 불가능
* Const ptr이면 const뒤에 ptr만 있으니 ptr 불가능
즉 가리키는 값의 불변이냐, 포인터 자체의 불변이냐
만약 두개가 붙는다면?
const int * const ptr = #
내 추측이 맞다면 이런경우 *ptr과 ptr둘다 값의 불변이 일어난다.
*ptr=20; 시도시 에러가 나올 것이고 ptr=&age여도 에러가 나올 것이다.
가리키는 값의 변경이 불가능하거 ptr자체도 다른주소를 가리킬 수 없기 떄문에
그러면 const선언은 어떤 의미가?
const 선언은 생각보다 중요한데, 코드의 안전성을 높혀주는 것에 있다.
int main(void)
{
double PI=3.1415;
double rad;
PI=3.07; //실수의 문장이 삽입되면?
scanf("%lf", &rad); //double형이라 lf
printf("circle area %f \n", rad*rad*PI);
}
위와 같은 경우 실행이 된다. 하지만 문제가 발생한다.
PI의 값이 변경되지만 컴파일러는 이상이 없다고 판단하는거다.
int main(void)
{
const double PI=3.1415;
double rad;
PI=3.07; //컴파일 시 상수 값의 변동으로 오류 발생
scanf("%lf", &rad); //double형이라 lf
printf("circle area %f \n", rad*rad*PI);
}
Const선언이 된 경우 doulbe이 상수화되어 추후 PI값이 들어와도 오류가 발생한다.
즉 코드에 대한 안전성이 올라가는 것이다.
문제풀이 Const선언 이해
문제1: 아래 정의된 함수를 참고하여
인자로 전달되는 정보를 참조하여 int형 배열요소 전체를 출력하는 함수이다.
매개변수 arr을 대상으로 const 선언을 한 이유는?
void ShowAllData(const int * arr, int len) // const *arr 고정주소 , len 길이
{
int i; //int i지정
for(i=0; i<len; i++) //조건문 i 0부터 시작해서 i가 len보다 작을때 반복, i 1씩 증가한다)
printf("%d", arr[i]); //arr[0] ~ 배열길이까지
}
이 코드를 보면 출력 arr[i]는 전체배열을 출력하는 함수다.
Const를 앞에 붙히는 이유는 *arr을 통해서 주소값 자체를 변하지 않게 하는 것이다.
배열 요소 값의 변동이 있는 경우를 제한하여 코드 변동을 확인할 수 있게 된다.
문제2: 예제를 보면 한 가지 지적할 사항이 있는데 그것이 무엇인가?
void ShowData(const int* ptr) // *ptr에 const선언이 붙음 ptr이 가리키는 변수 저장된 값 변경 x
{
int* rptr = ptr;
printf("&d\n", *rptr);
*rptr = 20;
}
int main(void)
{
int num = 10;
int* ptr = #
ShowData(ptr);
return 0;
}
*rptr을 이용해서 ptr을 가리키는 변수값을 바꾸려고 하지만
ShowData함수에서 const int * ptr의 선언으로 값을 바꿀 수가 없다.
정리
const int* ptr = ptr이 가리키는 값이 상수다.
즉 ptr을 통해서 가리키는 메모리 영역에 저장된 값은 변경이 불가능하다.
ptr 자체는 변경이 가능하다.
int * const ptr = ptr 자체가 상수다.
ptr이 가리키는 메모리 영역은 변경이 불가능하나 가리키는 값은 가능하다.
포인터관련은 끝이고 다음은 문제풀이로 만나도록 하겠다!
-
[C언어의 기본]포인터+배열
포인터+배열
포인터과 배열에는 관계가 있다
int* ptr;
int num;
ptr = #
이 구문을 보면 ptr은 num을 가리키고 있다.
int arr[3];
//ptr => arr[0]을 가리키고 있는 것이다.
가리킨다는 점에서 두개는 전부다 같다.
*ptr *arr은 가능하다. 변수와 상수의 차이점이라는 것이 존재할뿐.
즉 배열의 이름은 (상수형태)포인터를 말하는 것이다.
int main(void)
{
int arr[3]={0, 1, 2};
printf("배열의 이름: %p \n", arr);
printf("첫 번째 요소: %p \n", &arr[0]);
printf("두 번째 요소: %p \n", &arr[1]);
printf("세 번째 요소: %p \n", &arr[2]);
return 0;
// arr=&arr[i]; //이 문장을 넣으면 컴파일 에러가 나옴: 상수라서 오류
}
이 예시를 보면 arr[3]라는 배열 0,1,2를 초기화 해준 후
각 출력값을 뽑아내면 배열의이름: 00122FF50
첫번째: 00122FF50 , 두번째:0012FF54…이렇게 나오는데
이 것을 보면 배열의 이름은 첫번째 요소의 주소값과 동일하다.
arr은 상수기 때문에 대입연산이 불가능하다. 가리키는 대상을 바꿀수 없다는 것이다.
비교
포인터변수
배열의이름
이름존재
존재함
존재함
무엇을 나타내고 저장
메모리의 주소 값
메모리의 주소 값
주소값의 변경
가능하다
불가능하다.
배열요소간 주소 값의 크기는 4byte로 요소가 서로 붙어있다는 것을 알 수 있다.
double arr[1]=> double *상수다.
1차원 배열의 이름의 포인터형 배열이름 대상 *연산
int arr1[5]; //arr1은 int형 포인터 실수
double arr2[7]; //arr2는 double형 포인터 상수
즉, 1차원 배열이름의 포인터형은 배열의 이름이 가리키는 대상을 기준으로 결정된다.
int main(void)
{
int arr1[3]={1, 2, 3};
double arr2[3]={1.1, 2.2, 3.3};
printf("%d %g \n", *arr1, *arr2); //int %d / double %g
*arr1 += 100; //4byte
*arr2 += 120.5; //8byte
printf("%d %g \n", arr1[0], arr2[0]);
return 0;
}
포인터를 통해서 arr1의 첫번째 1+100을 해서 = 101
포인터를 통해서 arr2의 첫번째 1.1 + 120.5을 해서 = 120.6
+)추가로 생각해보기
int main(void)
{
int arr[3] = { 1,2,3 };
arr[0] += 5;
arr[1] += 7;
arr[2] += 9; //포인터를 대상으로 구성한 셈
...
}
이렇게 하면 될까?
된다. arr은 int형 포인터 변수나 배열의 모든요소를 접근한 것으로
실제로 이렇게 쓸 수 있다. 포인터 변수 ptr을 대상으로 ptr[0], ptr[1]이 가능하다.
즉, 포인터를 배열의 이름처럼 사용할 수도 있다.
int main(void)
{
int arr[3]={15, 25, 35};
int * ptr=&arr[0]; // int * ptr=arr; 과 동일한 문장 //배열 접근가능
printf("%d %d \n", ptr[0], arr[0]); //ptr[0]...도 가능하다.
printf("%d %d \n", ptr[1], arr[1]);
printf("%d %d \n", ptr[2], arr[2]);
printf("%d %d \n", *ptr, *arr);
return 0;
}
이 문장을 보면 포인터 수 변수를 배열의 이름처럼 사용하는 경우는 없지만 가능은 하다..!
포인터에 대한 정확한 지식을 넣고 가자.
포인터 연산
포인터 => *연산과 []연산이 있음.
위에 포인터가 *와 []이 있듯이!
int* P; = 0x01=[1]
P=+1
->0x01+4=0x05
포인터 에서는 0x01을 가리키는 곳에서 +1을 하면 0x02가 아니라
0x05가 된다.
왜? int형이니까 +4byte라서
int arr[3];
int*ptr=&arr; //둘다 타입이 일치되서 대입이 가능함. ptr은 변수고 arr는 상수니까 대입가능.
위 int *ptr = &arr을 거치면
arr[0] => 10 ~
arr[2] => 20 ~
arr[3] => 30 ~
*ptr=10이라는 것을 넣고 이후 ptr++를 하면? 4씩 증가
*ptr=20을 넣어주면 이후 20값 저장 후 ptr++을 하면? 4씩 증가…
이 상태에서 ptr -=2;를 하면?
2를 감소시키면 sizeof(int)*2만큼 값이 감소됨. (-8)
ptr은 다시 첫번째 요소를 가리키게 됨.
*ptr+=30을 하면 값이 40이 되면서 arr[n]이 바뀜.
포인터를 대상으로 하는 증가 및 감소 연산
int main(void)
{
int* ptr1 = ...;
int* ptr2 = ...;
ptr1++;
ptr1 += 3;
ptr2 -= 5;
ptr2 = ptr1 + 2;
...
}
연산의 결과를 확인한다면?
int main(void)
{
int * ptr1=0x0010;
double * ptr2=0x0010;
printf("%p %p \n", ptr1+1, ptr1+2);
printf("%p %p \n", ptr2+1, ptr2+2);
//적절하지는 않음
printf("%p %p \n", ptr1, ptr2);
ptr1++; //4증가
ptr2++; //8증가
printf("%p %p \n", ptr1, ptr2);
return 0;
}
이를 위해서 이 예제를 실행시켜보자!
00000014 00000018 //+4 / +8
00000018 00000020 //+8 / +16 // 16진수라서 0x0010+16 = 26이 아닌 0x0020
00000010 00000010 //기본 값 부르기
00000014 00000018 // 증가 후 기본 값 부름
이것을 보면 n x sizeoff(type)만큼 크기 증가/감소 된다는 사실을 알 수 있다.
위에는 int형이니까 값이 1증가할 때 마다 4씩 증가한다.
아래는 double형이니까 값이 1증가할 때 마다 8씩 증가한다.
연산 특성을 이용한 배열접근
int main(void)
{
int arr[3]={11, 22, 33};
int * ptr=arr; // int * ptr=&arr[0]; 과 같은 문장
printf("%d %d %d \n", *ptr, *(ptr+1), *(ptr+2));
printf("%d ", *ptr); ptr++; //후열이라 출력 되고
printf("%d ", *ptr); ptr++;
printf("%d ", *ptr); ptr--;
printf("%d ", *ptr); ptr--;
printf("%d ", *ptr); printf("\n");
return 0;
}
arr[3]배열 값이 11,22,33을 포인터로 지정
각 배열 값을 순서대로 출력한다. (단 ptr++로 후열에 있으니 출력되고 값이 +1되는 것.)
11,22,33
11,22,33,22,11이 된다.
이 예제를 근거로 연산의 차이를 알 수 있다.
*(++ptr)= 20; //저장된 값 자체를 변경
*(ptr+1)=20; // 값은 변경하지 않음.
int main(void)
{
int arr[3] = { 11,22,33 };
int* ptr = arr;
printf("%d %d %d \n", *ptr, *(ptr + 1), *(ptr + 2));
...
}
즉 arr[i]=*(arr+i)다.
여기서 ptr은 arr값과 출력 결과가 동일하다.
배열의 이름과 포인터 변수는 상수 변수차이만 있을 뿐 동일하기 때문
문제
문제1: 포인터를 이용한 배열 접근
길이가 5 int형 배열 arr을 선언하고 1,2,3,4,5로 초기화 후
배열의 첫번째 요소를 가리키는 포인터 변수 ptr선언
ptr저장된 값을 증가시키는 연산을 기반으로 배열요소접근
값을 2증가시키고 정상적 증가이루어졌는지 확인하라.
int main(void)
{
int arr[5] = { 1,2,3,4,5 };
int* ptr = arr;
int i;
printf("기본 값은 : %d 입니다 \n", ptr[0]);
for (i = 0; i < 5; i++) //0부터 시작해서 5개까지 1증가
{
*ptr += 2; //true면 2누적
ptr++; //ptr 값 상승 //다음배열로
}
for (i = 0; i < 5; i++) //출력 반복문
printf("%d \n", arr[i]);
return 0;
}
반복문을 통해서 각 배열의 순서대로 접근하면서 2을 누적시킨다.
그리고 출력반복문을 통해서 각 배열을 순차적으로 출력한다.
문제2:문제1에서 ptr 값을 변경시켜서 배열접근하라.
이번에는 값 변경 불가능 하고
덧셈연산하여 값 2씩 증가하는 코드
int main(void)
{
int arr[5] = { 1,2,3,4,5 };
int* ptr = arr;
int i;
printf("기본 값은 : %d 입니다 \n", ptr[0]);
for (i = 0; i < 5; i++) //0부터 시작해서 5개까지 1증가
{
*(ptr+i) +=2 ; // 축약 arr[i] = *(arr+i)
}
for (i = 0; i < 5; i++) //출력 반복문
printf("%d \n", arr[i]);
return 0;
}
기본적인 코드는 문제1의 코드를 따라간다.
포인터변수 ptr의 값을 변경시키지 않고 ptr자체에 덧셈연산을 해야되기에
*ptr +=2;를 *(ptr+i)+=2로 변경해준다.
문제3:
길이 5 int형 배열 arr 선언
이를 1,2,3,4,5 초기화
마지막 요소 가리키는 포인터변수 ptr선언
ptr 값을 감소시키는 형태 연산
모든 배열 요소 접근하여 배열에 정수 더하기
int main(void)
{
int arr[5] = { 1,2,3,4,5 };
int* ptr = &arr[4];
int total = 0, i; //포인터 0 ,null 초기화
for (i = 0; i < 5; i++)
total += *(ptr--); //포인터의 감소형 ptr[4] [3] ..
printf("%d", total); //역순으로 5+4+3+2+1로 더해진것
return 0;
}
주의 할점은 마지막 요소를 가리키는 포인터변수 ptr을 선언해야되니까 직접적으로 지시해준다.
&arr[4]를 지정시켜 마지막 요소를 가리킨다.
그후 ptr 값을 감소시키는 형태의 연산을 진행하니 누적변수를 통해서 포인터 감소형을 해준다.
즉 ptr[4]..[3]..[2]. ..그후 역순으로 합친 값을 출력한다.
문제4:
길이가 6인 int형 arr을 선언
이를 1,2,3,4,5,6으로 초기화
배열 저장된 값 순서가 6,5,4,3,2,1이; 되도록 변경
배열의 앞과 뒤를 가리키는 포인터 변수 두개를 선언해서 활용
int main(void)
{
int arr[6] = { 1,2,3,4,5,6 };
int* sptr = &arr[0];
int* eptr = &arr[5];
int a, b ;
for (a = 0; a < 3; a++) //3번 이후는 역전이라 3개까지만
b = *sptr; //b에 1저장
*sptr = *eptr; // sptr에 6저장
*eptr = b;// eptr에 1저장
sptr += 1; //1누적증가
eptr -= 1; //1누적감소
for (a = 0; a < 6; a++)
printf("%d", arr[a]);
return 0;
}
아까처럼 반대로 뒤집기를 해야되고 위치 변경 후
각각 가리키는 곳이 반대인 포인터 변수 2개 선언 (Start ptr , end ptr)
시작지점을 sptr에 끝지점을 eptr에 지정해줌.
b에 sptr이 가리키는 1값 / sptr에 eptr 가리키는 값 6 / eptr에 b가 가리키는 값 1 을 넣음.
eptr과 sptr이 교환 됨.
그후 sptr에 1누적증가 / eptr에 1누적감소를 시킴.
1. 초기 = {1,2,3,4,5,6}
2. 첫 번째 반복 진행 = {6,2,3,4,5,1}
3. 두 번째 반복 진행 = {6,5,4,3,2,1}
4. 결과 = {6,5,4,3,2,1}
중요) *sptr에 *eptr을 했는데 왜 str+=1하면 1부터 증가인가?
왜냐하면 *sptr = *eptr을 통해서 값을 ‘복사’된 것…
즉 값을 따온거지 그 자체의 값을 바꾼 것이 아님.
상수형태의 문자열을 가리키는 포인터
포인터는 1.변수 2.배열이 있다.
[문자열 가공]
char str[20] = "abcd"
? str="def" - 불가능
= def를 str이라는 상수에 저장해라 (상수라서 값 변환 불가)
[문자열 비가공 상수형]
char*str = "abcd"
의 경우는 가능하다.
why? []가 없는데? =>메모리 준비할 필요 x
abdc가 메모리 자동활당됨
위쪽 Str은 그 자체로 문자열 전체를 저장한다.
아래 Str은 메모리 상 자동으로 저장된 문자열 첫번째 문자 가르키기.
위는 계속해서 저장된 위치이다.
아래는 다른 위치로 변동이 가능하다는 점이 다르다.
char * str = "Yout Team"
str = "Our team"; //str이 가리키는 대상을 문자열 Out team 변경
//가능은 한데 되도록 작성을 지양하자.
변수형태의 문자열이라는 것이다.
이렇게 쓰면 헷갈리게 되니까 지양하는 것이다.
반대는 상수형태의 문자열
int main(void)
{
char str1[]="My String"; //변수형태
char * str2="Your String"; //상수형태
printf("%s %s \n", str1, str2);
str2="Our String"; // 가리키는 대상 변경
printf("%s %s \n", str1, str2);
str1[0]='X'; //성공
str2[0]='X'; //실패
printf("%s %s \n", str1, str2);
return 0;
}
변수형태와 상수형태에 대한 가리키는 대상 변경을 시도.
성공과 실패로 나뉜다.
이는 컴파일러마다 결과값이 다르긴한데 바꾸기는 가능하다.
하지만, 특정 컴파일러를 사용해야되는 코드작성은 올바르지 못하다.
어디서든 선언할 수 있는 상수형태의 문자열
char *str = "Const String"; (자동할당)
char * str = 0x1234; //이렇게 저장된다. -> 주소값
printf("show your string"); //함수 호출과정에서 선언되는 문자열
printf("0x1234"); //호출을 하면 주소값이다.
//printf함수는 문자열 통쨰로 받는 게 아니라 주소값 전달받음
Whoareyou("Hong"); //whoareyou함수 호출
void whoareyou(char * str) {....}
//실제로 전달되는 주소는 H의 주소값 이다.
포인터 변수로 이뤄진 배열 : 포인터배열
int arr[3]; -> int *arr[3]이다.
포인터배열의 이해
int * arr1[20]; //길이 20 int형 포인터 배열 arr1
double * arr2[30];//길이 30 doulb형 포인터 배열 arr2
int main(void)
{
int num1=10, num2=20, num3=30;
int* arr[3]={&num1, &num2, &num3}; //5행 선언한 변수로 주소값 초기화
printf("%d \n", *arr[0]);
printf("%d \n", *arr[1]);
printf("%d \n", *arr[2]);
return 0;
}
이 구문을 통해서 *arr[0]을 통해서
각각 값이 10,20,30이 출력된다.
문자열을 저장하는 포인터배열
char*strArr[3];길이가 3인 char형 포인터배열
int main(void)
{
char * strArr[3]={"Simple", "String", "Array"};
//char형 3개 [3]넣음 각각 Simple = 7byte / String= 7byte / Array=6byte
//메모리 simple -> 0x.. -> [0] -> 0x...
//char strArr={0x1004, 0x1048, 0x2102]
//strArr[0]->simple \0 strArr[1]->String \0 strArr[2] -> Array \0
printf("%s \n", strArr[0]);
printf("%s \n", strArr[1]);
printf("%s \n", strArr[2]);
return 0;
}
char형 이기 떄문에 글자수 +1(\0)을 계산한다. 7/7/6
그러면 메모리를 보면 simple이 0x123124이 되고 이를 [0]이고 이것이 또 0x..가된다.
즉 strArr={0x1104, 0x1048, 0x2102}가 된다.
각각 값은 입력한 값에 \0가 붙은 것으로 저장된다.
포인터배열에 대한것은 여기까지이다.
각 챕터별로 하면 올바른 분량이 나오는 것같다.
다음은 포인터와 함수에 대한 이해이다.
-
[C언어의 기본]포인터와 배열
포인터
포인터란? 주소 값 저장을 목적으로 선언되는 포인터 변수
int main(void)
{
char ch1 = 'A', ch2 = 'Q';
int num = 7;
...
}
이 구문을 보면 메모리 할당이 A 1칸 / 7 n칸이 되는데 n칸중 제일 앞에 칸이 주소(int형 특징)가 된다.
| 1칸 | 2칸 | 3칸 | 4칸 |
| —- | —- | —- | —- |
이렇게 4bit의 칸이 있다고 한다면 제일 앞에 1칸이라는 단어가 주소가 된다는 것이다.
즉, 포인터는 0x12ff76과 같이 저장된다 (주소값)
포인터는 상수와 변수 2가지의 형태가 있다.
연산자맛보기
정수 7이 저장된 int형 변수 선언, 이 주소값 저장을 위한 포인터 변수 pnum을 선언한다.
그리고 pnum에 변수 num주소 값을 저장한다.
int main(void)
{
int num = 7;
int* pnum; // 포인터 변수 선언
pnum = # //주소값 이니까 붙히고 저장
}
이렇게 되면 pnum은 0x122ff76과 같은 주소값 형태가 되고
num=7인데, 그 앞에 값이 0x122f76이니까 즉 포인터는 7을 가르키고 있는 것이다.
| 0x122ff76 | 0x122ff77 | 0x122ff78 | 0x122ff79 |
| ——— | ——— | ——— | ——— |
7의 값이 이러면 앞에 0x122ff76이 주소값 이라는 것이다.
포인터 변수 선언
int* pnum1;
double* pnum2;
unsigned int* pnum 3;
//= > 기본공식 type * ptr;
포인터 변수선언은 (Type) * ptr;로 이루어진다.
포인터와 연산자
int num = 20
int ptr = &num
//가능하다 왜 ? 정수형에 정수변수를 넣었기 때문
int num = 20
int* ptr = *num;
but)
int ptr=
//* ptr 은 안됨 num에 대한 정보가 없어서
이것을 통해 타입이 필요한 이유를 알 수 있다.
실제로 int형은 4byte, doulbe형은 8byte다.
이러한 형태를 맞추지 않으면 8byte에 4byte가 들어가서 이상해진다.
형을 말해주지 않으면 불가능하다
컴퓨터 - “그래서 얘가 몇바이트 인데 인간”
int main(void)
{
int num1 = 5;
double* pnum1 = &num1; //불일치
double num2 = 5;
int* pnum2 = &num2; //불일치
}
이것을 보면 메모리는 int형인데 포인터에게 난 8byte인데? 라고 해서
메모리를 오버하게 저장을 한다.
메모리 참조하는 *연산자
//포인터 변수를 가리키는 메모리 공간인 변수 num접근
=*pnum=20;
포인터 변수 pnum이 가리키는 메모리 공간인 변수 num에 정수 20저장
pnum이 가리키는 메모리 공간인 변수 num값을 출력
int main(void)
{
int num1 = 100, num2 = 100;
int* pnum;
pnum = &num1; // 포인터 pnum이 num1을 가리킴
(*pnum) += 30; // num1+=30과 동일
pnum = &num2; // 포인터 pnum이 num2를 가리킴
(*pnum) -= 30; // num2-=30과 동일
printf("num1:%d, num2:%d \n", num1, num2);
return 0;
}
이 예시를 보면 num=100으로 num2도 100으로 초기화되었고,
int * pnum을 하여 포인터 지정
첫 번째로, pnum에 &num1값을 가리킨다.
그 후 pnum값에 30을 누적
두 번째로, pnum에 &num2값을 가리킨다.
그 후 pnum값에 -30 누적
결과로 num1은 130, num2는 70이 된다.
여기서는 포인터를 통해 변수를 가리키는 지점을 변경시켜 관리한 것이다.
포인터 형의 존재 이유
포인터 형은 메모리 공간을 참조하는 기준이 된다.
int main(void)
{
double num = 3.14;
int* pnum = # //형 불일치
printf("%d", *pnum); //의미없음
}
이 예시를 보면 double형을 지정 후 그 값의 포인터를 int형으로 지정했다.
출력을 한다고 한듯 그것은 의미가 없다.
컴파일러에서는 에러가 발생하지 않으면 출력이 되지 않는다.
왜냐하면 C언어는 유연함을 보장하지 위해서 그렇다.
포인터 기반의 메모리 접근 기준을 만들기 위해서 타입이 존재한다는 것을 알아두자!
문제
문제1: 예제 실행시 변수와 포인터 변수의 관계를 그림을 그려서 표현한다.
- 이것은 직접 그리는 거라... 넘긴다..!!ㅋㅋㅋ 블로그 못올림
문제2 : int형 변수 num1 num2을 선언해서 동시에 10 20을 초기화한다. (조건1)
int형 포인터 변수 ptr1 ptr2 선언하여 각각 num1 num2값을 가르킨다. (조건2)
이 상태에서 ptr1 ptr2를 이용해서 num1값 10증가 num2 값 10감소 시킨다. (조건3)
그리고 이 상태에서 가리키는 대상을 바꿔보자.(조건4)
int main(void)
{
//조건1
int num1 = 10;
int num2 = 20;
//조건2
int * ptr1 = &num1;
int * ptr2 = &num2;
int * temp;
printf("기본값 ptr1는 %d \n", *ptr1);
printf("기본값 ptr2는 %d \n", *ptr2);
//조건3
(*ptr1) += 10;
(*ptr2) -= 10;
printf("추가값 ptr1는 %d \n", *ptr1);
printf("뺸값 ptr2는 %d \n", *ptr2);
//조건4
temp = ptr1;
ptr1 = ptr2;
ptr2 = temp;
printf("최종변환값 ptr1는 %d \n", *ptr1);
printf("최종변환값 ptr2는 %d \n", *ptr2);
return 0;
}
위를 보면 일단 바꿔치기 해야되니까 안전하게 temp를 써준다.
초기화를 한후 포인터 지정 그리고 체크를 위해서 한번 ptr1 과 ptr2값을 출력해준다.
그리고 그 값을 조건3에 맞춰서 한후 printf를 통해 추가값인지 뺀 값인지 체크
그리고 조건4를 통해 교차시킨후 최종변환값이라는 병목으로 바뀌었는지 체크한다.
잘못된 포인터의 사용과 Null 포인터
int* ptr; //쓰레기값
*ptr = 200; //0x0010
자 이렇게 ptr값을 초기화를 해주지 않으면 쓰레기값이 들어가게 된다.
이때 포인터 ptr에 값을 집어 넣는다면 어떻게 될까?
=> 포인터가 “어..? 기존 쓰레기값이 어디있지?”하면서 문제가 생길 수 있다.
int* ptr1 = 0;
int* ptr2 = NULL;
그래서 포인터 변수를 초기화 할때는 0이나 Null값을 지정해준다.
다음은 엄청 길거같아서 이번은 짧게가고 다음은 길게 갈것같다.
다음은 포인터 + 배열 / 함수 이다.
-
[C언어의 기본]배열 이해 선언 초기화
2챕터 배열
Visual studio 에서 직접 테스트 해보고 블로그에는 따로 안올려놔서 한번에 업로드 하게 될 것 같다.
원래 Visual Studio Code로 작성했는데 불편해서 타이포라로 바꾸니까 좀 많이 편해진 것 같다..
시작한다
배열
배열이란? 다수의 데이터를 처리하는 경우 유용하게 사용하는 것이다.
int main(void)
{
int floor101, floor102, floor103, floor104; //1층 101~104호
int floor201, floor202, floor203, floor204; //2층 201~204호
}
예를 들어 위와같이 아파트의 층에 따른 구분과 호수에 따른 구분을 저장하는 예시인데
이렇게 저장하면 하나하나 다 기입해야되니까 별로다..
위처럼 1차원배열의 선언에는 3가지가 필요하다 : 배열이름, 자료형, 길이정보이다.
int onedimarr[4]; 이것은 자료형 / 변수이름/ 길이 의 순서로 입력된 것이다.
= int형 변수 4개로 이루어진 onedimarr배열이다. 이것은 int int int int 이다.
int arr1[7]; //int형 1차원 배열 arr1 - 길이 7
float arr2[10]; //float 형 1차원 배열 arr2 - 길이 10
double arr3[12]; //double형 1차원 배열 arr3 - 길이 12
배열의 길이정보는 범용적 컴파일러를 위해서 상수로한다.
최근 새로운 C의 표준은 int len=20; int arr[len]이 가능하다.
선언된 1차별 배열의 접근
int arr[3]; //길이3 int형 1차원 배열
arr[0] = 10; //배열 arr의 첫 번째 요소에 10 저장
arr[1] = 12; //배열 arr 두 번째 요소 12
arr[2] = 25; //배열 arr 세번 쨰요소 25
arr[idx] = 20; //배열 arr의 idx+1번째 요소에 20을 저장
설명은 주석으로 적어서 그렇고
즉 arr[idx]의 기본형은 배열 의 idx+1번째 요소에 = n;값을 저장하는 것이다.
int main(void)
{
int arr[5];
int sum = 0, i;
arr[0] = 10, arr[1] = 20, arr[2] = 30, arr[3] = 40, arr[4] = 50;
for (i = 0; i < 5; i++) //배열의 순차접근
sum += arr[i];
printf("배열요소에 저장된 값의 합: %d \n", sum);
return 0;
}
이 구문을 통해 알수 있는 점은
배열의 모든 요소는 반복문을 이용하여 순차적인 접근이 가능하다는 것이다.
배열의 선언과 동시에 초기화 설명
int arr[5] = { 1,2,3,4,5 }; //순차적으로 1,2,3,4,5를 초기화함.
int arr[ ] = {1,2,3,4,5,6,7}; //이러면 자동으로 컴파일러에 의해서 7일 삽입됨.
int arr[5] = {1,2} 이러면 후방 요소는 0으로 채워짐 1 2 3 0 0
배열에 들어가면 순차적으로 초기화 시키고
빈 상태로 넣으면 컴파일러가 자동으로 삽입해줌
그리고 빈칸은 0으로 채워지게 됨.
int main(void)
{
int arr1[5] = { 1, 2, 3, 4, 5 }; //배열 1
int arr2[] = { 1, 2, 3, 4, 5, 6, 7 }; //배열 2 7저장
int arr3[5] = { 1, 2 }; //배열 3 후열 0저장
int ar1Len, ar2Len, ar3Len, i; //기타 변수
printf("배열 arr1의 크기: %d \n", sizeof(arr1)); //4byte *5
printf("배열 arr2의 크기: %d \n", sizeof(arr2)); //4byte * 7
printf("배열 arr3의 크기: %d \n", sizeof(arr3)); // 4byte * 5
ar1Len = sizeof(arr1) / sizeof(int); // 배열 arr1의 길이 계산 (EA) => 20/4 = 5개
ar2Len = sizeof(arr2) / sizeof(int); // 배열 arr2의 길이 계산 (EA) => 28/7 = 4
ar3Len = sizeof(arr3) / sizeof(int); // 배열 arr3의 길이 계산 (EA)
for (i = 0; i < ar1Len; i++) //배열의 길이를 보는 반복문 5까지
printf("%d ", arr1[i]);
printf("\n");
for (i = 0; i < ar2Len; i++) //7까지
printf("%d ", arr2[i]);
printf("\n");
for (i = 0; i < ar3Len; i++) // 5까지
printf("%d ", arr3[i]);
printf("\n");
return 0;
}
이 문장을 보면 배열의 이름을 대상으로 하는 sizeof 연산의 결과는 바이트 단위의 배열의 크기이다.
sizeof(arr1)/sizeof(int)는 arr1의 size인 4byte *5 = 20개 에서 기본 인트 4byte를 나눈 5개를 출력
문제풀이1
길이가 5인 int형 배열을 선언해서 프로그램 사용자에게 5개의 정수를 입력받자.
입력 종료시 다음의 내용을 출력하라
1.입력 정수 중 최대값 2. 입력 정수 중 최소값 3. 입력 정수의 총합
int main(void)
{
int arr[5];
int max, min, sum;
int ea;
//값 입력 반복문
for (ea = 0; ea < 5; ea++)
{
printf("값을 입력하세요: ");
scanf("%d", &arr[ea]);
}
//최대값
max = min = sum = arr[0]; //배열변환
for (ea = 1; ea < 5; ea++) //반복값
{
sum += arr[ea]; //sum변수 누적값
if (max < arr[ea]) //만약 최대치보다 arr[ea]이 크면
max = arr[ea]; //max값에 넣기 최대치 삽입
if (min > arr[ea])
min = arr[ea]; //min값에 최소치 삽입
}
printf("최대치: %d \n", max);
printf("최소치: %d \n", min);
printf("합계: %d \n", sum);
return 0;
}
문제풀이 2
char형 1차원 배열을 선언과 동시에 다음 문장의 내용으로 초기화,
초기화후에는 저장된 내용 출력예제 "Good Time"
int main(void)
{
char ea[] = { 'G','o','o','d',' ','t','i','m','e' };
int arrlen = sizeof(ea) / sizeof(char);
int i;
for (i = 0; i < arrlen; i++)
printf("%c", ea[i]);
printf("\n");
return 0;
}
개수를 담은 다음에 그것을 i에 대입해서 반복문을 진행하여서
순차적 접근하여 실행한다.
배열을 이용한 문자열 변수표현
char str[30]=30보다 작은 ~ 것이다.
char형 배열의 문자열 저장과 널 문자를 알아보자
char str[14] ="Good morning"이라는 것이 있다고 치면
배열에는 Good morning+ \0가 저장이 된다.
왜냐하면 컴퓨터는 어디서 끝나는지 알아야되는데 그것을 찾는 조건이 \0이기 때문이다.
그래서 +1이 카운팅된다. 이것이 Null문자 라는 것이다.
int main(void)
{
char str[] = "Good morning!";
printf("배열 str의 크기: %d \n", sizeof(str)); //byte
printf("널 문자 문자형 출력: %c \n", str[13]); //널 값
printf("널 문자 정수형 출력: %d \n", str[13]); //널 값
str[12] = '?'; //문자열 출력 = 변경
printf("문자열 출력: %s \n", str);
return 0;
}
이것을 보면 Null의 아스키 코드 값은 0, 문자는 미출력 된다는 사실을 알 수 있다.
공백문자의 아스키 코드 값은 32이 있기에 공백과 Null값은 다르다.
Scanf함수를 이용한 문자열을 입력했던 때가 있는데
그때 %d가 아닌 %s를 썼다. 그것이 str.
int main(void)
{
char str[50]; //배열
int idx = 0; //변수
printf("문자열 입력: ");
scanf("%s", str); //문자열을 입력 받아 str에 저장
printf("입력 받은 문자열: %s \n", str); //배열은 &없음. 문자열만
printf("문자 단위 출력: ");
while (str[idx] != '\0') //값이 Null이 아닐때까지 출력한다. - | /0 컷되는 컷반복문
{
printf("%c", str[idx]);
idx++;
}
printf("\n");
return 0;
}
배열 선언 후 변수를 입력했고 scanf를 통해서 문자열을 입력받는다. (문자와 문자열은 다르다..)
그 후 while반복문을 통해서 str배열을 출력해주는데 Null값이 아닐때 까지 출력이 된다.
이렇듯 이 케이스를 보면 Null문자가 중요하다는 사실을 알 수 있다.
Null이 있으면 문자열이고 Null이 없으면 문자열이 아닌 것이다.
//Null이 필요한 경우
while (str[idx] != '\0')
{
printf("%c", str[idx]);
idx++;
}
위 코드에서 이 부분만 똑 떼서 본다면?
문자열에서 \0로 구분되는 것이 없다면 컴퓨터는 글의 끝을 알 수 없다.
int main(void)
{
char str[50] = "I like C programming";
printf("string: %s \n", str);
str[8] = '\0'; // 9번째 요소에 널 문자 저장
printf("string: %s \n", str);
str[6] = '\0'; // 7번째 요소에 널 문자 저장
printf("string: %s \n", str);
str[1] = '\0'; // 2번째 요소에 널 문자 저장
printf("string: %s \n", str);
return 0;
}
이 문구를 통해서 내가 입력한 문자열이 있는데 그 사이에 Null값을 넣어
출력을 바꿀 수 가 있다..! 즉 Null값은 문자의 종료다.
추가로 scnaf의 문자열의 입력특성이 있는데
우리가 He is my friend로 저장하면 He is my friend 4개로 저장된다.
즉 scanf는 문자열 입력으로 적합하지 않다..!
문제
문제1: 사용자로부터 하나의 영단어를 받아 입력받은 길이를 계산하는 프로그램을 출력하라.
int main(void)
{
char arr[50];
int ea = 0;
printf("영단어를 입력: ");
scanf("%s", arr);
while (arr[ea] != 0) //arr의 순차계산으로 0이 아닐때 반복
ea++;
printf("단어의 길이는 : %d입니다 \n", ea);
return 0;
}
배열 크기는 적당히 지정하고
아까 빈칸은 0으로 저장된다고 하였으니 0값이 아닐때까지 반복한 것에 대한 개수를 추출한다.
그러면 단어의 길이가 나온다.
문제2: 사용자로부터 영단어르 입력받아 char형에 저장. 그후 그 영단어를 역순으로 뒤집기
이때 널 위치 문자를 변경해서는 안되고 뒤집혔는지 체크
int main(void)
{
char arr[50];
int ea = 0; //기입변수
int i; //뒤집변수
char temp; //잠깐 저장하는 변수
printf("단어 입력: ");
scanf("%s", arr);
while (arr[ea] != 0) //값이 공백이 아닐때까지
ea++;
for (i = 0; i < ea / 2; i++) //조건은 ea/2로 한다 why? 반을 뒤집이면 어차피 반은 뒤집히니까
{
temp = arr[i]; //temp값에 임시저장
arr[i] = arr[(ea - i) - 1]; //i번째 문자와 (ea-i)-1 번째 문자를 바꾼다.
//ex) i=0 (ea-i)-1 = (5-0)-1 = 4 arr[0] 와 arr[4] 교환
//ex) i=1 (ea-i)-1 = (5-1)-1 = 3 arr[1] 과 arr[3] 교환
arr[(ea - i) - 1] = temp; // temp저장된 값을 arr의 [(ea - i) -1]에 넣기
}
printf("뒤집한 값 : %s \n", arr);
return 0;
}
배열 지정 후
기입을 변수와 뒤집을 변수, 그리고 임시저장 변수 3가지를 지정한다.
단어입력은 받은 후 그 값을 공백이 아닐때 까지 반복한다.
(공백이 되면 임시적으로 그 글자가 끝났다는 것)
반을 뒤집으면 나머지 반은 뒤집히니까 조건은 ea/2로 하고
temp를 통해 임시값 저장 그리고 문장을 뒤집는 것을 발생
그리고 그 값을 다시 임시값을 불러들여서
뒤집한 값을 출력한다.
문제3 : 사용자부터 영단어를 입력받는다. 그 후 영단어를 구성하는 문자중 아스키코드 값이 큰 것을 찾아 출력한다
LOVE면 아스키 코드 값이 큰 V가 출력되어야한다.
int main(void)
{
char arr[50]; //배열
int ea = 0; //길이 저장 변수
int i; //인덱스
char max = 0; //최대 아스키 코드 값 저장 변수
printf("영단어 입력: ");
scanf("%s", arr);
while (arr[ea] != 0) //단어 길이 계산하기
ea++;
for (i = 0; i < ea; i++) // 0부터 시작해서 길이만큼
if (max < arr[i]) //만약 max값보다 arr[i]이 크면
max = arr[i]; //max값에 arr[i]을 넣어준다.
// 0 <-> ? 이후 ?값이 크면 ?으로 그 이후 ? <-> n 비교 ?가 크면 ?를 넣어서
// 큰값을 넣기.
printf("가장 큰 아스키코드 값의 문자: %c \n", max);
return 0;
}
배열을 지정해주고 길이 체크용 변수, 인덱스용 변수
그리고 최대 아스키 코드값 저장 변수를 찾는다. (MAX값을 찾아야하니까)
그러면 while 반복문을 통해서 단어의 길이가 0이 아닐때까지 찾아서 길이를 계산하고
반복문 안에 if문을 달아서 더 큰값을 arr[i] 값에 넣어주어 최대값을 출력한다
이게 보니까 글을 너무 많이쓰면 난잡해져서 끊어쓰는 방식으로 여러번 올려야겠다.
다음은 포인터를 시작한다.
-
[C언어의 기본]-도전프로그래밍1 문제풀이
도전 프로그래밍!
지금까지의 지식을 테스트해보고 예제문제를 풀어보는 시간이다.
문제1
10진수 정수를 입력받아서 16진수로 출력하는 프로그램을 작성해보자
int main(void)
{
int num;
printf("10진수 입력해주세요: ");
scanf("%d", &num);
printf("10진수 %d는 16진수 %x입니다. ", num, num);
}
10진수를 입력받아서 %d를 %x로 바꾼다.
문제2
두 개의 정수를 입력받아 구구단 출력하는 프로그램
조건 - 3과 5입력시 3 4 5단만 2와 4를 입력시 2 3 4만 출력된다.
조건 - 3 5, 5 3 순서에 관계없이 그 사이 범주의 값을 출력한다.
//구구단 테스트 코드 작성
int main(void) {
int dan1 = 2;
int dan2 = 5;
// 구구단 출력
for (dan1; dan1 <= dan2; dan1++) {
for (int num = 1; num <= 9; num++) {
printf("%dx%d = %d\n", dan1, num, dan1 * num);
}
printf("\n");
}
return 0;
}
구구단의 기본적인 틀을 만들어 준다.
int main(void)
{
int mindan, maxdan, num= 0;
int space = 0;
printf("몇단부터 몇단을 출력하실건가요?: ");
scanf("%d %d", &mindan, &maxdan);
//무조건 작은수 앞 큰수 뒤
if (mindan > maxdan) //min에 큰값이 max에 작은 값이 들어오면 변환
{
space = mindan; //공간에 큰 수 [5]
mindan = maxdan; // min에 작은 값을
maxdan = space; // 큰 공간에 큰수
}
//구구단 조건문
//i=무조건 작은수 dan1, 조건 dan1이 dan2보다 크거나 작으면, i증가식
for (mindan; mindan <= maxdan; mindan++) { //단의 증가
for (int num = 1; num <= 9; num++) { //수가 1씩 증가하고 9까지 반복.
printf("%d x %d = %d\n", mindan, num, mindan * num);
}
printf("\n");
}
return 0;
}
for (mindan; mindan <= maxdan; mindan++) 를 보면
초기식 : mindan =0
조건식 : mindan <= maxdan (mindan이 maxn단보다 작거나 같을때까지)
증감식 : mindan ++ (mindan을 1씩 증가)
for (int num = 1; num <= 9; num++) 이거는 수다.
nx1 ~ nx9까지 출력하는 것이다.
문제3
두 개의 정수를 입력받아 최대공약수(GCD -greatest common divisor)를 구하자
추가로, 여유가 되면 유클리드 호제법을 조사하여 구현해보자.
int main(void)
{
int num1, num2;
int gcd;
int i; //기준 값
printf("두개의 정수: ");
scanf("%d %d", &num1, &num2);
for (i = 1; i <= num1 && i <= num2;i++) //기준 i를 1부터 시작하여 늘려가는 나눗셈 반복
{
if (num1 % i == 0 && num2 % i == 0) //num1과 num2를 i로 나눠서 0이 나오는 값이 나오면
gcd = i; //gcd에 i값을 넣어준다,
}
printf("%d와 %d의 최대공약수는 %d입니다 \n", num1, num2, gcd);
return 0;
}
최대 공약수는 둘이 가지는 약수 중 가장 큰수
for (i = 1; i <= num1 && i <= num2;i++)
초기식 : i를 기준 1부터 시작
조건식: i가 num1보다 작거나 같되, num2보다 작거나 같아야된다.
증감식: i증가
if (num1 % i == 0 && num2 % i == 0)
만약에 위 조건이 성립하고
i 나 눴을 때 나머지 값이 둘다 0이면 출력한다.
유클리드 호제법 (최적화)
두 개의 정수 혹은 다수의 자연수에서 최대공약수를 구하는 알고리즘
원리: 두 수가 서로 나눠서 나머지를 구하기. - 나머지 0이면 최대공역수
ex)168, 1980의 GCD
1980 / 168 = 168 * 11 + 132
168 /132 = 132 * 1 + 36
132/36 = 36 *3 +24
36/24 = 24 * 1 + 12
24/12 = 12 * 2 + 0 //나머지 0일때
12 = 최대공약수
1980 168의 문제를 24 ,12로 바꾸는 것이다.
a,b의 최대공약수 = (a,b):
a=bq+r ex)a=4 b=2 q=2 r=0 => (a,b) = (b,r)
이 식에 168 1980 / 12 24를 보면
1980 / 168 = 168 * 11 + 132 = (1980[a],168(b), 11[q] + r[132])
a = bq+r => (a,b) = (b,r)
=> 1980 = 168 * 11+132 => (1980,168) = (168,132)
=> (168,132) ... (b,r)이 (a,b)위치로 가게되고 더 작은 수로 줄이는 과정
//유클리드 구현 (반복문)
int gcd(int a, int b) //최대공약수 기능
{
while(1) //항상 실행 loop - a와 b의 반복 교환
{
int temp = b; //b값은 계속 써야되니까 임시저장소
b = a % b; // a와 b를 나눈 값을 b에 저장
a = temp; // temp에 저장된 값을 a에 저장 (a,b)갱신
if (b == 0) //b가 0이되면 최대 공약수가 생기니
break; //탈출조건
}
return a; //값을 a값을 반환한다.
}
int main(void)
{
int a, b;
printf("두 수를 입력해주세요 : ");
scanf("%d %d", &a, &b);
printf("최대공약수는 %d", gcd(a, b)); //출력한다 gcd로 a와 b값을 한것
return 0;
}
반복문이 된다면 결국 자기 자신을 호출하는 재귀함수도 된다고 생각해서 제작해봤다.
//유클리드 구현(재귀함수)
int gcd(int a, int b) //최대공약수 기능
{
if (b == 0) //상위조건 b가 0이면
return a; // a값을 반환한다.
else //재귀조건
return gcd(b, a % b); //계속 자기를 호출시킴 (재귀)
//b를 현재 값을 넣어주고, a%b를 하여 나머지를 구한다 = (b,r)
//재 호출 후 (b,r)...반복하여 0까지 반복
}
int main(void)
{
int a, b;
printf("두 수를 입력해주세요 : ");
scanf("%d %d", &a, &b);
printf("최대공약수는 %d", gcd(a, b)); //출력한다 gcd로 a와 b값을 한것
return 0;
}
재귀함수를 쓰면 반복문보다 코드도 짧고 편해지는 것 같다.
문제4:
도전4: 금요일 집으로 가는길 수중 5000원있음.
조건1; DVD한편을 빌림 - 수중 3500원
조건2: 크림빵500원, 새우깡700원 콜라400원 사려함.
무잔돈 + 최소 1개이상 구매
수중의 돈 3500, 500 700 400 최소1회로 0원조건
int main(void)
{
int money; //돈
int bread = 500; //빵
int shrimp = 700; //새우
int cork = 400; //콜라
int a, b, c; //a-빵 b-새우 c-콜라
printf("수중의 돈: ");
scanf("%d", &money);
for (a = 1; a < money / bread; a++) //빵을 1개부터 떨어질때까지 반복
{
for (b = 1; b < money / shrimp; b++) //새우를 1개부터 떨어질까지 반복
{
for (c = 1; c < money / cork; c++) //콜라를 1개부터 떨어질때까지 반복
{
if (bread * a + shrimp * b + cork * c == money) //이 조건을 출력 돈과 같은 경우
printf("크림빵 %d개, 새우깡 %d개, 콜라 %d개 입니다\n", a, b, c);
}
}
}
printf("위 테이블이 가능한 수 입니다.");
return 0;
}
모든 경우의 수 출력이다.
변수를 아에 나누는 거로 하면 복잡하려나
int main(void)
{
int money; //돈
int bread, shrimp, cork;
int a, b, c; //a-빵 b-새우 c-콜라
printf("수중의 돈: ");
scanf("%d", &money);
bread = money / 500;
shrimp = money / 700;
cork = money / 400;
for (a = 1; a < bread; a++) //빵을 1개부터 떨어질때까지 반복
{
for (b = 1; b < shrimp; b++) //새우를 1개부터 떨어질까지 반복
{
for (c = 1; c < cork; c++) //콜라를 1개부터 떨어질때까지 반복
{
if (500 * a + 700 * b + 400 * c == money) //이 조건을 출력 돈과 같은 경우
printf("크림빵 %d개, 새우깡 %d개, 콜라 %d개 입니다\n", a, b, c);
}
}
}
printf("위 테이블이 가능한 수 입니다.");
return 0;
}
개인적으로 위 코드가 더 직관적인 것 같다..
문제5:
10개의 소수를 출력하는 프로그램
나눠서 (1, n) 이면 소수 / (1, n, 의외 수)가 있으면 소수가 아님
int main(void)
{
int num; //증가변수
int i; //반복문
int count = 0; //개수 제한
for (num = 2; count < 10; num++) //개수 증가 반복문 (0과 1은 소수가 아니여서 2부터)
{
for (i = 2; i <= num; i++) //1은 모든게 나뉘어져서 2로 제한, i가 num보다 작같
{
if (i == num) // i값과 num값이 같아지는 조건이면 소수다. i n만 인증
{
printf("%d ", num);
count++;
}
if (num % i == 0) //num과 i값의 나머지가 0이면 1 n +@이라 빠져나감.
break; //탈출 다음 num값 이동
}
}
printf("\n");
return 0;
}
최소 조건을 안해서 조금 애먹었다..
for (num = 2; count < 10; num++) 이 부분의 2
for (i = 2; i <= num; i++) 여기서 2
조금 고생했다..
도전 6:
초를 입력받으면 시 분 초 형태로 출력
int main(void)
{
int second;
int h, m, s;
printf("초 입력: ");
scanf("%d", &second); //초 입력
h = second / 3600; //3600나눈것
m = (second%3600) / 60; //3600으로 나눈 것의 60나눈것
s = (second%3600) % 60; //3600으로 나눈 것의 60나눈것의 나머지
printf("입력 한 초는 %d이며 이는 %d시간 %d분 %d초 입니다", second, h, m, s);
return 0;
}
초입력이라서 맨 처음에는 그냥
3600s = 1h => /3600
60s = 1m => /60
s = n
으로 생각해서 짰는데 안됐다.
생각해보니
1시간으로 나누고.
남은 값의 분
의 남은 값의 초다.
h = second / 3600; //3600나눈것
m = (second%3600) / 60; //3600으로 나눈 것의 60나눈것
s = (second%3600) % 60; //3600으로 나눈 것의 60나눈것의 나머지
그래서 이렇게 정의했다.
도전 7:
n을 입력받는다. 그리고 공식이 성립하는 k의 최대값을 계산하여 출력
\(2^k <= n\)
즉 내가 입력한 값에 2의 제곱 값이 나오는 것이다.
int main(void)
{
int n; //입력값
int k; //k값
int result = 1; //누적값
printf("입력 값: ");
scanf("%d", &n);
for (k = 0; result <= n; k++) //k를 0부터 / 누적값이 n과 같아질때까지 K증가
result *= 2; //2의 제곱 누적 result *2 = result
printf("k의 최대값은 %d \n", k - 1); //for가 커지면 종료되는데, k값은 넘은 값이니 -1 2^n-1
return 0;
}
내가 256을 입력하면 k값은 8이 반환되어야 하는 거다.
for (k = 0; result <= n; k++)
초기식: K=0
조건식: 누적값이 n보다 작거나 같을때까지
증감식 : K를 증가시킨다.
맞으면 result에 2배씩 누적된다.
2 * 1 =2 *2 = 4 *2 = 8…
출력에서 k-1을 해줘야한다.
왜냐면 for반복문에서 result값이 커지면 반복문이 종료된다.
즉 값이 이미 커져있기 떄문에 k값을 한 단계 내려주는 것이다.
도전8:
2의 n함수의 재귀 구현 - 위는 반복문이니 충분히 가능할 것이다.
int recursion(int num) //재귀함수
{
if (num == 1) //값이 1이면
return 2; //2반환 2^1
else //아니면 재귀걸기
return 2 * recursion(num - 1); //2*(직전2^n) 값 출력
}
int main(void)
{
int num;
printf("정수: ");
scanf("%d", &num);
printf("2의 %d승은 %d \n", num, recursion(num));
return 0;
}
1이면 그냥 2를 뱉어주고 아니면
2*(재귀(num-1))을 해준다.
ex) 3이 입력되면 2*(3-1)이다.
왜냐면 재귀를 하면서 앞에 2*가 있으니 -1을 해주는 것이다.
솔직히 처음에 감이 잘 안왔다
자꾸 2배 큰 숫자가 나와서.. 근데 앞에 2를 곱해줘야되니 2를 뺴야되는 것이다.
//재귀미사용
int answer(int);//기능 선언만 먼저
int main(void)
{
int num;
printf("정수를 입력해주세요: ");
scanf("%d", &num);
printf("2의 %d승은 %d \n", num, answer(num)); //재귀가 아니라서 num-1불필요
//2의 정수승은 answer(정수)값
return 0;
}
int answer(int a) //기능 출력은 int형
{
int i; //제곱 변수
int result = 1; //누적 변수
for (i = 1; i <= a; i++) //1시작, a값보다 작같, i증감
result *= 2; //2제곱 누적 i=1 2/ i=2 4/ i=3 6.. 누적
return result; //결과값은 result값
}
for문으로 누적을 시켜주는데 재귀처럼 2를 시작하고 하는게 아니라서
` *=2`로 그냥 누적만 시켜주면 된다
기본 적인 것이 끝나고 포인트와 배열로 간다!
-
[C언어의 기본] 함수와 전역지역변수
C언어의 함수
C언어의 핵심은 포인터가 아닌 함수라고 한다.
지금까지 예제 중에서 포인터는 없어도 함수는 무조건 있다.
함수를 잘 구성하고 활용하는 프로그래머가 실력있는 프로그래머다.
거기에는 어느정도의 시간과 경험이 필요하다.
함수의 정의와 선언
기본적으로 출력형태는
int main (void)처럼 반환형태(출력) / 함수이름 / 입력형태 다.
다시 한번 상기시키자.
함수를 만드는 이유
일을 하는 것에 있어서 문제를 작게 나누어서 하나씩 해결하는 것이 중요하듯, 프로그램의 구현도 같다.
구현을 하기 위해서는 구현에 필요한 기능을 분석하고 결과를 바탕으로 작은 크기의 함수를 디자인 해야한다.
“Divide and Conquer” - 나누어서 정복하라
함수는 크다고 해서 좋은것이 아닌 하나의 함수는 하나의 기능을 하는 소소익선이다.
[하나의 기능은 주관적인 기준이다]
함수의 입력과 출력: Printf함수도 반환을 한다.
우리가 이전에 배우듯 전달인자가 없거나 반환 값이 없는 경우가 있다.
이것을 통해서 우리는 “입력 또는 출력이 없는 함수도 존재한다(만들 수 있다)”
int main(void)
{
int num1, num2; //쓰레기값
num1 = printf("12345\n"); //6 12345+\n1
num2 = printf("I love my home\n"); //I love my home 11 V=3 \n=1 =15
printf("%d %d \n", num1, num2);
return 0;
}
이 예제를 실행해보면 Printf 함수에서 문자열의 길이를 반환한다.
num1에는 12345+ \n으로 총 6개의 문자열
num2에는 I()love()my()home\n으로 영어 11개 + \n 1개 + 띄어쓰기 4개로 총 16개의 문자열
전달인자의 유무와 반환 값 유무에 따라 함수는 나뉜다.
유형 1 : 전달인자가 있고, 반환 값이 있다.
유형 2 : 전달인자가 있고, 반환 값이 없다.
유형 3 : 전달인자가 없고, 반환 값이 있다.
유형 4 : 전달인자가 없고, 반환 값이 없다.
전달인자와 반환값이 모두 있는 경우
조건1)전달인자는 int형 정수 둘이며 이 둘을 이용한 덧셈진행
조건2)덧셈결과는 반환이 되며 따라서 반환형도 int형으로 선언한다.
조건3)함수이름은 Add라한다.
int add(int num1, int num2)
{
int result = num1 + num2;
return result; //result에 저장된 값을 Add함수를 호출 영역 전달
}
int add(int num1, int num2)
{
return num1 + num2;
}
int main(void)
{
int result;
result = add(3, 4); //변환 값을 대체됨 이때 int add를 실행함.
printf("덧셈결과1: %d\n", result);
result = add(5, 8); // 변화값 대체, 이때 int add다시 실행
printf("덧셈결과2: %d\n", result);
return 0;
}
여기서 return num1+num2; 처럼 결과를 바로 반환할 수 있으며
result=add(3,4)로 함수 호출문이 반환 값으로 대체될 수 있다.
즉 result = add(3,4)에서 위 int add가 한번 써지고
result = add(5,8)에서 int add가 한번 써지는 것이다.
전달인자나 반환 값이 존재하지 않는 경우
printf는 서식을 지정해야되기 때문에 빈번히 호출하기 번거롭기 때문에
서식지정을 매번 하지 않아도 되도록 함수를 정의한다.
(인자전달 O , 반환 X)
void showAddResult(int num) //인자전달 O 반환 X
{
printf("덧셈결과 출력: %d \n,num2);
}
void로 반환(출력)은 X int num으로 입력 인자전달은 O이다.
몸통을 보면 return이 없다. 반환 값이 없기 때문에
(인자전달 X, 반환 O)
int num ReadNum(void)
{
int num;
scanf("%d", &num);
return num;
}
호출하기 편리하도록 정의하는 것
(인자전달 X, 반환 X)
void HowtouseThisProg(void)
{
printf("두 개의 정수 입력 덧셈결과\n");
printf("두 개의 정수 입력하세요\n");
}
단순하게 메시지를 전달하기 때문에 인자의 전달과 값의 반환이 필요없다.
int add (int num1, int num2) // O O
{
return num1 + num2;
}
void showAddResult(int num) // X O
{
printf("덧셈결과 출력: %d \n", num);
}
int ReadNum(void) // O X
{
int num;
scanf("%d", &num);
return num;
}
void HowToUseThisProg(void) // X X
{
printf("두 개 정수 입력시 덧셈출력\n");
printf("두 개 정수 입력하세요. \n");
}
int main(void)
{
int result, num1, num2;
HowToUseThisProg(); //printf출력
num1 = ReadNum(); //num1에 ReadNum 값 대입 12
num2 = ReadNum(); //num2에 ReadNum 값 대입 24
result = add(num1, num2); //결과 ADD 함수이용
showAddResult(result); //값 출력 printf
return 0;
}
위에서 사용한 모든 경우의 수로 코드를 만든 것이다.
이를 통해서 좀 더 자세히 알 수 있다!
return이 지니는 두가지 의미 중 한 가지 살리기
리턴문은 두 가지가 의미가 있다.
함수를 빠져나간다
값을 반환한다.
간혹 반환형이 void로 선언된 함수에 못쓰는건가? 할 수 있지만 쓸 수 있다.
void NoReturnType(int num)
{
if (num < 0)
return; //반환하지 않는 리턴문 - 값 출력X
....
}
return ;값을 반환하지 말고 빠져나가라.
함수를 빠져나간다는 의미를 지정했다.
함수의 정의와 그에 따른 원형의 선언
함수의 위치를 결정할 때에도 주의를 기울여야 한다.
int Increment(int n)
{
n++;
return n;
}
int main(void)
{
int num = 2;
num = Increment(num);
return 0;
}
//가능
int main(void)
{
int num = 2;
num = Increment(num);
return 0;
}
int Incremnet(int n)
{
n++;
return n;
}
//불가능
가능은 위에서 함수를 알려주었기 때문에 메인이 읽을 수 있지만
불가능은 위에서 함수가 없기 때문에 인지하지 못한다.
int Increment(int n);; //생략가능 (int)
int main(void)
{
int num = 2;
num = Increment(num);
return 0;
}
int Increment(int n)
{
n++;
return n;
}
이렇게 먼저 main함수에게 밑에 int Increment라는 함수가 있다는 것을 귀띰해준다.
그러면 아 뒤에 있겠구나 ok하고 읽을 수 있게 된다.
int Incremnet(int)로 매개변수의 이름을 생략할 수도 있다.
다양한 종류의 함수 정의하기
“하나의 함수 내에 둘 이상의 리턴문이 존재하는 경우”
int numCompare(int num1, int num2);
int main(void)
{
printf("3과 4중 큰 수 %d\n", numCompare(3, 4));
printf("7과 2중 큰 수 %d\n", numCompare(7, 2));
}
int numCompare(int num1, int num2)
{
if (num1 > num2)
return num1;
else
return num2;
}
완전하지 못하지만 int numCompare에 리턴문이 2개가 들어가있다.
이 예제를 통해 retrun문이 실행되면 값을 반환하면서 빠져나간다. 를 알 수 있었다.
int AbsoCompar(int num1, int num2); //절대값 큰것
int GetAbsoValue(int num); //전달인자 절대값
int main(void)
{
int num1, num2;
printf("두 개의 정수 : ");
scanf("%d %d", &num1, &num2);
printf("%d와 %d중 절댓값이 큰 정수 : %d \n", num1, num2, AbsoCompar(num1, num2)); //비교호출
return 0;
}
int AbsoCompar(int num1, int num2) //큰 값 비교 함수
{
if (GetAbsoValue(num1) > GetAbsoValue(num2)) //절대값 호출
return num1;
else
return num2;
}
int GetAbsoValue(int num) //절대값 함수
{
if (num < 0)
return num * (-1);
else
return num;
}
절대값이 큰 수를 알려주는 프로그램이다.
음수를 알려주면 int GetAbsoValue에서 음수면 음값을 곱해주어 양수로 교환해준다.
int AbsoCompar에서는 절대값을 비교하여 옳은 값을 리턴(반환)해준다.
문제 [다양한 함수 정의하기]
문제1:
세 개의 정수를 인자로 전달받아 그 중 가장 큰 수를 반환하는 함수와 가장 작은 수를 반환하는 함수를 정의하자.
이 함수들을 호출하는 main함수도 작성해보자.
int upper(int num1, int num2, int num3); //큰 수
int lower(int num1, int num2, int num3); //작은 수
int checker(int check); //체크 수
int main(void)
{
int num1, num2, num3; //
int check;
printf("비교할 3개의 정수 입력: \n");
scanf("%d %d %d", &num1, &num2, &num3);
printf("1. 큰 수 반환 2.작은 수 반환 입력\n");
scanf("%d", &check);
//조건문으로 큰수를 할지 작은 수를 할지 지정
if (check == 1)
printf("선택한 수는 %d 큰 수는 %d 입니다\n", check, upper(num1, num2, num3));
else if (check == 2)
printf("선택한 수는 %d 작은 수는 %d 입니다\n", check, lower(num1, num2, num3));
else
printf("잘못된 선택입니다\n");
return 0;
}
//큰수 지정 함수
int upper(int num1, int num2, int num3)
{
if (num1 > num2) //두 개 비교
return (num1 > num3) ? num1 : num3; //나머지 비교
else
return (num2 > num3) ? num2 : num3;
}
//작은수 지정 함수
int lower(int num1, int num2, int num3)
{
if (num1 > num2) //두 개 비교
return (num2 > num3) ? num3 : num2; //나머지 비교
else
return (num1 > num3) ? num3 : num1;
}
//1와 2 둘중 어느 값이 왔는지 들어왔는지 체크
int checker(check)
{
if (check>0)
return 1;
else
return 0;
}
문제2:
섭씨온도를 입력하면 화씨온도로 변환하는 CelToFah이라는 함수와
그 반대로 화씨 온도를 입력하면 섭씨를 반환하는 FahToCel이라는
이름의 함수를 정의하고 호출하는 예제를 완성하자.
Fal=1.8 X cel +32
double CelToFah(double c) //실수형 섭씨 -> 화씨
{
return 1.8 * c + 32;
}
double FahTocel(double f) // 실수형 화씨 -> 섭씨
{
return (f - 32) / 1.8;
}
int main(void)
{
int num;
double num2;
printf("1.섭씨를 화씨로 2.화씨를 섭씨로 \n");
scanf("%d", &num);
if (num == 1)
{
printf("섭씨를 입력해주세요: \n");
scanf("%lf", &num2);
printf("변환된 화씨는 %f 입니다 \n", CelToFah(num2));
}
else if (num == 2)
{
printf("화씨를 입력해주세요: \n");
scanf("%lf", &num2);
printf("변환된 섭씨는 %f 입니다 \n", FahTocel(num2));
}
else
printf("잘못된 선택입니다\n");
return 0;
}
문제3:
인자로 전달된 수 만큼의 피보나치 수열을 출력하는 함수를 정의하자.
5를 입력시 0에서부터 시작해서 5개의 피보나치 수열을 출력한다.
void Fibo(int num) //피노나치 수열
{
int n1 = 0, n2 = 1, n3, n;
if (num == 1)
printf("%d", n1);
else
printf("%d %d ", n1, n2);
for (n = 0; n < num - 2; n++)//0시작 / 입력한수-2까지 / 1증가
{
n3 = n1 + n2; //n3 = 앞두개 +
printf("%d ", n3);
n1 = n2;
n2 = n3; // 0 1 1 2 3
}
}
int main(void)
{
int num;
printf("출력하려는 피보나치 개열 수: ");
scanf("%d", &num);
if (num < 1)
{
printf("피보나치는 1부터 가능합니다 \n");
return -1;
}
Fibo(num);
return 0;
}
지역변수
함수 내에서만 존재 및 접근 가능한 것을 지역변수라 한다.
중괄호 {} 내에 선언되는 변수는 모두 지역변수다.
int SimpleFuncOne(void)
{
int num = 10; // 이후부터 SimpleFuncOne의 num 유효 = 지역변수
num++;
printf("SimpleFuncOne num: %d \n", num);
return 0; // SimpleFuncOne의 num 유효한 마지막 문장
}
int SimpleFuncTwo(void)
{
int num1 = 20; // 이후부터 num1 유효
int num2 = 30; // 이후부터 num2 유효
num1++, num2--;
printf("num1 & num2: %d %d \n", num1, num2);
return 0; // num1, num2 유효한 마지막 문장
}
int main(void)
{
int num = 17; // 이후부터 main의 num 유효
SimpleFuncOne();
SimpleFuncTwo();
printf("main num: %d \n", num);
return 0; // main의 num이 유효한 마지막 문장
}
이름이 같은 변수를 볼 수 있는데
지역변수는 중괄호 내에서만 영향을 끼치기 때문에 문제 없다.
지역변수는 휘발성처럼 해당 지역을 벗어나면 자동으로 소멸한다.
지역변수를 stack메모리 영역에 할당된다.
main -> Funcone => Functwo -> main
[main] -> [main Funcone] => [main Funone Functwo] => [main]이다.
즉 SimpleFunOne이 열번 호출된다고해서 변수가 10번 할당되는게 아니라
휘발되고 할당 휘발되고 할당의 반복이다.
다양한 형태의 지역변수
지역변수는 반복문이나 조건문에도 선언이 가능하다.
int main(void)
{
int cnt; //count
for (cnt = 0; cnt < 3; cnt++)
{
int num = 0; //지역변수
num++;
printf("%d번째 반복, 지역변수 num은 %d. \n", cnt + 1, num);
}
if (cnt == 3)
{
int num = 7;
num++;
printf("if문 내에 존재하는 지역변수 num은 %d. \n", num);
}
return 0;
}
for문안에 지역변수가 있는데 이러면 반복되는 동안 누적일까?
아니다. 위에 말한것과 같이 휘발 되고 할당의 반복이다.
하지만 반복문내 변수선언은 되도록 지양하자.
int main(void)
{
int num = 1; //지역변수 1 (1)
if (num == 1)
{
int num = 7; //주석과 미주석 차이 (2)
num += 10;
printf("if문 내 지역변수 num: %d \n", num);//(2)
}
printf("main 함수 내 지역변수 num: %d \n", num); //(1)
return 0;
}
if문 내에서는 main에 있는 num이 가려지기 때문에
실행하면 지역변수가 달라진다.
지역변수의 일종의 매개변수다.
매개변수의 조건은
선언된 함수 내에서만 접근이 가능
선언된 함수가 반환시 지역변수와 마찬가지로 소멸된다.
지역변수의 큰 테두리안에 매개변수가 있다는 느낌이다.
전역변수
전역변수는 이름처럼 전역에 접근가능한 변수로서 중괄호 내에 선언되지 않는 특징이 있다.
void Add(int val);
int num; // 전역변수는 기본 0으로 초기화됨
int main(void)
{
printf("num: %d \n", num);
Add(3);
printf("num: %d \n", num);
num++; //전역변수 num 영향
printf("num: %d \n", num);
return 0;
}
void Add(int val)
{
num += val;
}
int num;이 아무런 중괄호에 들어가지 않았는데 이는 전역변수이다.
프로그램 시작과 동시에 메모리 공간에 할당되어 종료 시 까지 존재
초기화를 하지 않으면 0이 디폴트
전체영역 어디든 접근이 가능
전역변수와 동일한 이름의 지역변수가 선언되면?
“해당 지역의 전역변수는 가려지고, 지역변수의 접근이 이루어진다.”
우선권이 지역변수 > 전역변수 라는 것이다.
int Add(int val);
int num = 1; //전역변수
int main(void)
{
int num = 5; //지역변수
printf("num: %d \n", Add(3)); //add 진행
printf("num: %d \n", num + 9); // 지역값 +9
return 0;
}
int Add(int val)
{
int num = 9;
num += val;
return num;
}
하지만 변수의 이름은 되도록 중복이 되지않게하자.
추가적으로, 전역변수는 최대한 적게 사용하는 것이 좋다.
전역변수는 많은 기능이 배포되어 있기 때문에, 수정하면 할 것이 많아지고 유지보수가 힘들다.
즉 프로그램이 복잡도가 높아지고 좋은 구조가 아니게 되기 때문.
지역변수에 static 선언을 추가해서 만드는 static변수
static선언이 들어가면 전역변수를 지역변수로 만들어버리는 것이다.
void SimpleFunc(void)
{
static int num1 = 0; //전역변수의 static를 사용하여 안으로 넣음 초기화 0
int num2 = 0;
num1++, num2++;
printf("static: %d, local: %d \n", num1, num2);
}
int main(void)
{
int i;
for (i = 0; i < 3; i++)
SimpleFunc(); //얘가 호출하면 static은 보이지 않는다.
return 0;
}
일단 전역변수랑 성격이 같다.
초기화 되지 않으면 0이고, 시작과 동시에 종료까지 할당된다.
하지만 static을 통해 해당 전역변수의 영향력을 지역변수처럼 만들 수 있다.
static 지역변수는 써도 되는가?
정답은 Yes다.
static지역변수는 전역변수로 쉽게 대체가 가능하기 때문이다.
전역변수의 단점을 static으로 어느정도 해결한셈이다.
register변수
오늘날에는 성능이 좋아져서 잘 사용하지 않는다고 한다.
CPU에는 ALU산술연산장치라는 것이 있어 그안에 레지스터가 있는데
레지스터에게
“나 이거 자주쓰니까 알아둬”정도만 귀띰해주는 것이다.
int simple(void)
{
register(int num = 3);
}
전역변수에는 register를 사용할 수 없으며,
허용해도 의미가 없다고 한다.
문제 09-2 static변수 활용
int total = 0;
int AddToTotal(int num)
{
total += num;
return total;
}
int main(void)
{
int num, i;
for (i = 0; i < 3; i++)
{
printf("입력%d: ", i + 1);
scanf("%d", &num);
printf("누적: %d \n", AddToTotal(num));
}
return 0;
}
//이 프로그램을 static으로 변환
이것을 보면 전역변수 int total을 사용한 것인데
이 전역변수는 AddToTotal에만 국한되어있는 것을 알 수 있어
그 안에 넣어주면 된다.
int AddToTotal(int num)
{
static int total = 0;
total += num;
return total;
}
int main(void)
{
int num, i;
for (i = 0; i < 3; i++)
{
printf("입력%d ", i + 1);
scanf("%d", &num);
printf("누적: %d \n", AddToTotal(num));
}
return 0;
}
재귀함수
재귀는 자기 자신을 다시 한번 호출하는 함수이다.
void Recursive(void)
{
printf("Recursive call! \n");
Recursive( );
}
위에 Recusive함수가 있지만 그 안에 다시 Recursive를 부르게 되듯
자기 자신을 호출하는 것이다.
일단 완료되지 않아도 함수를 호출 할 수 있다.
함수를 다시 불러낸다는 개념보다는
“함수를 복사본을 만들어서 실행시킨다” 라는 개념이다.
무한반복을 하면 안되니 탈출조건
void Recursive(int num)
{
if (num <= 0)
return; //조건 탈출
printf("Recursive call! %d \n", num);
Recursive(num - 1);
}
int main(void)
{
Recursive(3);
return 0;
}
탈출 조건이 없으면 계속 복사만 하게 되니 탈출조건을 심어준다.
num값은 1씩 감소하되 0보다 작거나 같으면 탈출한다.
복사본 기준으로 생각하면
호출은 정방향이고 반환은 역반환이다.
재귀함수의 사례
팩토리얼값 n!=nx(n-1)... => n!=nx(n-1)!
팩토리얼 값을 반환하는 함수를 재귀적 구현
if (n >= 1)
return n * Factorial(n - 1);
if (n == 0)
return 1;
else
return n * Factorial(n - 1);
1이상이면 n x n(n-1)
0이면 1반환
그외는 n x n (n-1)
3가지가 나오는데 이러면 2개로 묶기가 된다.
if(n==0)
return 1;
else
return n*Factorial(n-1);
int Factorial(int n) //팩토리얼 진행 기능
{
if (n == 0)
return 1;
else
return n * Factorial(n - 1);
}
int main(void)
{
printf("1! = %d \n", Factorial(1));
printf("2! = %d \n", Factorial(2));
printf("3! = %d \n", Factorial(3));
printf("4! = %d \n", Factorial(4));
printf("9! = %d \n", Factorial(9));
return 0;
}
이렇게 이번에는 함수에 대해서 알아보았다
함수의 정의 ,재귀함수, 지역변수, 전역변수 등 기억하자
-
백준 C언어 단계별 풀이
백준 C언어 단계별 풀이 : 입출력과 사칙연산
printf를 넣으면 오류가 떠서
visual studio로 일단 컴파일후 확인용 printf문은 제거했다.
C언어 [문제번호 : 2557] => Hello World
문제 : Hello World!를 출력하시오.
#include <stdio.h>
int main(void)
{
printf("Hello World!");
return 0;
}
C언어 [문제번호 : 1000] => A+B
문제 : 두 정수 A와 B를 입력받은 다음, A+B를 출력하는 프로그램을 작성하시오.
입력 : 첫째 줄에 A와 B가 주어진다. (0 < A, B < 10)
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main(void){
int a, b;
scanf("%d %d", &a, &b);
printf("%d", a + b);
return 0;
}
두 정수 입력 => shortint , int ,long int , long long 중 연산이 빠른 int형
두 개의 정수 변수 = a,b 지정
입력 받는 scanf 로 변수 주소 값 지정
a+b값을 출력한다.
C언어 [문제번호 : 1001] => A-B
문제: 두 정수 A와 B를 입력받은 다음, A-B를 출력하는 프로그램을 작성하시오.
입력 : 첫째 줄에 A와 B가 주어진다. (0 < A, B < 10)
#include <stdio.h>
int main(void){
int a,b;
scanf("%d %d",&a,&b);
printf("%d",a-b);
return 0;
}
1000번과 유사핟. a+b a-b의 차이..
C언어 [문제번호 : 10998] => AxB
문제 : 두 정수 A와 B를 입력받은 다음, A×B를 출력하는 프로그램을 작성하시오.
입력 : 첫째 줄에 A와 B가 주어진다. (0 < A, B < 10)
출력 : 첫째 줄에 A×B를 출력한다.
#include <stdio.h>
int main(void){
int a,b;
scanf("%d %d",&a,&b);
printf("%d",a*b);
return 0;
}
사실 이것도 a+b, a-b, a*b의 차이다.
C언어 [문제번호 : 1008] => A/B
문제 : 두 정수 A와 B를 입력받은 다음, A/B를 출력하는 프로그램을 작성하시오.
입력 : 첫째 줄에 A와 B가 주어진다. (0 < A, B < 10)
출력 : 첫째 줄에 A/B를 출력한다. 실제 정답과 출력값의 절대오차 또는 상대오차가 10-9 이하이면 정답이다.
0.33333333333333333333333333333333
#include <stdio.h>
int main(void){
int a,b;
scanf("%d %d",&a,&b);
printf("%.9f",(double)a/b);
return 0;
}
\(기존과 다른 점은 출력에 10^{-9} 상대 오차라는 것이 생긴 것이다.\)
소수점을 표현해야되는 것으로 일단 입력은 정수형으로 받아야하니
int a,b를 지정해준다.
그리고 그것에 대한 scanf를 진행 한다.
그리고 출력할때 실수형으로 변경해주는데 여기서 (double)a/b로 하며
상대오차가 -9이니까 ”%.9f” 를 지정해준다. (소수점 9자리까지 출력)
C언어 [문제번호: 10869] => 사칙연산
문제 : 두 자연수 A와 B가 주어진다. 이때, A+B, A-B, A*B, A/B(몫), A%B(나머지)를 출력하는 프로그램을 작성하시오.
입력 : 두 자연수 A와 B가 주어진다. (1 ≤ A, B ≤ 10,000)
출력 : 첫째 줄에 A+B, 둘째 줄에 A-B, 셋째 줄에 A*B, 넷째 줄에 A/B, 다섯째 줄에 A%B를 출력한다.
입력
7 3
출력
10
4
21
2
1
#include<stdio.h>
int main(void){
int a, b;
scanf("%d %d\n",&a,&b);
printf("%d\n",a+b);
printf("%d\n",a-b);
printf("%d\n",a*b);
printf("%d\n",a/b);
printf("%d\n",a%b);
return 0;
}
정수형 자연수 범위가 1~10,000 이니까 int형을 쓴다.
쓰고 기존에서
printf형에서 (+ , - , * , /, %)을 출력한다.
C언어 [문제번호: 10926] => ??!
문제 : 준하는 사이트에 회원가입을 하다가 joonas라는 아이디가 이미 존재하는 것을 보고 놀랐다.
준하는 놀람을 ??!로 표현한다. 준하가 가입하려고 하는 사이트에 이미 존재하는 아이디가 주어졌을 때, 놀람을 표현하는 프로그램을 작성하시오.
입력 : 첫째 줄에 준하가 가입하려고 하는 사이트에 이미 존재하는 아이디가 주어진다. 아이디는 알파벳 소문자로만 이루어져 있으며, 길이는 50자를 넘지 않는다.
출력 : 첫째 줄에 준하의 놀람을 출력한다. 놀람은 아이디 뒤에 ??!를 붙여서 나타낸다.
입력 name
출력 name??!
#include <stdio.h>
int main(void){
char str[51];
scanf("%s",str); //%s입력받음
printf("%s??!", str); //%s에 ??! 받음
return 0;
}
이번에는 아이디를 주어진다. 그러면 문자형 정수 char을 써준다.
str[51] 이라는 길이가 51인 배열이다. (왜냐면 길이는 50자를 넘지않아야 하기에 마지막값은 null이니 51이면 50이다)
[n] [a] [m] [e] 이렇게 넣어지는 것이다.
scanf에 이 문자 배열을 넣어주고
출력에는 뒤에 ??!을 붙혀준다.
C언어 [문제번호: 18108] => 1998년생인 내가 태국에서는 2541년생?!
문제 : ICPC Bangkok Regional에 참가하기 위해 수완나품 국제공항에 막 도착한 팀 레드시프트 일행은 눈을 믿을 수 없었다. 공항의 대형 스크린에 올해가 2562년이라고 적혀 있던 것이었다.
불교 국가인 태국은 불멸기원(佛滅紀元), 즉 석가모니가 열반한 해를 기준으로 연도를 세는 불기를 사용한다. 반면, 우리나라는 서기 연도를 사용하고 있다. 불기 연도가 주어질 때 이를 서기 연도로 바꿔 주는 프로그램을 작성하시오.
입력 : 서기 연도를 알아보고 싶은 불기 연도 y가 주어진다. (1000 ≤ y ≤ 3000)
출력: 불기 연도를 서기 연도로 변환한 결과를 출력한다.
#include <stdio.h>
int main(void){
int num;
scanf("%d", &num);
printf("%d", num-543);//불기를 서기로
return 0;
}
1998년생이 2541이니 2541 - 1998을 해주면 543이 나오니 서기와 불기는 543의 차이가 난다.
불기를 입력하면 서기로 변환해야되니까 -543을 진행해주면 된다.
C언어 [문제번호: 10430] => 나머지
문제 : (A+B)%C는 ((A%C) + (B%C))%C 와 같을까?
(A×B)%C는 ((A%C) × (B%C))%C 와 같을까?
세 수 A, B, C가 주어졌을 때, 위의 네 가지 값을 구하는 프로그램을 작성하시오.
입력: 첫째 줄에 A, B, C가 순서대로 주어진다. (2 ≤ A, B, C ≤ 10000)
출력: 첫째 줄에 (A+B)%C, 둘째 줄에 ((A%C) + (B%C))%C, 셋째 줄에 (A×B)%C, 넷째 줄에 ((A%C) × (B%C))%C를 출력한다.
#include <stdio.h>
int main(void) {
int a, b, c;
scanf("%d %d %d", &a, &b, &c);
printf("%d\n",(a + b) % c);
printf("%d\n",((a % c) + (b % c)) % c);
printf("%d\n",(a * b) % c);
printf("%d\n",((a % c) * (b % c)) % c);
return 0;
}
세 정수 변수 a,b,c를 지정해준다.
그 값을 scanf에 받는다.
총 넷째 줄이 필요하며 각 필요한 수식을 넣어주면 된다.
C언어 [문제번호: 2588] => 곱셈
문제 : (세 자리 수) × (세 자리 수)는 다음과 같은 과정을 통하여 이루어진다.
입력 : 첫째 줄에 (1)의 위치에 들어갈 세 자리 자연수가, 둘째 줄에 (2)의 위치에 들어갈 세자리 자연수가 주어진다.
출력 : 첫째 줄부터 넷째 줄까지 차례대로 (3), (4), (5), (6)에 들어갈 값을 출력한다.
#include <stdio.h>
int main(void) {
int a, b; // ---472, 385
scanf("%d %d", &a, &b); //정수 a b 받음 --- 1 , 2
printf("%d \n", a * (b%10)); //472*(385%10) = 472*385(5) 일의 자리추출 = 2360
printf("%d \n", a * (b%100/10)); //472*(385%100(85)/10) = 472*(8) 십의 자리 추출 = 3776
printf("%d \n", a * (b/100));//백의 자리 472*(385/100) = 472*3 = 1416
printf("%d", a * b);//전체적으로 곱한것 = 472*385 = 181.720
return 0;
}
입력은 세자리 자연수 2개 a, b를 받는다.
그것을 scanf로 받는다.
이에 출력을 하는데 (3)은 일의 자리 / (4)는 십의 자리 / (5)는 백의 자리 / (6) 총값이다
(3)은 일의 자리를 추출한다. 472*5를 표현해야된다.
`472*(385%10)을 해준다. 나머지를 구하는 것으로 10으로 짤라주면 나머지는 5가 남는다.
즉 472*5 = 2360이 표현된다.
(4)는 십의 자리를 추출한다. 472*80를 표현해야된다.
472*(385%100/10)을 해준다. 100을 먼저 짤라서 85를 받은후 10으로 나눠서 8이 된다
정수형이라서 소수점 값은 버린다.
즉 472*8 = 3776이 표현된다.
(5)는 백의 자리를 추출한다. 472*300을 표현해야된다.
472*(385/100)을 해준다. 100으로 나눠서 3값을 받는다.
즉 472*3 = 1416이 표현된다.
(6)은 총 값을 나타낸다.
그냥 472*385 = 181.720 이다.
C언어 [문제번호: 11382] => 꼬마정민
문제 : 꼬마 정민이는 이제 A + B 정도는 쉽게 계산할 수 있다. 이제 A + B + C를 계산할 차례이다!
입력 : 첫 번째 줄에 A, B, C (1 ≤ A, B, C ≤ 10^12)이 공백을 사이에 두고 주어진다.
출력 : A+B+C의 값을 출력한다.
<오답코드>
int main(void) {
int a, b, c;
printf("값을 설정: ");
scanf("%d %d %d", &a, &b, &c);
printf("%d", a + b + c);
return 0;
}
어 왜 안되는거지? 라고 생각했는데 함정카드가 있었다.
1 ≤ A, B, C ≤ 10^12 간과한 것이다.
정수형 데이터는 shortint / int / longint / longlong 4가지가 있는데.
int형은 4byte를 쓴다. (1byte= 8bits)
크기는 약 4*8 = 32bits => 2^32의 크기다.
여기서 ^1은 정수구분으로 써야되니 한 개를 빼준다.
2^31는 구하기 어려우니 2^10 x 2^10 x 2^10 x 2^1 = 1024 x 1024 x 1024 x 2이다.
= 2,147,483,648 = 약 20억 이라는 값이 나온다.
그러면 이것은 10^n으로 구분해야되는데..
n이 1씩 올라갈때마다 0이 한개씩 늘어나니까
10^9 = 10억 *2정도가 되는 것이다.
= 20억의 공간에서 +만 쓰게 되면 40억이 되지만
10^12인 1조라는 수치에는 못 미친다
즉 10^12를 2^n으로 하면 2^40이상 이어야 한다는 것이다.
40bits이상인 정수형태를 쓰면 되니까 40/8 = 5
즉 5byte이상의 형태를 써야한다.
longint는 시스템 컴파일러에 다르기 때문에 제외하기에 longlong을 쓴다.
longlong = 8byte로 공간이 남게 된다.
입력 출력은 %lld 로 받는다.
#include <stdio.h>
int main(void) {
long long a, b, c;
scanf("%lld %lld %lld", &a, &b, &c);
printf("%lld", a + b + c);
return 0;
}
이렇게 longlong 변수 와 입력 값
출력 값을 지정해주면 된다.
C언어 [문제번호: 10171] => 고양이
문제 : 아래 예제와 같이 고양이를 출력하시오.
\ /\
) ( ')
( / )
\(__)|
이거는 이스케이프 시퀀스를 물어보는 것이다.
이것을 참고하면 지금 필요한 것은 백 슬래시 출력임을 알 수 있다.
#include <stdio.h>
int main() {
printf("\\ /\\\n");
printf(" ) ( ')\n");
printf("( / )\n");
printf(" \\(__)|\n");
return 0;
}
띄어쓰기를 맞춰준 후
백 슬래시가 출력되는 곳 앞에 백슬래시를 한개씩 더 붙혀주면 된다.
여기서 띄어쓰기 때문에 상당히 고통받았다..ㅋㅋ
C언어 [문제번호: 10172] => 개
문제: 아래 예제와 같이 개를 출력하시오.
|\_/|
|q p| /}
( 0 )"""\
|"^"` |
||_/=\\__|
이스케이프 시퀀스를 물어보는 문제다.
여기서 필요한것은 "의 이스케이프 시퀀스 /의 이스케이프 시퀀스 `의 이스케이프 시퀀스다.
int main() {
printf("|\\_/|\n");
printf("|q p| /}\n");
printf("( 0 )\"\"\"\\\n");
printf("|\"^\"` |\n"); //문제구간
printf("||_/=\\\\__|");
return 0;
}
여기서 복붙에 고통을 받았다.
|"^"` | 이 부분을 복사를 하니 |" ^ " ` | 이렇게 되서 띄어쓰기 오류가 난것.
일단 복붙시 기본적으로 주석에 복붙해서 끌어와야될듯하다.
C언어 단계별로 풀어보기
입출려과 사칙연산을 풀어보았는데 직접 문제를 풀어보니 문제들이 있고 해결하는 과정이 재미있었다.
언젠간 이 과정이 나를 개발자로 만들어 줄 수 있겠지!
생각하는 과정이 재미있으니 더 힘내자
-
[C언어의 기본] 반복문과 조건문
반복실행을 명령하는 반복문
이제부터는 기본이 아니라 약간 응용의 영역이라고 한다. 시작해보자!
while문에 의한 문장의 반복
반복문이란? : 문장을 열번 실행해주세요. 했을 때 문장을 열번 하는게 아닌 명령을 내리는 것
반복문의 종류) while문, do~while문, for문
일단 반복문은 많이 연습해서 머슬메모리처럼 몸이 반응되는 정도로 해야된다.
1.이해하기 : 예제중심으로 코드를 옮길 수 있을 정도로 이해
2.문제풀기 : 예제문제는 거의다 비슷하니까 문제제작해서 구현해보기
while문은 반복을 명령하는 문장
while(num<3) //while(조건)
{
printf("Hello world! \n");
num++;
}
while문을 선언 후 괄호 안에 조건을 적어낸다.
그러면 조건만큼 반복된다.
여기서는 Hello World 출력 후 num값을 1올려준 후 num값이 3보다 작을때 까지 반복한다.
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main(void) //출력은 int + main함수 + 입력은 void
{
int num = 0; //int형 변수 num 선언 및 0으로 초기화
while (num < 5) // while ~동안 (조건) => 조건이 유지되는 동안 반복한다. 여기서는 num값이 5보다 작으면 반복 5보다 크거나 같으면 중단
{
printf("Hello world! %d \n", num); // 조건이 참 True가 반환될 경우 위 값을 수행한다.
num++; // num ++이기때문에 num +1을 하는데 이 행을 벗어날 때 num값을 +1을 한다.
}
return 0;
}
num값은 0부터 시작하여 while문을 방문하여 조건 확인 후 True면 printf문 실행 후 num++을 하여 다시 while문으로 향하면서 num값에 +1을 해준다.
만약에 여기서 num++가 없으면 무한으로 반복된다. ‘무한루프’가 구동되는 것이다.
반복문에서 중요한 것은 반복 조건을 무너뜨리는 최소한의 연산이다.
반복의 대상이 하나의 문장이면 괄호는 생략이 가능
while(num<5)
printf("Hi %d \n", num++);
값의 라인이 한 개 있을 때는 생략이 가능하다는 것이며 하지만 오히려 줄인다고 해서 좋은건 아닌 듯하다.
가독성도 하나의 고려사항이라서..
while문의 흐름
int main(void)
{
int num=0;
while(num<3)
{
printf("Hi %d \n", num);
num++;
}
...
}
순서를 보면 순차적으로 내려오면서 변수 num이 0으로 초기화 된 후
반복문 while에 진입하게 되며 조건인 num값이 3보다 작기에 Hi를 출력하고 num값을 출력
그리고 num값이 +1이 되고 다시 while로 올라가면서 num값이 +1이 된다.
그리고 다시 반복문에 들어가서 반복된다.
num이 3이 되면 조건 불충분으로 반복하지 않고 밑으로 내려간다.
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main(void)
{
int dan=0, num=1;
printf("몇 단을 출력하나요?: ");
scanf("%d", &dan);
while(num<10)
{
printf("%dx%d=%d \n", dan, num, dan*num);
num++;
}
return 0;
}
이것은 내가 만약 구구단 5단을 보고싶으면 5을 입력하면
dan 변수에 내가 입력한 값으로 주소 대입
num은 초기화 된 1값부터 9가 될때까지 반복하는 조건이다.
그 안에는 반복이 되면 5단x 1~9 = 5*1~9 출력이 된다.
이는 순서도가 있지만 여기서는 삽입하지 않도록 하겠다.
무한루프
while (1) //항상 True
{
printf("%dx%d=%d \n", dan, num, dan*num);
num++;
}
while(조건)에 1값인 True를 넣어주게 되면
이 조건은 항상 True가 되어 무한으로 반복되는 상황이 나온다.
이럴때는 break; 문을 통하여 빠져나갈 수 있고
특정기능을 사용하기 위해서 일부러 무한루프를 사용하는 경우가 있다고 한다.
while문 연습 문제
1.프로그램 사용자로부터 양의 정수 하나를 받아 그 수만큼을 “Hello world”를 출력하는 프로그램의 제작
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main(void)
{
int num; //변수 num 입력받는 정보를 선언
int standard = 0; // 변수 0 기준이되는 변수 standard 변수를 선언
printf("정수를 입력해주세요. \n");
scanf("%d", &num);
while (standard<num) //5를 입력하면 5보다 작은 경우 출력 조건식은 0을 기준으로 0<5다.
{
printf("Hello world!\n"); //반복명령 Hello World를 출력한다.
standard++; //1회 반복후 다시 while로 들어가면서 standard 변수에 +1을 해준다.
}
return 0;
}
2.프로그램 사용자로부터 양의 정수를 하나 입력 받은 후, 그 수 만큼의 3의 배수를 출력하는 프로그램을 작성하라.
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main(void)
{
int num=0; //양의 정수를 입력받는 값
int dan=0; //3의 배수를 출력해야되니 3단인 dan변수 지정 및 1초기화
printf("정수를 입력해주세요\n"); //입력받을 변수 요청 출력
scanf("%d", &num); //입력받은 변수를 num으로
while (dan++ < num) //조건문 dan은 이행 후 1이 증가함. num보다 작은 조건 반복
{
printf("%d\n", 3 * dan); // 출력한다. 3*dan의 수만큼 1부터 시작.
}
return 0;
}
변수 num와 dan을 초기화 시켜준 후 정수를 입력받는다. 그 후 그 값을 num 값에 대입
반복문을 실행한다. 조건문에서 dan은 조건문을 이행 후 1이 증가하고 num보다 작은 조건을 반복한다.
dan은 1부터 시작한다. 왜냐하면 dan++에서 printf로 넘어가면 1이 증가하기 때문이다.
3*dan을 해준다 (3의배수)
그러면 3*내가 입력한 정수의 숫자만큼 출력이 된다.
아마 이쪽은 num과 dan값이 0이니
int num, dan=0;
으로 한줄을 더 줄일 수 있을 것이다.
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main(void)
{
int num = 0; //양의 정수를 입력받는 값
int dan = 1; //3의 배수를 출력해야되니 3단인 dan변수 지정 및 1초기화
printf("정수를 입력해주세요\n"); //입력받을 변수 요청 출력
scanf("%d", &num); //입력받은 변수를 num으로 저장하는데 &num+1을 하면 num변수 주소에 +1을 더한값으로 메모리 위치가 아닌 다른 메모리 위치에 저장되어 있다.
//그래서 변수 자체의 주소를 넘거야 하기 때문에 &num+1을 하기 보다는 하단 while 조건문의 num+1을 해준다.
while (dan < num +1) //dan보다 num이 크다는 조건
{
printf("%d\n", 3 * dan); // 출력한다. 3*dan의 수만큼 1부터 시작.
dan++; //dan이 1씩 증가되어감.
}
return 0;
}
입력받은 정수를 num값을 대입.
반복문을 실행하는데 조건은 dan보다 num+1이 클때까지
dan은 1씩 증가한다.
내가 3을 입력하면 dan은 1 num은 4기준으로 반복문이 진행된다.
즉 1~3이니 3번을 3*dan을 실행하기에 3 6 9 가 된다.
3.프로그램 사용자로부터 계속해서 정수를 입력 받는다. 그리고 그 값을 계속해서 더해 나간다. 이러한 작업은 사용자가 0을 입력할 때 까지 계속되며 0을 입력하면 입력된 모든 정수의 합을 출력하고 종료한다.
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main(void)
{
int all=0;
int num=1;
while (num != 0)
{
printf("정수를 입력해주세요(0이 나오면 종료합니다)\n");
scanf("%d", &num);
all += num;
}
printf("입력된 수의 합은 : %d \n", all);
return 0;
}
변수를 선언 후
조건문에서 num!=0 변수 num가 0이 아닐때 까지 반복한다.
while문안에 scanf가 있기 때문에 반복되고 all이라는 변수에 누적된다.
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main(void)
{
int all = 0; //합을 저장할 수 있는 변수
int num = 0; //입력받는 값의 수
int num2 = 0; //수를 담는 그릇
printf("더하고자 하는 값의 수를 입력하세요 \n");
scanf("%d", &num);
int i = 1; //반복 변수 지정
while (i <= num)
{
printf("%d번째 수를 입력해주세요: ", i);
scanf("%d", &num2);
all += num2; // 변수 all에 num2의 값을 더하고 그 값을 다시 all에 저장하는 것이다. all = all + num2.
i++;
}
printf("입력된 수의 합은: %d \n", all);
return 0;
}
비효율적인거 같지만 내가 수를 지정해서 저장하는 것을 한번 해봤다.
변수 all, num, num2를 지정하고 내가 더하고자 하는 값의 수를 입력한다.
반복변수를 넣어 while문에 내가 더하고자하는 값만큼 반복한다.
내가 더하고자한 값은 num2에 들어가고 all에 누적된 후 반복 변수가 1증가한다.
4.프로그램 사용자로부터 입력 받은 숫자에 해당하는 구구단을 출력하되, 역순으로 출력하는 프로그램을 작성해보자.
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main(void)
{
int num = 9;
int dan = 0;
printf("구구단 몇단을 출력하실건가요?\n");
scanf("%d", &dan);
while (num > 0)
{
printf("%d x %d = %d\n", dan, num, dan * num);
num--;
}
return 0;
}
num 변수를 9로 초기화하고, dan을 0으로 초기화한 후
내가 출력하고자 하고 싶은 구구단의 단을 입력하면 dan에 입력
반복문이 실행되며 조건은 num값이 0보다 클때다.
(dan) * (num) = dan *num 이 반복출력되며 1회 반복할때마다 num값은 1씩 줄어든다
5.프로그램 사용자로부터 입력 받은 정수의 평균을 출력하는 프로그램을 작성하되 다음 두 가지 조건을 만족한다.
“먼저 몇개의 정수를 입력할 것인지 묻고 그 수만큼 입력받는다.”
“평균 값은 소수점 이하까지 계산하여 출력한다”
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main(void)
{
int ea = 0; //입력받는 정수의 갯수
int num = 0; //정수의 입력
int all = 0; //모든 정수의 입력을 담는 그릇
int i = 1; //반복변수
printf("입력받을 정수의 개수를 입력해주세요 \n");
scanf("%d", &ea);
while (i < ea+1)
{
printf("%d 번째 정수를 입력해주세요 \n", i);
scanf("%d", &num);
i++;
all += num; //입력받은 모든 값을 all에 더해준다.
}
printf("입력받은 정수의 평균은 : %f \n", (double)all / ea);
return 0;
}
각각 변수들을 선언해준다.
그 후 내가 입력받을 정수의 개수를 입력 해준 후
갯수만큼 반복되며 모든 값을 all에 저장된다.
마지막에 평균값을 출력하는데 소수점 까지 출력해야되니까
int형인 all을 double 실수형으로 바꿔준 후 갯수만큼 나눠준다. 그후 출력 %f을 해준다.
while문의 중첩
while문의 중첩은 말 그대로 while문안에 마트료시카처럼 while문이 있는거다.
즉, 조건 반복 안에 조건 반복이 있는것이다.
2단부터 9단까지 출력하는데 반복문 중첩이 없으면 반복문을 8회 삽입해야되니 비효율적이다.
즉 중첩을 시키면 짧게 할 수 있다. (최적화가 좋다)
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main(void)
{
int cur = 2;
int is = 0;
while (cur < 10) //cur2부터 시작해서 9까지 반복 총 8회 반복한다.
{
is = 1; //새로운 단의 시작 is선언
while (is < 10) //is 1부터 9까지 9회반복
{
printf("%dx%d=%d \n", cur, is, cur * is); //출력한다. nxm=nm이다. n=2->9 , m=1~9, n*m
is++; //프린트 출력후 이 행을 벗어나면 is에 +1 다시 is=2로 간다.
}
cur++; //is가 10이 되면 cur +1을 진행하고 다시 9행으로 간다.
}
return 0; //이 행은 총 cur 8 * is 9 = 72개의 답을 얻을 수 있다.
}
cur2를 통해서 2단부터 시작한다. is는 0으로 초기화한다.
while문에서 큰 조건은 cur이 2부터 시작하여 9까지 반복을 하는것이니 2~9 => 8회 반복한다.
그 사이에 is=1;로 새로운 단의 시작을 적고
그 다음에 작은 조건 is가 10미만까지 반복이니 1~9까지 9회반복을 해준다.
is 9회 반복의 내용은 cur * is = cur *is를 9회 반복 해준 후
나가서 is가 10이 되면 빠져나와 cur이 1증가하는데 while(cur<10)으로 올라가면서
수가 1 증가한다.
그러면 총 cur 8 * is 9 = 72ea 답을 얻을 수 있다.
while문의 중첩 문제
프로그램 사용자로부터 총 5개의 정수를 입력 받아서, 그 수의 합을 출력하는 프로그램을 작성해보자.
조건!: 정수는 반드시 1 이상이어야한다. 1미만의 수가 입력되면 이를 인정하지 않고 재 입력을 요구한다.
결과적으로 1이상의 정수를 5개를 모두 입력 받아야한다.
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main(void) //while문의 중첩문
{
int num = 0; //입력받는 정수의 값을 받을 변수
int sum = 0; //합을 출력하는 변수
int ea = 0; //입력받는 변수의 수를 받는 변수
while (ea < 5) //개수가 0~4 즉 5개가 될때까지 반복한다.
{
while (num <= 0) //입력받는 변수가 0보다 작거나 같을때 반복한다. 즉 1미만의 수를 입력시 반복되는 구간
{
printf("입력받을 정수 (%d번째)\n", ea + 1); //입력받을 정수 EA+1번째
scanf("%d", &num); //num변수 주소저장
} //1미만이 들어오면 여기서 계속 루프된다.
sum += num; //sum= sum+num
num = 0; // num=0;
ea++; //여기까지 완료시 ea+1
}
printf("모든 값의 합은 : %d \n", sum);
return 0;
}
각 변수들을 0으로 초기화해준 후
반복문을 실행하는데
대반복문(ea<5) 0~4로 즉 5개가 될때까지 반복하게 된다.
소반복문(Num값이 0보다 작거나 같을 때 반복한다.) = 1미만 정수 입력 시
[만약 1미만이 들어오면 while문에서 안에서 계속 반복되기에 빠져나갈 수 없다.]
소 반복문을 통해서 정수를 입력받고 그것을 num으로 받아 sum에 누적시킨다.
그 후 num값을 0으로 초기화 (기록삭제)
그후 ea의 값을 한개 늘려준다.
그후 ea값이 4까지 반복해준다.
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main(void) //while의 중첩문 나의 방식
{
int num = 0; //받은 정수값
int sum = 0; //합의 정수값
int ea = 0; //받은개수의 정수값
int is = 0; //시작 개수의 정수값
while (ea <= 0) //입력받을 정수 값이 1미만일 경우 루프 생성
{
printf("입력받을 정수의 개수를 입력해주세요");
scanf("%d", &ea);
}
while (is < ea) // 0~n까지 내가 입력받은 정수만큼 반복
{
while (num <= 0) //입력받은 정수값이 1미만시 루프생성
{
printf("정수 값을 입력해주세요. (%d번째의 입력)\n", is + 1); //%d에 is+1번째 입력을 통해 몇번째인지 확인가능
scanf("%d", &num); //입력받은 값을 num에 저장
} //만약 1미만시 다시 while문으로 아니면 아래로 진행한다.
sum += num; //sum=sum+num 총합의 값을 저장한다.
num = 0; //저장된 num값을 초기화 시켜준다. 초기화 안하면 루프 값 유지됨.
is++; //정수값을 +1해준다.
}
printf("입력받은 정수는 %d개 이며, 총합은 %d 입니다.", ea, sum);
return 0;
}
변수를 4개 지정해준다. C언어는 순차적으로 실행되니
일단 1차 while문으로 정수 값이 1미만 이었을 떄의 루프를 먼저 생성해준다.
내가 입력하고 싶은 정수의 개수를 입력하되 1미만은 불가능
그후 새로운 반복문으로 내가 입력받은 정수반복 반복시킨다.
그 내부 반복문에는 내가 더하고자 하는 값이 1미만일때 반복을 시킨다.
내부 반복문이 반복되면서 sum에 정수 값을 누적 및
num의 초기화 밑 횟수+1을 해주며 입력받은 정수의 수만큼 반복한다.
그 후 입력받은 정수의 개수와 총합을 보여준다.
아래의 출력으로 보이는 프로그램을 작성해보자
*
ㅇ*
ㅇㅇ*
ㅇㅇㅇ*
ㅇㅇㅇㅇ*
참고로 5행에 걸쳐 출력이 이뤄지며, 행이 더해질때마다 ㅇ의 문자수가 증가한다.
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main(void)
{
int star_ea = 0;
int o_ea = 0;
while (star_ea < 5) //별의 갯수가 5개 이하면 반복한다.
{
while (o_ea < star_ea) //별의 개수보다 o의 개수가 적으면 반복한다.
//맨 첨은 0<0이 성립이 되지 않으니 while문 바로 종료
{
printf("o"); // o을 출력
o_ea++; // o갯수를 ++해준다.
}
o_ea = 0; //o갯수를 초기화
printf("* \n"); //별의 출력
star_ea++; //별 개수 카운트 1늘려준다.
}
return 0;
}
변수로 별의 개수 즉, 5행이니까 <5를 조건을 걸어준다. 별의 개수가 5개 이하면 반복 [ 0,1,2,3,4]
소 반복문에는 별의 개수보다 o이 적으면 반복하는 것이다.
맨 처음은 0=0이기에 바로 종료 후 *을 한개 출력 그후는 0<1이니 ㅇ이 한개 앞에 추가 되어 반복된다.
그러면 문제와 같은 답이 도출된다.
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main(void)
{
int star_ea = 0;
int o_ea = 0;
int ea = 0;
printf("몇 층으로 올리실 건가요?\n");
scanf("%d", &ea);
while (star_ea < ea) //별의 개수가 ea만큼 반복한다.
{
while (o_ea < star_ea) //o의 개수보다 *의 개수가 더 적으면 실행한다.
{
printf("o"); //먼저 o를 출력
o_ea++; //o값 +1
} //o값+1했는데 도 *보다 작은지 체크
o_ea = 0; //출력해준다.
printf("* \n"); //*출력
star_ea++; //star 카운트 +1
}
return 0;
}
요즘 응용해서 내가 원하는 층만큼 올려보는 거로 응용해봤다.
입력받은 층수만큼 별의 개수가 나올떄까지 반복하게 된다.
do~ while문에 의한 문장의 반복
do ~while문은 while문과 다른점은 ‘반복조건’을 뒤에 작성하는 것이다.
즉, 무조건 최소 한번 이상은 실행되는 구조다.
do
{
printf("Hello world! \n");
num++;
}while(num<3);
일단 한번 실행 후 조건만큼 반복한다.
그러다보니 순서만 바꾼것인데 while문과 do while문으로 서로 교체할 수 있다.
while(num<10)
{
printf("%d x %d = %d \n",dan,num,dan*num);
num++;
}
do
{
printf("%d x %d = %d \n",dan,num,dan*num);
num++;
}while(num<10);
자세히 보면 거의 순서의 차이라고 볼 수 있다.
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main(void)
{
int total = 0, num = 0;
do
{
printf("정수 입력(0 to Quit): ");
scanf("%d", &num);
total += num; //누적
} while (num != 0);
printf("합계 %d\n", total);
return 0;
}
이것처럼 일단 한번 입력한 후에 앞으로 더 입력할 수 있을지 없는지 결정을 지어야되는 경우 같을 때
do~while이 필요하다.
while문과 do~while문 문제
일단 한번 받고 값을 더 받을건지 안받을건지 결정하는 문제를 기반으로 재 구현해보자
변수 num값을 적절히 초기화해서 첫 번째 반복조건의 검사결과가 ‘참’이 되게 한다.
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main(void)
{
int total = 0, num = 1;
while (num != 0) //num값이 0인지 아닌지 체크
{
printf("정수 입력(0 to Quit): ");
scanf("%d", &num); //num값을 입력받음 주소 저장
total += num; //누적
} //0이 아니면 반복 루프구간
printf("합계 %d\n", total);
return 0;
}
변수 total과 num을 각각 0과 1로 초기화 해준다.
while문으로 num값이 0인지 아닌지 체크해주고 0아니면 반복문을 계속 루프한다.
0이 나오면 끝나고 합계를 알려준다.
while문에 진입하기 전에 프로그램 사용자로부터 값을 1회 입력 받게 한다.
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main(void)
{
int total = 0, num = 0;
printf("정수 입력하세요(0이면 종료): ");
scanf("%d", &num);
total += num; //0이면 최종값은 0으로 진행
while (num != 0) // 0을 받으면 그냥 아래
{
printf("정수 입력하세요(0이면 종료): ");
scanf("%d", &num);
total += num;
}
printf("합계는: %d \n", total);
return 0;
}
진입하기전에 일단 정수값을 받은 후 누적을 한번 시킨다.
그 후 입력 한 값이 0이면 누적값은 0으로 종료된다.
원래 코드가 do~ while이었는데 변경시키니 1회 입력이면 do ~while이 좋다는 것을 알았다.
0이상 100이하의 정수 중에서 짝수의 합을 출력하는 프로그램을 구현해보자.
단 do~while 기반이어야 하며, 덧셈의 결과는 2550 이어야한다.
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main(void)
{
int num = 0;
int sum = 0;
do //일단 실행한다.
{
sum += num; // 0부터 누적값을 구하기
num = num + 2; // num+2값을 해준다 0,2,4,....100
} while (num <= 100); //100까지 반복한다.
printf("합은 : %d \n", sum);
return 0;
}
변수를 초기화 후 일단 한번 실행을 시켜준다.
0부터의 누적값을 구한 후 num+2를 통해 짝수를 선별한다.
그 값을 100까지 반복하고 합을 나타낸다.
int main(void)
{
int num = 0;
int sum = 0;
while (num <= 100)
{
sum += num; // 0부터 누적값을 구하기
num = num + 2; // num+2값을 해준다 0,2,4,....100
}
printf("합은 : %d \n", sum);
return 0;
}
do ~while을 while문으로 바꾸면 순서처럼 이렇게 바뀌게 된다. 굉장히 쉽다.
int main(void)
{
int sum = 0;//합을 받을 정수
int start = 0;//시작 할 정수
int end = 0;//종료할 정수
printf("시작할 정수를 입력해주세요");
scanf("%d",&start);
printf("끝낼 정수를 입력해주세요");
scanf("%d", &end);
do
{
sum += start; //start부터 누적값 구하기
if (start % 2 == 0) //start가 짝수일때
start += 2; //start 값에 +2를 해준다.
else start += 2; //아니면 start에 2씩 누적한다.
} while (start <= end);
printf("총 합은 %d입니다.\n", sum);
return 0;
}
이번에는 내가 직접 범위를 지정하여 결과를 추출한다.
근데 모든 값이 2씩 누적이라 의미가 없는 것 같다.;
int main(void)
{
int sum = 0;
int start = 0;
int end = 0;
char num[10];
printf("시작 범위를 입력해주세요");
scanf("%d", &start);
printf("끝낼 범위를 입력해주세요");
scanf("%d", &end);
printf("짝수를 구할건가요?, 홀수를 구할건가요?: ");
scanf("%s", num);
if (strcmp(num, "짝수") == 0)
printf("짝수로 인식되었습니다.\n");
else if (strcmp(num, "홀수") == 0)
printf("홀수로 인식되었습니다.\n");
else
{
printf("값을 다시 확인하세요.");
}
int i = start;
while (i <= end)
{
if (strcmp(num, "짝수") == 0)
{
while (i <= end && i % 2 == 0)
{
sum += i;
i += 2;
}
}
else if (strcmp(num, "홀수") == 0)
{
while (i <= end)
{
if (i % 2 != 0)
{
sum += i;
}
i++;
}
}
else
{
printf("값을 다시 확인해주세요 \n");
return 0;
}
}
printf("%d ~ %d범위내에서 %s를 선택하여 총합은 %d입니다.\n", start, end, num, sum);
return 0;
}
이번에는 위의 작품에서 범위를 나타내고 짝수와 홀수에 따라서 값이 다르게 했다.
범위를 입력 후 짝수와 홀수를 선택하며 짝수가 홀수가 아니면 값을 다시 확인하라는 문구가 뜨게한다.
새로운 변수에 시작 범위 값을 넣어주고
짝수일시)그 값이 끝 범위보다 작거나 같고 2로 나눴을 때 0이 만족되면
start값을 누적시켜준다. 그후 +2를 더 누적시킨 후 끝 범위까지 반복한다.
홀수일시) 그 값이 끝 범위보다 작거나 같고 2로 나눴을 때 0이 아니면
start값을 누적시켜준다. 그후 +1를 더 누적시킨 후 끝 범위까지 반복한다
짝수도 홀수도 아니면 값을 다시 확인해달라고 안내가 뜨며 종료되고
조건이 만족하면 범위와 짝수홀수의 선택 그리고 총합을 보여준다.
while문의 중첩 예제를 do~while문으로 재구성해보자.
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main(void)
{
int cur = 2;
int is = 0;
do //while 자리에 do
{
is = 1;
do
{
printf("%dx%d=%d \n", cur, is, cur * is); //출력한다. nxm=nm이다. n=2->9 , m=1~9, n*m
is++; //프린트 출력후 이 행을 벗어나면 is에 +1 다시 is=2로 간다.
}while (is < 10)
cur++; //is가 10이 되면 cur +1을 진행하고 다시 9행으로 간다.
printf("\n");
}while(cur<10);
return 0; //이 행은 총 cur 8 * is 9 = 72개의 답을 얻을 수 있다.
}
while문의 위치에 do가 오고 괄호 끝에 while(조건)을 넣어준다.
for문에 의한 문장의 반복
일단 말하기 전에 반복문은 크게 2가지가 있다고 한다.
첫 번째, 횟수가 정해져 있는 상황.
두 번째, 횟수가 정해지지 않은 상황 (조건)
for문은 while문을 편하게 쓰기위해 압축된 틀과 같다.
“Hi문자를 3회 출력하고 싶습니다.”
int main(void)
{
int num = 0; //초기식
while (num < 3) //조건식
{
printf("Hi~");
num++; //증감식
}
....
}
기존의 while문의 경우 우리는 이렇게 사용했다.
주석에 따라서 초기식 조건식 증감식이 있는데 이것을 그냥 for 형식의 틀에 맞게 옮겨주면 된다.
for (초기식; 조건식; 증감식)
int maiint main(void)
{
for (int num = 0; num < 3; num++)
printf("Hi~");
}
이렇게 순서대로 넣으면 while문과 같게 된다. 가독성이 높아졌다.
1.int num=0 / 2=num <3 / 3.Printf() / 4.num++
가 있다면 1이후 2 3 4가 반복으로 이루어 진다는 것이다.
그러면 무조건 for문을 쓰는가? 그건 아니다.
반복의 횟수가 정해졌다면 for문이며 값을 기다리는 상황이면 while문이 더 자연스럽다.
for문 기반의 다양한 예제
int main(void)
{
int total = 0;
int i, num;
printf("0부터 num까지의 덧셈, num은? ");
scanf("%d", &num);
for (i = 0; i < num + 1; i++)
total += i;
printf("0부터 %d까지 덧셈결과: %d \n", num, total);
return 0;
}
이렇게 하면 for문 덕분에 가독성이 올라가고 편하며 코드를 조금 더 줄일 수 있지만 두 가지 스타일을 익히자.
for (int i=0; i<num +1; i++)
처럼 바뀔 수 있는 것이다. 초기화가 안에 들어가는 것
int main(void)
{
double total = 0.0;
double input = 0.0;
int num = 0;
for( ; input>=0.0 ; )
{
total += input;
printf("실수 입력(minus to quit) : ");
scanf("%lf", &input);
num++;
}
printf("평균: %f \n", total/(num - 1));
return 0;
}
이 예제는 입력하는 실수의 평균값을 출력하며 작은 음의 실수 값이 입력될 떄 까지 계속되고 마지막 0은 제외한다.
여기서 자세히보면 초기식과 증감식이 없는데 진행되었다.! for문은 필요없다면 채우지 않아도 된다.
이때 조건문이 비어버리면 조건이 없어서 모든 값이 True가 된다.
ex) for( ; ;) <- 처럼 말이다.
문제 for문의 활용
프로그램 사용자로부터 두 개의 정수를 입력 받아, 두 정수를 포함하여 그 사이에 존재하는 정수들의 합을 계산하는 프로그램
단 두 번째 수가 더 커야 한다는 조건
int main(void)
{
int start1=0;
int start2=0;
int total=0;
printf("두개의 정수를 입력해주세요: ");
scanf("%d %d", &start1, &start2);
for (total = 0; start1 <= start2; start1++)
total += start1;
printf("합계는 %d \n", total);
return 0;
}
2개의 정수를 입력 받은 후, 누적값을 0으로 만든다.
그 후 start1 변수가 start보다 작거나 같은 때,
start이 1씩 커진 후 누적한다.
int main(void)
{
int start = 0;
int end = 0;
int total = 0;
printf("두개의 정수를 입력해주세요: ");
scanf("%d %d", &start, &end);
if (start > end || start == end) //만약에 start가 end보다 크거나 같은 경우
{
int temp = start; //정수형 temp에 임시로 start값을 넣는다.
start = end; //end를 start에 삽입한다.
end = temp; //temp값을 end에 삽입한다.
}
//이를 통해서 교환이 이루어지는 코드가 완성되었다.
for (total = 0; start <= end; start++)
total += start;
printf("합계는 %d \n", total);
return 0;
}
추가 연습으로 만약 start 가 end보다 크다고 해도 여유공간을 통해 저글링 하듯 옮겼다.
다음 수식인 계승(팩토리얼)을 계산하는 프로그램을 작성해보자
n! = 1*2*3....*n;
이 수식에 맞는 프로그램을 작성해보자.
int main(void)
{
int start;
int factorial;
int multiplied=1;
printf("입력 받고자하는 n!의 값을 입력해주세요: ");
scanf("%d", &factorial);
for (start=1; start <= factorial; start++)
multiplied *= start ;
printf("%d!은 %d입니다.\n", factorial, multiplied);
return 0;
}
변수 초기화 후, 몇 팩토리얼을 할지 결정한다.
그후 start 가 팩토리얼보다 작거나 같을 때 까지 반복한다.
그리고 그 값을 누적으로 곱해준다.
for문의 중첩
while문의 중첩처럼 for문에도 중첩을 할 수 있다.
int main(void)
{
int cur, is;
for (cur = 2; cur < 10; cur++)
{
for (is = 1; is < 10; is++)
printf("%dx%d=%d \n", cur, is, cur * is);
printf("\n");
}
return 0;
}
구구단처럼 초기화 값과 범위에 의하여 2~ 9 *1~9 최대 81를 얻을 수 있다
int main(void)
{
int cur = 2;
int is = 0;
while (cur < 10)
{
int is = 1;
while (is < 10)
{
printf("%dx%d=%d \n", cur, is, cur * is);
is++;
}
cur++;
printf("\n");
}
return 0;
}
그 값을 while문으로 바꾸면 초기식 조건식 증감식을 분활해주면 된다.
이렇듯 while, do~while, for에 대해서 배웠고,
반복문은 중요하니 자주 연습하고 꾸준히 해야겠다.!
조건적 실행과 흐름의 분기
흐름의 분기가 필요한 이유?
기존의 배운 계산기는 덧셈하고 뺄셈하고 곱셈하고 나눗셈을 했다면 이제는 선택을 하여 내가 필요한 것을 한다.
if문을 이용한 조건적 실행
if (num1 > num2) //num1이 num2보다 크다면 아래 조건문을 실행
{
printf("A \n");
pritnf("B \n");
}
if(조건) => 조건문의 내용이 True면 내용 문을 실행한다.
if문은 컴퓨터가 이렇게 써도 한줄로 이해한다. 컴파일러는 한 문장으로 이해하기 때문이다.
우리가 if else문은 세로로 길게써도 컴파일러는 if ~ else 라고 보이는 거다.
int main(void)
{
int num;
printf("정수입력");
scanf("%d", &num);
if (num < 0)
printf("0보다 작다\n");
if (num > 0)
printf("0보다 크다\n");
if (num == 0)
printf("0이다\n");
return 0;
}
입력받은 num변수의 값의 조건에 따라서 출력되는 값이 다른 것이다.
num=1이라고 하면 첫 번째 (num<0)에서 False후 밑으로 간다.
(num>0)으로 간후 True로 printf문을 출력
(num == 0)으로 간후 false후 밑으로 간다.
//계산기 프로그램
int main(void)
{
int opt;
double num1, num2;
double result;
printf("1.덧셈 2.뺄셈 3.곱셈 4.나눗셈 \n");
printf("선택?");
scanf("%d", &opt);
printf("두 개의 실수입력: ");
scanf("%lf %lf", &num1, &num2);
if (opt == 1)
result = num1 + num2;
if (opt == 2)
result = num1 - num2;
if (opt == 3)
result = num1 * num2;
if (opt == 4)
result = num1 / num2;
printf("결과: %f \n", result);
return 0;
}
이렇게 보면 좋은 것 같지만 문제점이 발생한다.
if문이 모두 다 한번씩 거쳐간다는 점이다. 즉 이건 효율적이지 못하다..
//1~100범위 내 3의 공배수 또는 4의 공배수를 출력
int main(void)
{
int num;
for (num = 1; num < 100; num++)
{
if (num % 3 == 0 || num % 4 == 0) // || 연산자보다 == 연산자 우선순위가 높음
//이러면 두개의 연산 결과에 대해서 ||연산을 수행
printf("3또는 4의 배수: %d \n", num);
}
//이러면 {}를 없앨 수 있음 printf는 하나의 if문 이기 떄문이다.
return 0;
}
if ~else문을 이용한 흐름의 분기
//if ~else문
if (num1 > num2) //참이면 아래 문 실행
{
printf("num1이 num2보다 크다. \n");
pritnf("%d > %d \n", num1, num2);
}
else //거짓이면 아래 문을 실행
{
printf("num1이 num2보다 크지않다.\n");
printf("%d <= %dn, num1, num2");
}
맨 처음에 말한 것처럼, if 문 1개 else 문 1개로 보이는게 아니라
if ~ else문 한개로 한 문장이다.
int main(void)
{
int num;
printf("정수입력: ");
scanf("%d", &num);
if (num < 0)
printf("입력 값은 0보다 작다\n");
else
printf("입력 값은 0보다 작지 않다\n");
}
값이 True면 if문 내의 printf값을 출력.
그 외 값은 else를 실행한다.
if… else if… else의 구성
if (조건1)
else if (조건2)
... (무한 조건n else if)
else (모든 조건 불만족)
조건 만족시 밑에 조건은 모두 무시한다.
기존의 if문과 다른 점은 else if에서 조건이 만족되면 밑의 else if문을 실행하지 않는다.
조건3이 만족한다는 기준)
if(조건1) false => else if(조건2) false => else if (조건3) True => 탈출
//계산기 프로그램의 if..else if..else
int main(void)
{
int opt;
double num1, num2;
double result;
printf("1. 덧셈 2.뺄셈 3.곱셈 4.나눗셈 \n");
printf("선택? ");
scanf("%d", &opt);
printf("두 개의 실수 입력: ");
scanf("%lf %lf", &num1, &num2);
if (opt == 1)
result = num1 + num2;
else if (opt == 2)
result = num1 - num2;
else if (opt == 3)
result = num1 * num2;
else if (opt == 4)
result = num1 / num2;
else
{
printf("1~4 사이의 값이 아닙니다.");
exit(1);
}
printf("결과: %f \n", result);
return 0;
기존의 계산기 프로그램은 if문을 모두 실행하였다.
하지만 else if문으로 건너뛰는 특성으로 인하여 연산의 수가 줄어들었다.
if … else if .. else의 진실
if (num > 0)
printf("A \n");
else if (num > 0)
printf("B \n");
else
printf("C \n")
else if (num>0)
else
if (num>0)
두 개를 비교하면 else if에 if else(IF else(if …))인 것이다.
조건 연산자: ‘삼 항 연산자’
if~else문의 대신으로 한 줄로 표현하는 문이다.
마치 n=n+m을 n+=M 같은 것이다.
(num1>num2) ? (num1) : (num2);
int num3= (num1>num2) ? (num1) : (num2);
이런 조건식이 있다고 해보자.
변수 num3은 조건식(num1>num2)을 보고
True면 num3=num1
false면 num3=num2
조건연산자는 코드를 간결하게 해준다.
int main(void)
{
int num, abs;
printf("정수 입력: ");
scanf("%d", &num);
abs = num > 0 ? num : num * (-1);
printf("절댓값: %d \n", abs);
return 0;
}
이것처럼 if ~ else문을 간략하게 뽑을 수 있다.
문제 if~else문의 활용
첫 번째. 1이상 100미만의 정수 중에서 7의 배수와 9의 배수를 출력하는 프로그램 (단 7과 9의 공배수는 한번만 출력해야한다.)
조건체크 : 1이상 100미만의 정수 / 7의 배수 / 9의배수 / 7과 9의 공배수
int main(void)
{
int count;
for (count = 1; count < 100; count++)
{
if (count % 7 == 0 || count % 9 == 0)
printf("%d\n", count);
}
return 0;
}
추가. 만약에 7과 9의 공부새만 2번 출력하려면 어떻게 해야될까?
int main(void)
{
int count;
int detection = 0 ; //7과 9의 공배수를 판별하는 함수
for (count = 1; count < 100; count++)
{
if (count % 7 == 0 && count % 9 == 0) //최상위 조건 : 공배수 출력
{
printf("%d\n", count);
printf("%d\n", count);
detection = 1;
}
if (count % 7 == 0 && detection == 0)
printf("%d\n", count);
if (count % 9 == 0 && detection == 0)
printf("%d\n", count);
if (detection = 1 && count > 63)
{
if (count % 7 == 0)
printf("%d\n", count);
if (count % 9 == 0)
printf("%d\n", count);
}
}
return 0;
}
일단 먼저 7과 9의 공배수를 출력하는데 있어
if (count % 7 == 0 && count % 9 == 0)
{
printf("%d\n", count);
printf("%d\n", count);
detection = 1;
}
이 것을 삽입해주는데 2번 출력하는게 생각이 안나서 printf를 두 번 했다.
그리고 공배수가 나왔는지 안나왔는지 detection 변수를 통해 공배수가 나오면 1을 올려준다.
if (count % 7 == 0 && detection == 0)
printf("%d\n", count);
if (count % 9 == 0 && detection == 0)
printf("%d\n", count);
일단 공배수가 7, 9이면서 공배수가 나오지 않는 AND조건으로 넣는다.
if (detection = 1 && count > 63)
{
if (count % 7 == 0)
printf("%d\n", count);
if (count % 9 == 0)
printf("%d\n", count);
}
공배수가 한번 이상 출력되었으며, 63이상일때
7공배수 9공배수를 출력한다.
이게 없으면 63이후가 안나오기 때문에 추가했다.
두 번째, 두 개의 정수를 받아 두 수의 차를 출력하는 프로그램을 구현해보자.
단, 무조건 큰 수에서 작은 수를 뺸 결과를 출력한다. 7 5면 2 , 5 7이여도 2다.
조건: 두 개의 정수, 두 수의 차, 큰 수에서 작은 수 (작은 틀 한개 추가)
int main(void)
{
int num1, num2;
int subtract;
printf("두 개의 수를 입력 해주세요: ");
scanf("%d %d", &num1, &num2);
//subtract= (num1 - num2);
if (num1 > num2)
subtract = num1 - num2;
else
subtract = num2 - num1;
printf("빼기 값은: %d 입니다 \n", subtract);
return 0;
}
두 개의 수를 입력받아 num1이 크면 num1 -num2 아니면 num2 -num1다.
세 번째, 학생의 전체 평균점수에 대한 학점을 출력하는 프로그램을 작성하자.
90점이상은 A, 80점 이상은 B, 70점 이상은 C, 50점 이상은 D 그리고 그 미만은 F.
순서대로 국, 영,수 점수를 받아 평균을 출력한다.
조건체크: 국영수점수, 평균점수, 학점별 조건
int main(void)
{
int language, english, math;
double average;
printf("순서대로 국어, 영어, 수학 점수를 입력해주세요: ");
scanf("%d %d %d", &language, &english, &math);
average = (language + english + math) / 3.0;
printf("평균값은: %f \n", average);
if (average >= 90.0)
printf("학점: A");
else if (average >= 80.0)
printf("학점: B");
else if (average >= 70.0)
printf("학점: C");
else if (average >= 60.0)
printf("학점: D");
else
printf("재수강: F");
return 0;
}
if ~ else if로 조건을 넣어준다.
조금 아쉬운건 excel의 count(범위)처럼 개수를 세듯
변수의 개수를 자동으로 세주는 함수가 있다면
average = (language + english + math) / (변수 갯수 함수)
이렇게 하면 좀 더 좋을 것 같다.
네 번째, 위의 문제2 if ~else문을 이용해서 해결하면 그것을 조건 연산자로 변환해보자.
int main(void)
{
int num1, num2;
int subtract;
printf("두 개의 수를 입력 해주세요: ");
scanf("%d %d", &num1, &num2);
subtract = (num1<num2)? num2-num1: num1-num2;
printf("빼기 값은: %d 입니다 \n", subtract);
return 0;
}
if ~ else문이 간단해졌다.
반복문의 생략과 탈출
반복문인 (while, do~while, for)문에서 쓰는 것으로 continue와 break가 있다.
생략 continue / 탈출 break 다.
Break 빠져나간다. 탈출
int main(void)
{
int sum = 0, num = 0;
while (1) //항상 True
{
sum += num; //sum = sum+num
if (sum > 5000) //sum이 5000이상이면 발동
break; // 종료 (break는 자신을 감싸는 가장 가까운 위치 반복문 하나 빠져나감)
num++;
}
printf("sum: %d \n", sum); //sum =1+2+3...n 100까지 5050 99면
printf("num: %d \n", num); //num =숫자
return 0;
}
위의 예제를 보면 break문을 통해서 if문을 빠져나올 수 있다!
if문이 실행되면 break가 실행되면서 if문이 바로 종료된다.
주의: if의 빠져나가는 것이 아닌, 가장 가까이 감싸는 반복문을 하나 탈출하는 것이다.
결과값은 sum: 5050 num: 10
왜냐하면 sum값은 1+2+3…n
num값은 1+1+1+1…n
등차수열로 구해보자
\(등차수열
S_n=n(a_1+a_n)/2 이다.\)
\[S_n=100(1+100)/2 \\ =100*101/2 \\ =10100/2 \\5050\]
즉 C언어로 등차수열을 구현한 것이다.
continue 반복조건으로 되돌아가자. 생략
break문과 마찬가지로 반복문 안에 삽입이 되는데, 조건이 만족하면 그냥 뒤에 문장을 생략하고 반복문으로 가는 것이다.
//컨티뉴 예제
int main(void)
{
int num;
printf("start! ");
for (num = 1; num < 20; num++)
{
if (num % 2 == 0 || num % 3 == 0) //2의 배수 3의 배수는
continue; // 여기서 다시 위로 올려버림
printf("%d", num); //2의 3의 배수는 작동x
}
printf("end! \n");
return 0;
}
1~20 사이의 범위에서 2와 3의 배수가 아닌 것을 출력한다.
if문의 조건이 참이면 continue를 진행시켜서 밑을 생략하고 올리니 printf를 생략한다.
문제 continue &break 활용
첫 번째. 구구단을 출력하되 짝수 단(2단 4단 6단 8단)만 출력되도록 하자.
추가로 2단은 2X2까지 4단은 4X4까지 6단은 6X6까지 8단은 8X8까지 출력되도록 만들자.
조건체크: 짝수 단을 출력 / 2단은 … 8단은 8X8까지 출력
int main(void)
{
int dan;
int su;
for (dan = 1; dan < 10; dan++)
{
if (dan % 2 != 0)
continue; //실행이 안되게 위로 올려 버리기
for (su = 1; su < 10; su++)
{
if (su > dan)
break;
printf("%d x %d = %d 입니다\n", dan, su, dan * su);
}
printf("\n");
}
return 0;
}
일단 for문을 통해서 구구단의 큰 틀인 n단의 반복문을 만들어준다.
1조건을 넣는다 (짝수 단이면 출력 아니면 미출력) if(dan&2!=0) 이다.
그리고 작은 틀은 nXm에서 m단의 반복문을 만들어준다.
2조건을 넣는다 (2=2x2 4=4x4이니까 단x수로 보면 수는 단을 초월할 수없기에 if(su>dan)을 넣는다)
두 번째, 다음 식을 만족하는 모든 A와 Z를 구하는 프로그램을 작성해보자.
\(AZ\\
+ZA\\
-----\\
99\)
가능한 모든 수의 조합을 시도하기 위해 반복문을 조합한다.
int main(void)
{
int A, Z; // A와 Z 변수
int sum; //A+Z의 합
for (A = 0; A < 10; A++) //A는 0부터, 10이하, 1반복시 +1 A의 경우의 수대입
{
for (Z = 0; Z < 10; Z++) //Z는 0부터, 10이하, 1반복시+1 Z의 경우의 수 대입
{
if (A == Z) //A와 Z가 같다면 중복방지
continue; // 조건문으로 올려버린다. Z++후에 Z<10 진행 5+5 4+4는 불가능
//없어도 실행과 값은 같은데 아마 연산의 차이가 나는 듯. 중복방지
sum = (A * 10 + Z) + (Z * 10 + A); // (1~9*10+1~9)AZ + (1~9 * 10 + 1~9)ZA 경우의 수
if (sum == 99) //sum이 99면
printf("%d%d+%d%d=%d\n", A, Z, Z, A, sum); //printf 출력
}
}
return 0;
}
A를 1~9까지 범위에서 1증가 , Z 1~9까지 범위에서 1증가
그리고 if(A==Z) continue로 중복을 방지해준다.
AZ+ZA인 sum = (A * 10 + Z) + (Z * 10 + A)를 넣는다.
만약) AB+BA=88이면
int main(void)
{
int A, B;
int sum;
for (A = 0; A < 10; A++)
{
for (B = 0; B < 10; B++)
{
sum = (A * 10 + B) + (B * 10 + A);
if (sum == 88)
printf("%d%d+%d%d=%d\n", A, B, B, A, sum);
}
}
return 0;
}
이렇게 처리할 수 있다. 근데 ` if(A==Z) continue`을 넣으면 44+44가 출력되지 않아서 제거했다.
만약) 만약 ABC+DEF=999
int main(void)
{
int A, B, C;
int D, E, F;
int sum;
for (A = 0; A < 10; A++){
for (B = 0; B < 10; B++){
for (C = 0; C < 10; C++){
for (D = 0; D < 10; D++) {
for (E = 0; E < 10; E++) {
for (F = 0; F < 10; F++) {
sum = (A * 100 + B * 10 + C) + (D * 100 + E * 10 + F);
if (sum == 999)
printf("%d%d%d+%d%d%d=%d \n", A, B, C, D, E, F, sum);
}
}
}
}
}
}
return 0;
}
for문안에 for문안에 for문안에.. ;;
이런 다중 중첩일때 중괄호를 들여쓰기 하면 오히려 가독성이 구려서 for 마지막에 넣어줬다.
switch문에 의한 선택적 실행과 goto문
swtich문은 if…else if…else와 유사한 측면이 있다.
하지만 switch문은 if…else if..else문에 비해서 제한적이다.
switch문의 구성과 기본기능
int main(void)
{
int num;
printf("1이상 5이하의 정수입력: ");
scanf("%d", &num);
switch(num)
{
case 1: //case 1영역 1입력시 여기서부터 실행
printf("1은 ONE\n");
break; //빠져나감! 근처의 반복문 switch 종료
case 2:
printf("2은 TWO\n");
break;
case 3:
printf("3은 THREE\n");
break;
case 4:
printf("4은 FOUR\n");
break;
case 5:
printf("5은 FIVE\n");
break;
default:
printf("I don't know! \n");
}
return 0;
}
내가 입력한 num값을 받아서 그것을 switch에 넣어서 그 값에 따라서 case문의 내용을 출력한다.
case문 내부에 break가 없으면 그 밑으로 쭉 실행으로 하는 것이고
break;가 존재시 거기까지만 진행된다.
switch(2)라고 하면 case 1 -> case 2로 가는 것이 아닌 한번에 case2로 간 후 실행되고 break문으로
가장 가까운 반복문 swtich를 탈출한다. break문이 없다면 2부터 쭈욱 실행된다.
switch문의 일반적 형태
int main(void)
{
...
switch(n)
{
case 1:
printf("A1");
pritnf("A2";
break;
case 2:
printf("B1");
pritnf("B2";
break;
default
printf("default");
}
...
}
기본 적 형태로 n에 값에 따라서 내가 실행할 case영역이 다른 것이다.
break문을 생략한 형태의 switch 문 구성
해당 case문만 실행되도록 switch문의 동작방식을 결정하면 일일히 break를 안달아도 된다.
int main(void)
{
char sel;//문자 받음
printf("M 오전, A 사과, E 엘프 \n");
printf("입력: ");
scanf("%c", &sel);
switch (sel)
{
case 'M':
case 'm':
printf("Morning \n");
break;
case 'A':
case 'a':
printf("Apple \n");
break;
case 'E':
case 'e':
printf("elf \n");
break; //불필요한 break
}
return 0;
}
이번에는 기존과 다르게 char형 변수를 지정하여
내가 입력한 문자를 받아들여 출력하는데 대소문자에 대한 case를 부여하여 실행시킬 수 있게 한다.
break문이 삽입되어서 유용한 경우가 있고 break문이 들어가지 않아서 유용한 경우가 있다.
switch vs if…else if..else
둘은 차이가 있다.
“분기의 경우의 수가 많아지면 가급적 switch문으로 구현한다. - 가독성”
//if..else if..else
if (n == 1)
printf("A");
else if (n == 2)
printf("B");
else if (n == 3)
printf("C");
else
printf("D");
//switch
switch (n) {
case 1:
printf("A");
break;
case 2:
printf("B");
break;
case 3:
printf("C");
break;
default
printf("D");
}
switch문이 더 간결해보이는 것을 할 수있다.
실제로 if..else if..else문은 비선호한다고 하며
switch문을 선호하기에 switch문의 가독성 향상을 위한 코드를 작성하도록 노력해보자!
“하지만 switch는 한계가 있다. 표현하기 애매하면 if …else if ..else를 사용해야되는 것이다.”
//if..else if..else
if (0<=n && n<10)
printf("10");
else if (10 <= n && n < 20)
printf("1020");
else if (20 <= n && n < 30)
printf("2030");
else
printf("40");
switch (n) {
case ?:
printf("10");
break;
case ?:
printf("20");
break;
case ?:
printf("30");
break;
default
printf("40");
}
이렇게 보면 조건이 복잡하여 switch 문으로 표현하기 어렵다.
이런 경우에는 분기의 수가 많아도 switch문을 고집할 필요 없다.
문제 switch 문으로 변경하기
if…else if …else문과 switch문은 서로 교체가 가능하듯 해보자!
int main(void)
{
int n;
printf("0이상의 정수를 입력해주세요");
scanf("%d", &n);
switch (n/10) { //10을 나눠숴 0=1~9 / 1=10~19 / ...
case 0: //값이 0이면
printf("0이상 10미만입니다.");
break;
case 1: //값이 1이면
printf("10이상 20미만입니다.");
break;
case 2:
printf("20이상 30미만입니다.");
break;
default:
printf("40이상 및 재입력");
}
return 0;
}
이렇게 조건을 잘 설정할 수 있으면 swtich!!
마지막으로 goto에 대해서 소개하자.
goto문은 마검과 같고, 실제로 잘 사용하지 않는다고한다.
int main
{
..
rabbit:
...
goto rabbit;
...
}
rabbit이라는 레이블을 지정하고 가다가 goto rabbit를 하면 rabbit으로 이동하는 것이다.
int main(void)
{
int num;
printf("입력: ");
scanf("%d", &num);
if (num == 1)
goto ONE;
else if (num == 2)
goto TWO;
else
goto OTHER;
ONE:
printf("1입력\n");
goto END;
TWO:
printf("2입력\n");
goto END;
OTHER:
printf("다른 값\n");
END:
return 0;
}
이러면 입력하는 것에 따라서 내가 설정한 레이블로 간다.
이러면 단점들이 보인다
첫 번째. 가독성이 구려진다 (왜냐? 점프하는 것으로 넘어가서 봐야되니까)
두 번째. 버그가 발생할 수 있다
세 번째. 절차저리의 역전이 발생한다.
=Joat다.
그냥 이런 것만 알고있고 쓰지말자.
-
[C언어의 기본] 데이터 표현과 연산관련
데이터 표현방식의 이해
‘컴퓨터는 2진수를 기반으로 데이터를 표현하고 연산을 진행한다’
.
여기서는 진법 자체의 이해를 해보고자 한다.
숫자로 기호에 포함이 된다!
10진수는 총 열 개의 숫자를 이용해서 수를 표현
10진수는 총 열 개의 기호를 이용해서 수를 표현
컴퓨터는 0과 1로 데이터를 표현하니 2진수만 알면된다? 그건 아니다.
2진수로 데이터를 표현하게 되면 길이가 길어져 표현도 파악하기도 어렵다.
16진수를 이용하면 2진수를 간단하게 표현 및 파악하기 쉽기 때문이고
많은 서적이 16진수를 사용한다고 한다.
그러니까 2진수 10진수 16진수를 열심히 공부하자.
문제
문제1: 10진수 8부터 20까지 2진수와16진수로 각각 표현해보자!
10진수 8 = 2진수 1000 = 16진수 8
10진수 9 = 2진수 1001 = 16진수 9
10진수 10 = 2진수 1010 = 16진수 A
10진수 11 = 2진수 1011 = 16진수 B
10진수 12 = 2진수 1100 = 16진수 C
10진수 13 = 2진수 1101 = 16진수 D
10진수 14 = 2진수 1110 = 16진수 E
10진수 15 = 2진수 1111 = 16진수 F
10진수 16 = 2진수 10000 = 16진수 10
10진수 17 = 2진수 10001 = 16진수 11
10진수 18 = 2진수 10010 = 16진수 12
10진수 19 = 2진수 10011 = 16진수 13
10진수 20 = 2진수 10100 = 16진수 14
문제2: 10진수 5부터 18까지 8진수로 표현해보자
10진수 5 = 8진수 5
10진수 6 = 8진수 6
10진수 7 = 8진수 7
10진수 8 = 8진수 10
10진수 9 = 8진수 11
10진수 10 = 8진수 12
10진수 11 = 8진수 13
10진수 12 = 8진수 14
10진수 13 = 8진수 15
10진수 14 = 8진수 16
10진수 15 = 8진수 17
10진수 16 = 8진수 20
10진수 17 = 8진수 21
10진수 18 = 8진수 22
데이터 표현단위 비트(bit)와 바이트(Byte)
컴퓨터를 하다보면 비트, 바이트에 대해서 많이 듣는다. 비트는 데이터의 최소단위이다.
그 비트가 8개가 묶으면 바이트라는 단위가 된다.
문제
문제1: 1비트로 표현할 수 있는 데이터의 수는 0과 1이렇게 두개이다.
2비트가 표현할 수 있는 데이터의 수는 00,01,10,11이다.
그렇다면 4비트, 1바이트 그리고 4바이트로 표현할 수 있는 데이터의 수는 몇개인가?
4비트: 0001, 0010, 0011, 0100, 0101, 0110, 0111, 1000, 1001, 1010, 1011, 1100 ... 1111 = 16가지
1바이트(8bit) : 00000001 .... 11111111 = 256가지
4바이트(32bit) : 00000000000000000000000000000001 ~ 111111111111111111111111111111111 = 4,294,967,296가지
이 문제를 통해 알 수 있는 것은 $2^n$ 이라는 것이다.
문제2: 1바이트 00000001은 10진수로 1이고, 1바이트 00000010은 10진수로 2이다.
그렇다면 다음 바이트들은 각각 10진수로 얼마가 되겠는가?
00000001 = 1
00000010 = 2
00000100 = 4
00001000 = 8
00010000 = 16
00100000 = 32
01000000 = 64
10000000 = 128
문제3: 문제2에서 얻은 결과를 가지고, 다음 각각의 바이트들이 나타내는 값이 10진수로 얼마인지 계산하자.
00010001 = 17
10100010 = 162
11110111 = 247
8진수와 16진수를 이용한 데이터 표현
int num1 = 10; //특별한 선언이 없기에 10진수 표현
int num2 = 0xA; //0x로 시작하면 16진수로 읽음
int num3 = 012; //0으로 시작하면 8진수로 읽음.
16진수중 10은 A로 표현아니까 0x(16진수)A(10) 이라는 것이다.
예제
#include <stdio.h>
int main(void)
{
int num1=0xA7, num2=0x43;
int num3=032, num4=024;
printf("0xA7의 10진수 정수 값: %d \n", num1);
printf("0x43의 10진수 정수 값: %d \n", num2);
printf("032의 10진수 정수 값: %d \n", num3);
printf("024의 10진수 정수 값: %d \n", num4);
printf("%d-%d=%d \n", num1, num2, num1-num2);
printf("%d+%d=%d \n", num3, num4, num3+num4);
return 0;
}
0xA7의 10진수 정수 값: 167
0x43의 10진수 정수 값: 67
032의 10진수 정수 값: 26
024의 10진수 정수 값: 20
167-67 = 100
26+20=46
정수와 실수의 표현방식
1바이트를 기준으로하여 정수형의 표현방식을 설명들었다.
일단 정수의 가장 왼쪽에 존재하는 비트는 ‘부호비트’이다.
1byte= 8bit 이니까 1바이트가 있다면 앞에 1개는 부호비트, 뒤는 데이터크기이다.
양의 정수라면 부호비트에 0 , 음의 정수라면 부호비트에 1을 기입한다
음의 정수는 어떻게 표현할까?
음의 정수는 2의 보수를 취해야한다 뭐..이건 학교에서 배웠으니..
예를 들면 -5가 있다고 하면 부호비트1에 000101 이면 -5아님? 이지만 아니다.
일단 2의보수를 취하려면 1의 보수 (0과 1의 뒤집기)를 해야된다.
정수5 비트: 00000101 에서 1의 보수를 취한다 (11111010)
그리고 1의 보수 취한값에 +1을 해준다 (11111011)
이 값이 -5가 된다.
문제
문제1: 양의 정수 01001111과 00110011은 각각 10진수로 얼마인가?
01001111 = 79
00110011 = 51
문제2: 음의 정수 10101001과 11110000은 각각 10진수로 얼마인가?
10101001 = 01010110 = 01010111 = -87
11110000 = 00001111 = 00010000 = -16
실수의 표현방식
실수가 표현하는 방식은 조금 복잡하다.
실수를 표현하는 기본방식
: 2바이트 메모리를 가지고 한다는 가정하에 하면 반으로 나누고 반을 소수점이상, 반은 소수점이하 표현
실수는 소수점이 많기 때문에 모두 표현하면 엄청난 바이트가 소모되게 된다.
그렇기 때문에 어떠한 정의된 수식이 정의된다.
컴퓨터가 실수를 이렇게 표기하기 때문에 넓은 범위를 표현할 수 있지만 오차가 발생하게 된다.
엄청난 정밀도가 아니라 듬성듬성 표현하자는 것이다.
ex)0.0이 있다고 하면 이미 $2^e$ 이기떄문에 e에 0이 들어가도 0이라는 값은 나올 수 없다.
그렇기 때문에 근사치를 통해서 실수를 표현하는데 이러한 것을 ‘부동 소수점 오차’라고 한다.
예제
#include <stdio.h>
int main(void)
{
int i;
float num=0.0;
for(i=0; i<100; i++)
num+=0.1;
printf("0.1을 100번 더한 결과: %f \n", num); //실수 표현은 float 출력
return 0;
}
0.1을 100번 더한 결과: 10.000002
가 나오게 된다. 즉 0.000002의 오차가 구현된 것이다.
비트 연산자
비트연산자는 하드웨어 관련 프로그래밍에 활용된다
하지만 비트 연산자를 통해 메모리 공간의 효율성을 높이고 연산의 수를 줄이는 요인이 된다.
연산자: 비트단위 AND
AND는 모두가 1일때 1을 반환하는 연산이다.
#include <stdio.h>
int main(void)
{
int num1 = 15; // 00000000 000000000 000000000 00001111
int num2 = 20; // 00000000 000000000 000000000 00010100
int num3 = num1 & num2; // num1과 num2의 비트단위&연산
printf("AND 연산의 결과: %d \n", num3);
return 0;
}
AND 연산의 결과 : 4
왜 4가 나오는지 알아야한다! num1 (true:1) & num2(true:2)이 아닌 15와 20의 AND이다.
000 01111
000 10100
---------
000 00100
두 값이 1 1 인 것만 1이 출력되는 것을 통해 4가 출력됨을 알 수 있다!
연산자: 비트단위 OR
OR은 둘중 하나라도 1이면 1을 출력하는 것이다
#include <stdio.h>
int main(void)
{
int num1 = 15; // 00000000 000000000 000000000 00001111
int num2 = 20; // 00000000 000000000 000000000 00010100
int num3 = num1 | num2; // num1과 num2의 비트단위|연산
printf("OR 연산의 결과: %d \n", num3);
return 0;
}
OR연산의 결과: 31
이렇게 나오는 것은 위처럼 똑같이 해주면된다!
000 01111
000 10100
---------
000 11111 (00100000-1) = 31
^연산자: 비트단위 XOR
^연산자는 두개의 비트가 서로 다른 경우에 1을 반환하는 연산으로서 배타적 논리합이라고 한다.
#include <stdio.h>
int main(void)
{
int num1 = 15; // 00000000 000000000 000000000 00001111
int num2 = 20; // 00000000 000000000 000000000 00010100
int num3 = num1 ^ num2; // num1과 num2의 비트단위^연산
printf("XOR 연산의 결과: %d \n", num3);
return 0;
}
XOR연산의 결과: 27
000 01111
000 10100
-----------
000 11011
~연산자: 비트단위 NOT
~(NOT)연산은 0에서 1으로, 1에서 0로 반전시킨다 (1의보수랑 비슷하다고 보면된다) </br>
#include <stdio.h>
int main(void)
{
int num1 = 15; // 00000000 000000000 000000000 00001111
int num2 = ~num1;
printf("NOT 연산의 결과: %d \n", num2);
return 0;
}
NOT연산의 결과: -16
11111111 11111111 11111111 11110000 [NOT을 수행]
[2의보수 진행]00000000 00000000 00000000 00010000 = +16
위의 값이 - 16임을 알 수 있다.
«연산자: 비트의 왼쪽이동(Shift)
«연산자는 두개의 피연산자를 요구한다.</br>
num1 « num2 : num1의 비트열을 num2 2칸씩 왼쪽으로 이동시킨 결과 반환
8 « 2 : 정수 8의 비트 열을 2칸씩 왼쪽으로 이동시킨 결과를 반환
#include <stdio.h>
int main(void)
{
int num=15;
int result1 = num<<1;
int result2 = num<<2;
int result3 = num<<3;
printf("1칸 이동 결과: %d \n", result1);
printf("2칸 이동 결과: %d \n", result2);
printf("3칸 이동 결과: %d \n", result3);
return 0;
}
1칸 이동 결과: 30
2칸 이동 결과: 60
3칸 이동 결과: 90
int num 15
= 00000000 00000000 00000000 00001111
result1 : 00000000 00000000 00000000 00011110
result2 : 00000000 00000000 00000000 00111100
result3 : 00000000 00000000 00000000 01111000
이렇게 바뀌었다는 것을 알 수 있다.
비트의 이동으로 인해서 생기는 오른쪽 빈 칸은 ‘0’으로 채워지고 왼쪽은 버려진다.
또한 비트의 열을 왼쪽 1칸씩 갈때마다 값이 2배씩 증가한다는 것을 알 수 있다.
그러면 비트의 열을 오른쪽 1칸씩 갈때마다 값은 2배씩 작아진다는 것을 알 수 있다.
ex) 30*4 = 30 «2
»연산자: 비트의 오른쪽 이동(shift)
위처럼 이면 num1 » num2은 무엇이 될까?
num2의 크기만큼 num1의 비트 열이 오른쪽으로 이동한 결과가 반환된다.
11111111 111111111 11111111 11110000 //-16
여기서 num1 »> num2을 하면 CPU에 따라서 값이 달라진다 .
00111111 11111111 11111111 11111100 // 0이 채워진다.
11111111 11111111 11111111 11111100 // 1이 채워진다.
CPU에 따라서 0이 채워질 수 있고 1이 채워질 수 있다.
그렇기에 일반적으로 잘 안쓴다고 한다. 왜? CPU마다 값이 다르니 호환성이 별로라서..
#include <stdio.h>
int main(void)
{
int num= -16; // 11111111 11111111 11111111 11110000
printf("2칸을 오른쪽 이동의 결과 : %d \n", num>>2);
printf("3칸을 오른쪽 이동의 결과 : %d \n", num>>3);
return 0;
}
2칸을 오른쪽 이동의 결과: -4 (2배줄음)
3칸을 오른쪽 이동의 결과: -2 (3배줄음)
문제
문제1: 입력 받은 정수 값의 부호를 바꿔서 출력하는 프로그램을 작성해보자
-3이 입력되면 3이 출력되듯이 단, 비트연산자를 이용해서 구현해야한다.
#include <stdio.h>
int main (void)
{
int num;
printf("정수 값을 입력하세요: ");
scanf("%d", &num);
num = ~num;
num = num+1; //2의 보수를 구하는 과정
printf("부호를 바꾼 결과: %d \n", num)
return 0;
}
정수 값을 입력하세요: -3
부호를 바꾼 결과: 3
문제2: 다음 연산 결과를 출력하는 프로그램을 작성해보자.
3x8/4 [단 사칙연산자를 사용하지 않는다.]
#include <stdio.h>
int main(void)
{
int num = 3;
num = num << 3; //8배 곱하기
num = num >> 2; //4배 나누기
printf("값은? %d \n", num);
return 0;
}
값은? : 6
4칙 연산을 사용한다면 어떻게 할 수 있을까
#include <stdio.h>
int main(void)
{
int num1=3;
int num2=8;
int num3=4;
printf("%d*%d/%d= %d",num1,num2,num3,num1*num2/num3);
return 0;
}
값은?: 6
하지만 여기서 scanf까지 써본다면?
int main(void)
{
int num;
printf("정수를 입력하세요: ");
scanf("%d", &num);
num = num << 3; //8배 곱하기
num = num >> 2; //4배 나누기
printf("값은? %d \n", num);
return 0;
}
정수를 입력하세요: 3
값은? : 6
기본 자료형의 이해
자료형은 데이터를 표현하는 방법이다
컴퓨터는 묻는다. “정수를 저장할 것인가요? 실수를 저장할 것인가요?”
대답을 하면 다음은 “정수를 저장하기 위해 몇 바이트를 사용하실 건가요?”
“정수를 저장하는데 크기는 4바이트, 변수를 num으로 할게요” = int num; 가 된다.
기본 자료형의 종류와 데이터의 표현범위
C언어의 표준 기본 자료형은 이렇게 되어있다.
“short와 int는 최소 2바이트 이되 int는 short와 크기가 같거나 더 커야 한다.”
라는 말이 있듯이 자료형 별 크기를 정확히 하게는 제한 할 수 있지만 알아두는게 좋다..
그러면 이런 생각을 한다. “무조건 큰거 쓰면 장땡아닌가?”
하지만 실수의 소수점 처럼 바이트를 너무 낭비하게 되니 효율적으로 사용하기 위해서는 딱 맞춰써야된다
연산자 sizeof 자료형의 크기 확인
연산자를 통해서 자신이 사용하는 컴파일러의 자료형 별 바이트 크기를 확인 할 수 있다.
#include <stdio.h>
int main(void)
{
int num=10;
int sz1= sizeof(num);
int sz2= sizeof(int);
....
}
sizeof 후에 피연산자를 ()로 감싼다. 보기 편하게!
자료별 바이트 크기를 확인해보자
#include <stdio.h>
int main(void)
{
char ch=9;
int inum=1052;
double dnum=3.1415;
printf("변수 ch의 크기: %d \n",sizeof(ch));
printf("변수 inum의 크기: %d \n",sizeof(inum));
printf("변수 dnum의 크기: %d \n",sizeof(dnum));
printf("char 크기: %d \n",sizeof(char));
printf("int 크기: %d \n",sizeof(int));
printf("long 크기: %d \n",sizeof(long));
printf("long long 크기: %d \n",sizeof(long long));
printf("float 크기: %d \n",sizeof(float));
printf("double 크기: %d \n",sizeof(double));
return 0;
}
변수 ch의 크기: 1
변수 inum의 크기 : 4
변수 dnum의 크기 : 8
char 크기 : 1
int 크기 : 4
long 크기 : 4
long long 크기 : 8
float 크기 : 4
double 크기 : 8
정수를 표현 및 처리하기 위한 일반적인 자료형의 선택
“값의 범위만 가지고 int인지 short인지 결정할 수 없다. 상황에 따라 다르다.”
#include <stdio.h>
int main(void)
{
char num1=1, num2=2, result1=0;
short num3=300, num4=400, result2=0;
printf("size of num1 & num2: %d, %d \n", sizeof(num1),sizeof(num2));
printf("size of num3 & num4: %d, %d \n", sizeof(num3),sizeof(num4));
printf("size of char add: %d, %d \n", sizeof(num1+num2));
printf("size of short add: %d, %d \n", sizeof(num3+num4));
result1=num1+num2;
result2=num3+num4;
printf("size of result1 & result2: %d, %d \n",sizeof(result1),sizeof(result2));
return 0;
}
size of num1&num2 : 1, 1
size of num3&num4 : 2, 2
size of char add : 4
size of short add : 4
size of result1 & result2 : 1, 2
여기서 보면 num1의 크기는 1바이트, num2도 1바이트인데 num1+num2가 4바이트임을 알 수 있다.
또한 num3의 크기는 2바이트, num4도 2바이트인데 num3+num4는 4바이트임을 알 수 있다.
CPU가 처리하기 가장 적한 크기의 정수 자료형을 int형으로 정의한다.
즉, int형의 연산속도가 빠르기 때문에 int으로 연산하기 위해 바꿔주는 것이다.
“char형, shor형 변수”의 경우에는 연산속도보다 데이터의 크기가 줄이는 것이 더 중요한 것에 쓴다.
실수를 표현 및 처리하기 위한 일반적 자료형의 선택
실수형의 자료형은 이렇게 있다.
정수형에서 대표가 int형이듯 실수형에도 대표가 있다고한다. 대부분 double을 쓴다고 한다.
왜냐하면 float보다는 유효자릿수가 높아 정밀도가 높고 long double보다는 너무 부담스럽다.
#include <stdio.h>
int main(void)
{
double rad; //반지름
double area; //넓이
printf("원의 반지름을 입력하세요: ");
scnaf("%lf", &rad); //double형의 데이터의 입력은 &lf
area= rad*rad*3.1415; //넓이 구하는 공식
printf("원의 넓이 : %f \n", area); //double형의 데이터 출력은 &f
return 0;
}
원의반지름 입력: 2.4
원의 넓이: 18.095040 <float형이라 소수점 6자리>
unsigned를붙여서 0과 양의 정수만 표현할 수 있다.
하지만 unsigned를 실행하기 위해서는 조건이 3가지 있다.
정수 자료형의 이름 앞에만 붙일 수 있다.
unsigned가 붙으면, MSB도 데이터의 크기를 표현하는데 사용된다. (why? 양의정수만 있으니 필요없자나~)
표현할 수 있는 값이 0 이상의 범위로 두배가 된다(음수가 필요없으니까 2배)
=> 즉 int 는 singed int, short는 singed short인데 앞에 singed가 생략된것 이다
단) char형은 예외일 수 있다고한다.
char를 unsigned char로 처리하는 컴파일러도 있기 때문에 char 변수 선언해서 음의 정수를 선언하면 singed를 추가하기도 한다.
</br>
문자의 표현방식과 문자를 위한 자료형
컴퓨터는 숫자를 이용해서 무언가를 인식하고 표현하는데 그러면 문자는 어떻게 인식하는 것인가..
아스키코드(ASCII)코드가 있다! 숫자를 문자에 연결(mapping)시키는 것이다.!
“문자 A는 숫자65, 문자 B는 숫자 66 정의..” 이렇게 해서 표준화를 시키는 것이다.
C언어는 ANSI(American National Standards Institute)에 의해서 제정된 아스키코드(ASCII:American Standard Code ..)로 표현한다.
아스키코드 값은 방대하니 나중에 알아서 찾아보자…
그러면 문자는 어떻게 표현될까?
int main (void)
{
char ch1 = 'A' //문자열은 "" , 문자는 ''
char ch2 = 'C'
...
}
우리는 기존의 “ “이라는 문자열을 썻다! 하지만 문자이기 떄문에 이번에는 ‘ ‘을 쓴다! .
int mian(void)
{
char ch1 = 65;
char ch2 = 67;
}
컴파일러는 이러한 아스키코드를 알고 있기 때문에 문자를 숫자로 바꿔버린다. 실제로 한번 보자!
#include <stdio.h>
int main (void)
{
char ch1='A', ch2=65;
char ch3='Z', ch4=90;
printf("%c %d \n", ch1, ch1); //문자 출력 , 10진수출력(숫자)
printf("%c %d \n", ch2, ch2);
printf("%c %d \n", ch3, ch3);
printf("%c %d \n", ch4, ch4);
return 0;
}
A 65
A 65
Z 90
Z 90
이라는 결과값이 나오게된다. %c는 문자의 형태로 출력(입력)하라는 것이다
“정수는 출력 방법에 따라 문자의 형태로 숫자의 형태로 출력이 가능하다”라는 것이다!
중요한점은! 예전에 int형이 빠르다고 했는데 보통 문자는 char형을 쓴다고 한다
그것은 int형은 연산특화형이고 char형의 경우는 연산보다는 저장에 특화가 필요하기 때문이다.
문제
문제1: 프로그램 사용자로부터 두점의 x, y 좌표를 입력 받아서, 두 점이 이루는 직 사각형의 넓이를 계싼하여 출력하는 프로그램을 작성해보자. (단 좌상단의 x, y 좌표 값이 우하단의 x, y좌표 값보다 작다고 가정하고 좌 상단의 좌표정보를 먼저 입력 받는 형태로 작성하자. )
좌상단의 x,y는 2,4 우 하단의 x, y 좌표는 4, 8 이고 두점이 이루는 직사각형의 넓이는 8이다.
#include <stdio.h>
int main(void)
{
int lx1, ly1; // left x , y
int rx1, ry1; // Right x , y
int area; //넓이
printf("좌 상단의 x, y 좌표: ");
scanf("%d %d", &lx1, &ly1);
printf("우 하단의 x, y 좌표: ");
scanf("%d %d", &rx1, &ry1);
area = (rx1 - lx1) * (ry1 - ly1); // 좌상단 값이 우하단 값보다 작기 때문에 먼저 입력.
printf("두 점이 이루는 직사각형의 넓이 %d \n", area);
return 0;
}
좌 상단의 x, y좌표 : 2 4
우 하단의 x, y좌표: 4 8
두 점이 이루는 직사각형의 넓이 8
문제2: 프로그램 사용자로부터 두 개의 실수를 받아서 double형 변수에 저장하자. 그리고 두수의 사칙연산 결과를 출력하자.
#include <stdio.h>
int main(void)
{
double num1, num2;
printf("두 개의 실수를 입력하세요: ");
scanf("%lf %lf", &num1, &num2); //실수의 입력 lf
printf("두 수의 덧셈은: %f \n", num1 + num2); //실수의 출력 f
printf("두 수의 뺼셈은: %f \n", num1 - num2);
printf("두 수의 나눗셈은: %f \n", num1 / num2);
printf("두 수의 곱은: %f \n", num1 * num2);
return 0;
}
두개의 실수를 입력하세요: 2.4 3.1
두 수의 덧셈은: 5.500000
두 수의 뺄셈은: -0.70000
두 수의 나눗셈은: 0.774194
두 수의 곱은 : 7.440000
문제3: Appendix A의 아스키 코드 표를 참조하여 다음 질문의 답을 해보자.
질문1) 아스키 코드를 구성하는 값의 범위는 어떻게 되는가?
질문2) 대문자 A부터 대문자 Z까지 값이 어떻게 증가하는가? 증가의 폭은?
질문3) 알파벳 대문자와 소문자 간의 값의 차는 어떻게 되는가? 규칙성은?
아스키 코드를 구성하는 값의 범위는 0~127 이다.
대문자 A [65] ~ 대문자 Z [122] 까지 값이 1씩 증가된다.
알파벳 대문자A[65] 소문자a[97]값의 차이는 32이다. 소문자에서 32를 빼면 대문자.
아스키코드에서는 십진수 기준 숫자 (0~9)가 [48]~[57] 할당
대문자(A~Z) : [65] ~ [90] / 소문자 [97]~[122] 이다.
문제4: 프로그램을 사용자로부터 아스키 코드 값을 정수의 형태로 입력 받은 후 해당 정수의 아스키 코드 출력을 하는 프로글매 작성하자.
ex) 프로그램 사용자가 정수 65입력시 문자 A를 출력한다.
#include <stdio.h>
int main(void)
{
int num;
printf("아스키 코드 정수 값을 입력해주세요: ");
scanf("%d", &num);
printf("아스키 코드 값: %c \n",num);
return 0;
}
아스키 코드 정수 값을 입력해주세요: 65
아스키 코드 값: A
문제5: 프로그램 사용자로부터 알파벳 문자 하나를 입력받는다. 그리고 이에 해당하는 아스키 코드 값을 출력하는 프로그램 작성하자.
#include <stdio.h>
int main(void)
{
char ch;
printf("알파벳 문자를 입력해주세요: ");
scanf("%c", &ch);
printf("아스키 문자 숫자 값: %d \n", ch);
return 0;
}
알파벳 문자를 입력해주세요: B
아스키 문자 숫자 값: 66
상수에 대한 이해
상수 역시 int, double과 같은 값을 근거로 표현된다!
상수는 변경 불가능한 데이터를 뜻한다. 예를 들어보면..
int main(void)
{
int num = 30 +40; // 30과 40은 고정값 즉 상수이다.
}
이것으로 우리는 3가지를 알 수 있다.
정수와 30과 40이 메모리 공간에서 상수의 형태로 저장된다.
두 상수를 기반으로 덧셈이 진행된다 (30+40연산)
덧셈의 결과로 얻어진 정수 70이 변수 num에 저장된다. (초기화)
즉 상수의 특징으로는 변수 num과 다르게 할당된 메모리 공간에서 이름이 없다
이렇게 이름이 없는 상수를 리터럴상수, 리터럴 이라고 한다
int main(void)
{
int num1 = 1,2,3;
int num2 = 7+num1;
double num3 = 2.12 + 7.49;
...
}
여기서 리터럴 상수값은 1, 2, 3, 7, 2.12, 7.49로 5개다!
리터럴상수의 자료형도 마찬가지로 고민해야된다 정수/실수, 몇바이트 크기 말이다.
int main(void)
{
int inum = 5;
double dnum = 7.15;
}
정수형 상수는 int형으로 메모리 공간 저장, 실수형 상수는 double형으로 저장됨을 알 수 있다 .
그러면 아까 위에서 배운 문자는 어떻게 표현되는가..?
char ch = 'A';
#include <stdio.h>
int main(void)
{
printf("literal int size: %d \n", sizeof(7));
printf("literal double size: %d \n", sizeof(7.14));
printf("literal char size: %d \n", sizeof('A'));
return 0;
}
literal int size: 4
literal double size: 8
literal char size: 4
int 4바이트, 더블 8바이트, 문자 4바이트를 알 수 있었다 .
접미사를 이용한 상수의 표현을 해보자!
int main (void)
{
float num1: 5.789;
float num2: 3.24 + 5.12;
return 0;
}
경고 메세지가 뜬다고 하는데 왜 안뜨지..; “double형 데이터를 float형에 저장하였으니 잘려나갈 수 있다고 한다.”
이럴때는 뒤에 f를 붙혀주면 된다고 한다!
int main (void)
{
float num1: 5.789f;
float num2: 3.24 + 5.12f;
return 0;
}
위의 표를 확인해서 정수형, 실수형에 맞는 것을 사용하자!
이름을 지나는 심볼릭(Symbolic)상수 : Const 상수를 써보자.
const를 이용하면 변수를 상수로 변신시켜버린다는거다!
int main (void)
{
const int MAX=100; // MAX상수 변경불가
const double PI=3.1415; //PI상수 변경불가
}
아까는 이름이 없는 상수를 : 리터럴 상수
이번에는 이름이 있는 상수: 심볼릭 상수
int main(void)
{
const int MAX; //쓰레기 값 심볼릭 상수
MAX=100; //변경안됨 why? 상수값이고 위에 초기화 됐으니까.
}
상수의 이름은 모두 대문자로 표시한다.
둘 이상의 단어로 연결할때는 언더바(_)로 이용하여 구분한다.
자료형의 변환
자료형의 변환이라는 것은 데이터의 표현방식을 바꾼다는 것이고 크게 2개로 나뉜다
자동 형 변환(묵시적 형 변환)
강제 형 변환(명시적 형 변환)
대입연산의 전달과정에서 발생하는 자동 형변환을 알아보자!
왼편에 있는 피연산자를 대상으로 형 변환이 자동으로 일어난다(왼쪽이 기준!!! 이라고 외치는 거 ㅋㅋ)
double num1 = 245; //int형 정수 245를 왼쪽(double)기준으로 자동 형 변환
int num2=3.1415; // double형 실수 3.1415를 int형으로 자동 형 변환 하지만 3만 변환된다.
즉 실수형을 정수형으로 변환하면 소수부의 손실이 발생한다는 것이다.
int num3 = 129; // 00000000 00000000 00000000 10000001
char ch = num3; // int형 변수를 char변수인 1바이트로 변경해야된다
int형 4바이트 변수를 char인 1바이트 변수로 바꾸게 되면 1000001만 남아서 정수로 -127이 출력된다
상위 바이트 손실이 발생하게 되면 부호가 바뀔 수 있으니 주의하자!
정수를 실수로 형 변환 하는 경우: 실수 표현 범위가 더 넓어서 데이터 손실은 잃어나지 않음.
실수를 정수로 형 변환 하는 경우: 소수점 이하를 표현 못해서 소수점 손실이 일어난다.
바이트 크기가 큰 정수를 바이트 크기가 작은 정수로 형 변환하는 경우: 상위 바이트들이 소멸된다.
#include <stdio.h>
int main(void)
{
double num1= 245;
int num2 = 3.1415;
int num3 = 129;
char ch= num3; //int형을 char형으로
printf("정수 245를 실수로: %f \n", num1);
printf("실수 3.1415를 정수로: %d \n", num2);
printf("큰 정수 129를 작은 정수로: %d \n", ch);
return 0;
}
정수 245를 실수로: 245.000000
실수 3.1415를 정수로: 3
큰 정수 129를 작은 정수로: -127
정수의 습격(Intergral Promotion)에 의한 자동 형 변환
“CPU가 가장 처리하기 적합한 크기의 정수 자료형은 int로 정의하고 int형 연산은 다른 자료형에 연산속도에 비해서 빠르다.”
int main(void)
{
short num1=15, num2=25;
short num3=num1+num2; //연산을 진행하여 short가 int형으로 변환된다.
}
이렇게 형이 자동으로 변환되는 과정을 ‘정수의 승격(Intergral Promotion)’이라 한다.
피연산자의 자료형 불일치로 발생하는 자동 형 변환
기본적인 산술연산에는 두 개의 피연산자가 필요하다.
double num1 = 5.15 + 19 // (두개의 피 연산자 5.15, 19)
여기서 5.15를 5로 바꿔 5+19를 하는게 좋은선택인가? 5.15 + 19.0을 하는게 좋은 선택 인가?
손실을 최소화 하려면 int형 정수를 double형 실수로 형 변환 해야된다 .
피연산자의 자료형이 일치하지 않아서 발생하는 자동 형 변환은 데이터 손실을 최소화 하는 방향으로 진행된다
데이터 손실을 최소화 해야 되기 때문에 정수자료형 보다 실수 자료형이 앞선다
명시적 형 변환 : 강제로 일으키는 형 변환
#include <stdio.h>
int main (void)
{
int num1=3, num2=4;
double divResult; //강제로 형변환시킨다 double로
divResult = num1 / num2;
printf("나눗셈 결과: %f \n", divResult); //실수 형변환 했으니 실수 출력 f
return 0;
}
나눗셈 결과 0.000000
근데 여기서 divResult = num1 /num2를 diveResult = (double) num1 / num2 으로 해줘야 된다. 뭐로 변환됐는지 알게 하기위해
이것을 형 변환 연산자 라고 하며 위의 문장은 이렇게 된다.
divResult = 3.0 / num2; //(double)num1을 통해서 num1이 double형으로 변환된다.
divResult = 3.0 / 4.0 //위의 형 변환 규칙에 의해서 왼쪽 기준으로 바뀐다. num2 => double값인 4.0
그리고 3.0 / 4.0이 되고 그 변수가 diveResult 변수에 저장된다.!
int main(void)
{
int num1=3;
double num2 = 2.5 * num1;
...
} //이러면 어떻게 형 변환됐는지 가독성이 구리다.
int main(void)
{
int num1=3;
double num2 = 2.5 * (double)num1; // 이것을 통해 가독성을 높혀준다.
...
}
Printf 함수와 scanf 함수 정리하기
이번 꺼는 레퍼런스(reference) 성격이 강하다. = 정보를 취합하는 것
printf 함수는 문자열을 출력하는 함수이다.
#include <stdio.h>
int main(void)
{
printf("I like programming \n");
printf("I love puppy! \n");
printf("I am so happy \n");
return 0;
}
I like programming
I love puppy!
I am so happy
특수문자의 출력이 필요한 이유
char ch = '\n';
특수문자 = 이스케이프 시퀀스(escape sequence)라 불리는 문자들이 필요한 이유는??
앞집 강아지가 말했다. “멍~! 멍~!” 정말 귀엽다
하지만 이걸 그대로 printf로 출력하면 오류가 난다. “멍~! 에서 끝났다고 인식하기 때문이다 .
printf("앞집 강아지가 말했다. \"멍~! 멍~!\"정말 귀엽다.")
이렇게 해주면 출력이 되는 것을 알 수 있다
여기서 폼피드(/f), 수직탭(/v)는 프린터 출력을 위한 거기 때문에 이상한 문자가 출력이 된다..?
printf 함수의 서식지정
printf 는 서식이 지정된 형태의 출력으로서 출력양식이 만들어진 상태에서 출력을 진행한다 .
#include <stdio.h>
int main(void)
{
int myAge=12;
printf("제 나이는 10진수로 %d살, 16진수로 %x살입니다. \n", myAge, myAge);
return 0;
}
제 나이는 10진수로 12살, 16진수로 C살 입니다.
서식문자의 종류와 그 의미
정수 출력을 위한 서식문자들: %d, %u, %o, %x
위의 표를 보면 8진수 %o, 16진수 %x를 이용해서는 양의 정수만 출력이 가능하다.
음의 정수를 출력하면서 %d를 사용하여야 하고 8진수와 16진수는 음의 정수 표현을 거의 사용되지 않는다
8진수를 8진수답게 표현 하려면 %#o , 16진수를 16진수처럼 표현하려면 %#x를 이용하면 된다.
#include <stdio.h>
int main(void)
{
int num1=7, num2=13;
printf("%o %#o \n", num1, num1);
printf("%x %#x \n", num2, num2);
return 0;
}
7 07
d 0xd
실수의 출력을 위한 서식문자들 : %f, %e, %g
#include <stdio.h>
int main(void)
{
printf("%f \n", 0.1234);
printf("%e \n", 0.1234);
printf("%f \n", 0.12345678);
printf("%e \n", 0.12345678);
return 0;
}
0.123400
1.234000e-001
0.123457
1.234568e-01
여기서 나오는 e-01 표기법이 e표기법이라고 하며 ‘지수 형택의 출력’을 표현 한 것이다.
즉 $1.0X10 ^{-20}$ = 1.0e-20이 되는 것이다.
#include <stdio.h>
int main(void)
{
double d1=1.23e-3; //0.00123 = -3이면 정수부분이 세번째 부터 시작.
double d2=1.23e-4; //0.000123
double d3=1.23e-5;
double d4=1.23e-6;
printf("%g \n", d1);
printf("%g \n", d2);
printf("%g \n", d3);
printf("%g \n", d4);
return 0;
}
0.00123
0.000123
1.23e-005
1.23e-006
“%g는 소수점 이하의 자릿수가 늘어나면 e표기법으로 출력한다. 대충 6자리 까지 표현되는 듯하다.”
%s를 이용한 문자열 출력
%s는 오직 문자열만 출력하고 숫자는 안된다.
#include <stdio.h>
int main(void)
{
printf("%s, %s, %s \n", "AAA","BBB","CCC");
return 0;
}
AAA, BBB, CCC
#include <stdio.h>
int main(void)
{
printf("%s, %s, %s \n", "AAA", 123, "123");
return 0;
}
이렇게 하면 버그가 난다; 123때문이고 “123”은 출력잘됨
필드 폭을 지정하여 정돈된 출력 보이기
%8d = 필드 폭 8칸 확보, 오른쪽 정렬해서 출력 진행
%-8d = 필드 폭 8칸 확보, 왼쪽 정렬해서 출력 진행
#include <stdio.h>
int main(void)
{
printf("%-8s %14s %5s \n", "이 름", "전공학과", "학년");
printf("%-8s %14s %5d \n", "우투리", "겨드랑이학", 3);
printf("%-8s %14s %5d \n", "김개똥", "반도체공학", 4);
printf("%-8s %14s %5d \n", "김첨지", "삼계탕학", 2);
return 0;
}
이름 전공학과 학년
김동수 전자공학 3
이을수 컴터공학 2
한선영 미술교육 4
이름은 -로 왼쪽정렬되어있고 학과는 오른쪽 정렬 학년은 오른쪽 정렬이다 .
scanf 함수 이야기
scanf 함수를 호출을 위해서는 2가지 (1)입력형식 (2)입력장소가 필요하다.
%d 10진수 정수의 형태로 데이터를 입력 받는다.
%o 8진수 양의 정수의 형태로 데이터를 입력 받는다.
%x 16진수 양의 정수의 형태로 데이터를 입력 받는다.
#include <stdio.h>
int main(void)
{
int num1, num2, num3;
printf("세 개의 정수 입력하세요: ");
scanf("%d %o %x", &num1, &num2, &num3);
printf("입력된 정수 10진수 출력: ");
printf("%d %d %d \n", num1, num2, num3);
return 0;
}
세 개의 정수 입력하세요 : 12 12 12
입력된 정수 10진수 출력: 12 10 18
주의해야되는건 세 개의 정수 12를 입력했다고 해서 메모리에 12가 저장되는게 아니라 2진 바이너리 형태로 저장된다. 그 값을 10진수로 해석하고 8진수로 해석하고 16진수로 해석하는 것이다.
실수 기반의 입력형태 정의하기
printf 함수에서는 서식문자 %f, %e. %g의미가 있는데 %e, %g는 거의 안쓴다고 한다;
scanf 함수에서는 세개 다 float형 데이터를 입력 받겠다는 의미를 담고있다 .
%lf = double %f에 l이 추가된 형태
#Lf = long double %f에 L이 추가된 형태
#include <stdio.h>
int main(void)
{
float num1;
double num2;
long double num3;
printf("실수 입력하세요 (e표기법): ");
scanf("%f", &num1);
printf("입력된 실수 %f \n", num1);
printf("실수 입력하세요 (e표기법): ");
scanf("%lf", &num2);
printf("입력된 실수 %f \n", num2);
printf("실수 입력하세요 (e표기법): ");
scanf("%Lf", &num3);
printf("입력된 실수 %f \n", num3);
return 0;
}
실수 입력 : 1.1e-3
입력된 실수: 0.001100
실수 입력: 0.1e+2
입력된 실수: 10.000000
실수 입력: 0.17e-4
입력된 실수: 0.000017
실수의 입력과정에서 e표기법을 사용해도 가능하다!
float는 입력 %f, 출력 %f
double은 입력 %lf, 출력 %lf
long double은 입력 %Lf, 출력 %Lf
이라는 것을 알 수 있다.!
-
[C언어의 기본] 기본 연산자와 함수
C언어란?
C언어는 ‘프로그래밍 언어’이다.
*프로그래밍언어: 컴퓨터와 대화에서 사용되는 일종의 대화수단
A라는 사람이 한국어, 영어
B라는 사람이 영어, 일본어
C라는 사람이 일본어
가능한다는 조건이 있다.
a -(영어) -> B -(일본어) -> C
이렇게 B라는 통역가를 통해서 A의 말을 C에게 전달하는 것이다.
프로그래밍에서는 컴파일러(Compiler)라는 통역가라는 것이 존재한다.
A라는 사람이 한국어, C언어
B컴파일러는 C언어, 기계어
C컴퓨터는 기계어
이렇게 위와 같이 A라는 사람의 C언어를 컴파일러를 통해 기계어로 해준다.
*프로그래밍언어는 사람과 컴파일러가 이해할 수 있는 약속된 형태의 언어로서 C언어도 프로그래밍언어이다. (java, C++, C#…)
*컴파일러는 프로그래밍언어로 작성된 프로그램을 컴퓨터가 이해할 수 있도록 기계어로 번역한다. 번역하는 일을 컴파일(Compile)이라 한다.
*기계어(Machine Language)? : 컴퓨터가 이해할 수 있는 0과 1로 구성된 언어 체계이다.
C언어의 장점
(1)절차지향적 특성 (순서대로 실행)
(2)이식성이 좋다 (CPU종류에 상관없고 운영체재 차이도 덜 민감)
(3)좋은 성능을 보인다. (메모리양이 상대적으로 적고, 속도 저하 요소가 최소화)
프로그램 완성과정
프로그램 완성과정은 위와같다.
1.프로그램작성을 통해 소스파일(.c)파일을 생성한다.
2.컴파일을 통해서 오브젝트 파일(.obj)파일을 생성한다.
3.링크를 통해 실행파일(.exe)을 생성한다.
4.실행파일 생성 후 실행한다(cpu)
프로그램 기본구성
“C언어는 함수로 시작해서 함수로 끝난다”
C언어로 프로그램을 만드는 것은 ‘함수를 만들고, 함수의 실행순서를 결정하는 것’이다.
3x+4=y
예를 들면 x가 1이라는 가정하에 x에 1을 넣으면 7이 된다.
여기서 x값을 입력, y값을 출력이라고 한다.
적절한 입력과 그에 따른 출력이 존재하는 것을 ‘함수’라 한다.
int main (void)
이런 문구를 많이 볼 수 있는데 처음에는 이 뜻을 모르고 그냥 외우기만 했다.
왜 int ? main ? void인가?
하지만 책을 통해 알 수 있었다.
int = 출력형태
main = 함수이름
void = 입력형태
‘출력의 형태는 int, 입력의 형태가 void인, main함수이다’
int main (void)
{
함수의 몸체
}
그리고 이러한 main함수의 기능을 담당하는 것이 함수의 몸체이다.
이것을 통해 나는
‘void입력을 가지고 main 함수로 가져와 함수의 기능을 수행하여 int로 반환하라’ 라고 알 수 있었다.
함수 내에 존재하는 문장의 끝에는 세미클론 ;을 붙힌다. (문장의 끝을 표현)
Hello World 알아보기
printf(“Hello World! \n”);
어느 책이든 Hello World, Hello C World로 구성되는 것 같은건 기분 탓일까?
C언어는 “ “ 을 통하여 문자열을 표현한다. 위 문장은 “Hello World! \n”을 인자로 전달하면서
printf라는 이름의 함수를 호출하는 것이다.
printf라는 함수를 만든적이 없는데 어떻게 호출할 수 있는가? : 기본제공함수(표준함수)가 있기 때문이고 표준함수의 모임을 ‘표준라이브러리’라고 한다.
표준함수 호출을 위해서는 그에 맞는 헤더파일 선언이라는 것을 해야한다.
why? 도서관을 가져와 그에 필요한 책(헤더파일)을 부르듯이..
#include
stdio.h라는 확장자가 .h로 끝나는 헤더파일을 포함하라는 의미의 선언이다.
#(전처리기) include(포함하다) (standard input/Output.Header - 표준입출력)
헤더파일의 선언은 소스파일의 맨 앞부분, main함수 정의 이전에 와야한다.
return 0;
마지막에 위와같은 내용이 많이 붙는데 솔직히 몰랐다. ㅋㅋㅋ;;
위는 return문이라고 하는데 의미는 2가지이다.
(1)함수를 호출한 영역으로 값을 전달(반환)
(2)현재 실행중인 함수의 종료
return 0;는
OS에게 0을 전달하고 main 함수를 종료시키는 것이다.
return ;으로 한다면
반환할 값이 없으니 main 함수를 그냥 종료시키라는 것이다.
가지고 놀아보기
문제 1: 본인의 이름을 출력하는 프로그램을 작성해보자. 단 printf 함수를 한번만 호출해야한다.
1.홍길동
2.홍길 동
3.홍 길 동
#include <stdio.h>
int main (void)
{
printf("홍길동\n홍길 동\n홍 길 동\n");
return 0;
}
문제2: 본인의 이름, 주소, 전화번호를 출력하는 프로그램을 작성해보자 총 3번의 printf함수를 사용한다.
#include <stdio.h>
int main (void)
{
printf("이름: 홍길동\n");
printf("주소: 서울특별시 어쩌구..\n");
printf("전화번호: 010-1111-2222\n");
return 0;
}
주석 이해하기
“주석은 선택이 아닌 필수이다!”
주석은 블록단위, 행단위가 있다.
블록단위 주석 /* */ (안의 내용은 전부 주석)
행단위 주석 // (그 줄만 주석으로 처리)
*프로젝트의 진행에 앞서 어떠한 형태로 어느 수준으로 주석을 달 것인지 결정하고 따른다고 한다.
/*
제목 : Hello World 출력시키기
기능 : 문자열의 출력
파일이름 : HelloCommnet.c
수정날짜 : 2024.01.02
작성자 : 미누
*/
#include <stdio.h> //헤더파일의 선언
int main(void) //main함수 시작
{
printf("Hello World! \n"); //문자열 출력
return 0; //0을 반환시키기
}
주석은 간단하고 명료해야된다. 복잡하면 오히려 해가되니 조심하자!
블록 단위의 주석은 중첩될 수 없다.
/*
주석처리 된 문장
/*단일 행 주석처리시키기 */
주석처리 된 문장2
*/
1행에서 주석처리를 시작하고 2행과 3행이 주석처리되고 3행의 마지막 주석처리기 시키기 뒤에 주석끝으로 인식하고 4행과 5행은 주석으로 인식되지 않는다.
/*
주석처리 된 문장
// 단일행 주석처리
주석처리 된 문장2
*/
위와 같이 바꾸게 되면 주석에는 이상이 없어지니 블록단위의 주석을 할떄는 주의하자!
Printf 함수의 기본적인 이해
#include <stdio.h>
int main(void)
{
printf(Hello Everybody \n");
Printf("%d\n", 1234);
printf("%d %d\n", 10, 20);
return 0;
}
실행결과를 보면
Hello Everybody
1234
10 20
을 알 수 있듯이. 위의 프로그램 동작을 통해 C언어는 순차적으로 실행된다는 사실을 알 수 있다.
그리고 Printf를 통해서 함수를 이용하여 문자열, 정수데이터를 출력할 수 있음을 알 수 있다.
즉 ‘Printf 함수는 첫번째 인자로 전달된 문자열을 출력한다’
여기서 %d라는 것이 처음등장하게 되는데 이러한 문자를 가리켜 ‘서식문자(ConverSion Specifier)’이라고 한다.
출력의 형태를 지정하는 용도로 사용이 된다.
Printf(인자1("%d\n"), 인자2(1234)); => printf("%d\n",1234)
%d에 1234라는값이 들어가게 되어 출력문은 “1234\n”이 된다.
좀더 응용하면 printf(“%d %d\n”, 10, 20);을 보자.
printf(인자1 ("%d %d\n"), 인자2(10),인자3(20)); => printf("%d %d\n", 10, 20);
첫번쨰 %d에는 인자2값 10, 두번쨰 %d에는 인자3값 20이 들어가게 되어 출력문은 “10 20\n”이 된다.
의외에도 서식문자가 많지만 진도를 나가면서 보여준다고 하셨으니 이정도만 기억하고자 한다.
또한 출력의 형태를 다양하게 조합가능 하다고 한다.
#include <stdio.h>
int main(void)
{
printf("My age: %d \n", 27);
printf("%d is my point \n", 100);
printf("Good \n moring \neverybody\n");
return 0;
}
My age: 27
100 is my Point
Good
moring
everybody
출력이 되는 것으로 여러가지로 조합 할 수 있다.
다른 강의에서는 %d, %i (10진수 정수) , %x, %o (16진수), %f, %lf(10진수 실수)
%c(문자) = ‘ ‘ , %s(문자열)= “ “, %u(10진수의 정수 -양수) 가 있다고 해서 연습만 해보았다.
#include <stdio.h>
int main(void)
{
printf("10진수:%d는 16진수:%x, 8진수: %o 입니다. \n", 50,50,50);
printf("10진수:%d는 16진수:%x, 8진수: %o 입니다. \n", -50, -50, -50);
return 0;
}
10진수:50, 16진수:32 8진수: 62 입니다.
10진수 -50, 16진수fffffce, 8진수:377777777716입니다.
하단의 %x, %o를 적용한 곳은 음수출력이 되지 않기에 쓰레기 값으로 나온다는 사실을 알 수 있다.
가지고 놀아보기
문제1: 출력결과를 보이도록 작성해보자. 출력되는 숫자들(20, 123, 456)은 서식문자를 이용하여 출력하자.
제 이름은 미누입니다.
제 나이는 27살이고요.
제가 사는 번지는 123-456입니다.
#include <stdio.h>
int main(void)
{
printf("제 이름은 미누입니다.\n");
printf("제 나이는 %d이고요.\n",27);
printf("제가 사는 번지는 %d-%d 입니다.",123,456);
return 0;
}
문제2: 출력결과를 보이도록 작성해보자. 출력되는 숫자들은 서식문자를 활용하자.
4x5=20
7x9=63
#include <stdio.h>
int main(void)
{
printf("%dx%d=%d\n",4,5,20);
printf("%dx%d=%d",7,9,63);
return 0;
}
혹시 곱셈을 이용한 것도 되는지 확인해보고 싶어졌다 해보자.
#include <stdio.h>
int main(void)
{
printf("%d*%d=\n",4,5);
printf("%d*%d=\n",7,9);
return 0;
}
이렇게 하면 안되는 듯.. printf는 그냥 서식문자에 문자열을 박아주는 것 같다.
“ “ 사이의 계산식을 넣어도 구동이 안된다
#include <stdio.h>
int main(void)
{
printf("%dx%d=%d\n", 4,5,4 * 5);
return 0;
}
혹시 아예 서식문자에 대한 값을 넣어주고 마지막 값을 20이 아닌 4*5라는 계산식을 넣어주면 작동하는지 확인해봤다.
아주 잘된다. 하지만 나중에 수정이 어려울 거 같으니 변수를 이용하는게 훨 씬 이로울 것 같다.
변수와 연산자
#include <stdio.h>
int main(void)
{
3+4: //3과 4의 덧셈
return 0;
}
문제없이 출력되는 예제가 있다. C언어는 특정연산을 요구할때 사용하는 약속된 기호를 가리켜 ‘연산자(Operator)’라고 한다.
해당 프로그램을 실행하면 결과가 안나오는데 그는 출력값이 없기 떄문이다.
이를 변경해보자
‘덧셈연산을 한 후 그 결과를 메모리 공간에 저장한다. 그리고 그 저장된 값을 출력한다.’
할 수 있게 된다면 다양한 형태로 출력할 수 있는데 이를 위해서는 덧셈결과를 저장하기 위해 변수(variable)를 이용해야한다.
int main(void)
{
int num; //num변수 선언
....
}
위를 참고하면 int와 num이 등장한다.
int 정수의 저장이 가능한 메모리 공간을 할당한다.
num 메모리 공간의 이름을 num이라 지정한다.
정수는 -1, 0, 1…
실수는 1.2, 1.3, 3.1 …
이렇게 서식문자와 변수를 사용하면 변경할 수 있게 된다.
#include <stdio.h>
int main(void)
{
int num; // 변수 num 선언
num=20; //변수 num에 값 20을 저장
print("%d", num); // 변수 num 값을 참조하여 출력
}
선언된 변수를 처음 값을 저장하는 것을 가리켜 ‘초기화’라고 한다.
초기화 이후 저장된 값을 변경할 떄에는 ‘대입’또는 ‘대입연산’을 진행한다고 한다.
변수를 통해 ‘선언과 동시에 초기화’ 하는것이 가능하다.
int num=12;
해당연산을 실행하게 되면, 변수 num이 메모리 공간을 할당 받자마자 12로 초기화 된다.
그리고 다음 둘 이상의 변수를 동시에 선언도 가능하고 동시에 선언 및 초기화도 가능하다.
int num1, num2; //변수 선언
int num3=30, num4=40; // 선언 및 초기화
좀 더 구체적인 예를 보자
#include <stdio.h>
int main(void)
{
int num1, num2; //변수 num1, num2의 선언
int num3=30, num4=40; //변수 num3, num4의 선언 및 초기화
printf("num1: %d, num2: %d \n",num1,num2);
num1=10; // num1 초기화
num2=20; // num2 초기화
printf("num1: %d, num2: %d \n",num1,num2);
printf("num3: %d, num4: %d \n",num3,num4);
return 0;
}
구동이 되지 않은데 아마도 쓰레기값이 발생해서 그런 것 같다.
num1: 쓰레기값 , num2:쓰레기값
num1: 10 , num2: 20
num3: 30 , num4: 40
변수 선언시 주의 할 사항
“중괄호 내에 변수를 선언할 경우, 변수의 선언문은 중괄호의 앞부분에 위치해야 한다.”
int main(void)
{
int num1; //앞부분에 위차한 선언문
int mun2;
num1=0; //변수 선언 이후에 등장한 초기화 문장
num2=0; //변수 선언 이후에 등장한 초기화 문장
...
}
1999년도 발표된 C언어표준에서는 변수의 선언문 위치에 아무런 제한을 두지 않는다고 한다.
하지만 아직 상당수의 컴파일러가 변수 선언문 앞 부분을 요구하니 주의하자!
변수 선언시 주의해야 되는 점이 있다.
변수의 이름은 알파벳, 숫자, 언더바(_)로 구성된다.
변수의 이름은 대소문자를 구별한다. num!=Num
변수의 이름은 숫자로 시작할 수 없고,키워드도 변수의 이름으로 사용할 수 없다.
이름 사이에 공백이 삽입될 수 없다.
int 7sadkmsakd <불가능
int korea# <특수문자 불가능
int Poker game <공백 발생
그리고 “변수의 이름을 정할때는 의미 있는 이름을 지어야한다.” <이게 제일 어렵다고 합니다..
변수의 자료형
변수는 크게 2가지로 나뉜다.
1.정수형 변수 = int, char, short, long ..
2.실수형 변수 = float, double ..
2가지로 나뉘는 이유는 정수냐, 실수냐에 따라서 값이 메모리 공간에 저장 및 참조되는 방식이 다르기 때문이다.
(정수와 실수의 저장방식이 다르다)
int num1=24; // num1을 가리켜 int형 변수
double num2=3.14; //num2를 가리켜 doulbe형 변수
덧셈 프로그램의 완성
#include <stdio.h>
int main(void)
{
int num=3;
int num=4;
int result=num1+num2;
printf("덧셈결과: %d \n", result);
printf("%d+%d=%d \n", num1, num2, result);
printf("%d와 %d의 합은 %d입니다.\n", num1, num2, result);
return 0;
}
덧셈결과: 7
3+4 = 7
3와 4의 합은 7입니다.
단항 연산자와 이항연산자
단항연산자는 피연산자의 개수가 1개 - ex) ++num
이항연사자는 피연사자의 개수가 2개 - ex) 3+4
다양한 연산자 소개
| 연산자 | 기능 | 결합방향 |
| = | 오른쪽 값을 왼쪽에 대입함 | <- |
| + | 두 피연산자의 값을 더한다 | -> |
| - | 왼쪽 값에서 오른쪽 값 뺀다 | -> |
| * | 두 피연산자의 값을 곱한다 | -> |
| / | 왼쪽 값에서 오른쪽 값을 나눈다 | -> |
| % | 왼쪽 값에서 오른쪽 값을 나눈 나머지 값을 반환 | -> |
연산자의 사용 예를 한번 보도록 하자.
#include <stdio.h>
int main(void)
{
int num1=9, num2=2;
printf("%d+%d=%d \n", num1, num2, num1+num2);
printf("%d-%d=%d \n", num1, num2, num1-num2);
printf("%dx%d=%d \n", num1, num2, num1*num2);
printf("%d/%d=%d의 몫 \n", num1, num2, num1/num2);
printf("%d/%d=%d의 나머지 \n", num1, num2, num1%num2);
return 0;
}
9+2=11
9-2=7
9x2=18
9/2의 몫 =4
9/2의 나머지 = 1
위를 통해 “함수 호출문이 인자전달 위치에 연산식이 올수 있다.”
아까 연습했던게 여기에 나온 것 같다 굿굿
그리고 num1+num2부분에서 우선연산이 실행된다.
복합대입연산자
*=,/=,%=,+=,-=,«=,»=,&=,^=,/=
a= a+b <=> a +=b
a= a-b <=> a -=b
a= a*b <=> a *=b
a= a/b <=> a /=b
a= a%b <=> a %=b
대충 겹치는 부분을 사라지게 해서 코드를 더 짧게 하는 것 일지도?
#include <stdio.h>
int main(void)
{
int num1=2, num2=4, num3=6;
num1 +=3;
num2 *=4;
num3 %=5;
printf("Result: %d, %d, %d \n", num1, num2, num3);
return 0;
}
Result : 5, 16, 1
num1 +=3 은 num1=num1+3; = 5
num2 =4 는 num2=num24 = 16
num3 %=5 는 num3=num3%5 = 1
주의할점
주의 할점으로는 부호연산과 헷갈리지 않도록 조심해야한다.
#include <stdio.h>
int main(void)
{
int num1 = +2;
int num2 = -4;
num1 = -num1;
printf("num1: %d\n",num1);
num2 = -num2;
printf("num2: %d\n",num2);
return 0;
}
num1 = -2
num2 = 4
위처럼 헷갈릴 수 있다고 한다.
num1=-num2; //부호 연산자
num1-=num2; // 복합대입연산자
그래서 공백을 두는 것이 혼란을 최소화 할 수 있다.
num1 = -num2;
num1 -= num2;
증가, 감소 연산자
표를 그리기 어렵기에 사진으로 대체한다..;;
위처럼 앞이냐 뒤냐에 따라 나뉜다.
#include <stdio.h>
int main(void)
{
int num1=12;
int num2-12;
printf("num1= %d \n", num1);
printf("num1++= %d \n", num1++);
printf("num1= %d \n", num1);
printf("num2= %d \n", num1);
printf("num2++= %d \n", ++num1);
printf("num2= %d \n", num1);
}
num1: 12
num1++: 12
num1: 13
num2: 12
++num2: 13
num2:13
선연산 후 증가인지 선증가 후연산에 따라 다르다.
#include <stdio.h>
int main(void)
{
int num1=10;
int num2=(num1--)+2;
printf("num1: %d \n", num1);
printf("num2: %d \n", num2);
return 0;
}
num1: 9
num2: 12
이를 보면 int num2 =(num1–)을 통해 num1은 num2의 초기화 이후 9로 변하게 되어 printf를 통해 9로 보인다.
int num2는 10+2를 시킨 후 num1이 떨어진 것이기 때문에 num2= 12가 출력된다.
but) ++num이 아닌 ++3은 실행되지 않는다. 변수의 지정한 값을 변형시키는 것이기 때문이다.
이것을 통해 ‘소괄호의 영향을 받지 않고, 다음 문장으로 넘어가야만 비로소 값의 증가 및 감소가 이뤄진다’
관계연산자
조건을 만족하면 1을 , 만족하지 않으면 0을 출력한다. (true, false)
#include <stdio.h>
int main(void)
{
int num1=10;
int num2=12;
int result1, result2, result3;
result1=(num1==num2);
result2=(num<=num2);
result3=(num>=num3);
printf("result1: %d \n", result1);
printf("result2: %d \n", result2);
printf("result3: %d \n", result3);
return 0;
}
result1: 0
result2: 1
result3: 0
첫번째는 10과 12가 같지 않으니 0
두번쨰는 12가 10보다 크니 1
세번째가 10이 12보다 작으니 0
즉 result1은 num1 ==num2의 ture(1)값을 반환한 것이다.
논리연산자
논리연산은 디지털 논리회로의 AND, OR, NOT 과 같으니
&&, ||, !를 들어다본다.
#include <stdio.h>
int main(void)
{
int num1=10;
int num2=12;
int result1, result2, result3;
result1=(num1==10 && num2==12);
result2=(num<12 || num2>12);
result3=(!num1);
printf("result1: %d \n", result1);
printf("result2: %d \n", result2);
printf("result3: %d \n", result3);
return 0;
}
result1 = 1
result2 = 1
result3 = 0
이를 보면
result1은 num1==10 AND num2=12이기에 num1==10은 true(1), num2==12는 true(1)이기에 true true로 1이다.
result2는 num1<12 OR num2>12 이기에 num1<12은 true(1), num2>12은 false(0)이기에 ture false로 1이다.
result3은 1이상의 값은 True로 인식되기에 true의 not인 false(0)이 출력된다.
즉 “C언어는 0이 아닌 값은 참(true)으로 간주한다’라는 것을 알 수 있다.
콤마 연산자
둘 이상의 변수를 동시 선언하거나, 둘 이상의 문장을 한행에 삽입하는 경우이며 구분을 목적으로 주로 사용한다.
#include <stdio.h>
int main(void)
{
int num1=1, num2=2;
printf("Hello "),printf("World! \n");
num1++, num2++
printf("%d",num1), printf("%d", num2), printf("\n");
return 0;
}
Hello World!
2 3
으로 문장을 여러개 삽입하여 출력할 수 있음을 알게됐다.
연산자의 우선순위와 결합방향
우리가 +,-,*,/ 처럼 사칙연산의 우선순위가 있는것 처럼 연산자도 우선순위가 있다.
+결합방향도 있는데 이건 외우기보다는 천천히 익혀가는게 좋을 것 같다.;;
Scanf 함수
scanf 함수를 활용하면 키보드로 입력받은 형태의 데이터를 받을 수 있다.
int main(void)
{
int num;
scanf("%d", &num);
...
}
이것을 통해 %d = 10진수 정수 형태로 입력받아서 num변수에 저장하라는 것이다.
변수 num 앞에 연산자 &는?
해당 이유는 주소값 반환이지만 일단 지금은 scanf함수의 호출을 위해서는 저장할 변수 이름 앞에 &를 붙힌다고만 알자.
#include <stdio.h>
int main(void)
{
int result;
int num1, num2;
printf("정수 One: ");
scanf("%d", &num1);
printf("정수 Two: ");
scanf("%d", &num2);
result=num1+num2;
printf("%d + %d = %d \n", num1, num2, result);
return 0;
}
뭐지 출력이 안된다..;
scnaf의 반환값이 무시된다고 한다.
구글링 해보니까
앞에 # pragma warning(disable:4996)를 붙혀주면 된다고 한다.
정수 One: 1
정수 Two: 2
를 입력하니
1+2=3이 나왔다.
입력의 형태를 다양하게 지정가능
int main(void)
{
int num1, num2, num3;
scanf("%d %d %d", &num1, &num2, &num3);
...
}
3개의 10진수 정수를 입력가능하다.
#include <stdio.h>
int main(void)
{
int result;
int num1, num2, num3;
printf("세 개의 정수를 입력: ");
scanf("%d %d %d", &num1, &num2, &num3);
result=num1+num2+num3;
printf("%d + %d + %d = %d \n", num1, num2, num3, result);
return 0;
}
오류가 나서 다시 위의 값을 붙혀준 후 실행한다.
세 개의 정수를 입력 3 4 5
3 + 4 + 5 = 12
scnaf함수는 공백을 기준으로 데이터를 구분하는 것을 알 수 있다.
가지고 놀아보기
문제1: 프로그램 사용자로부터 두개의 정수를 입력받아서 두 수의 뺄셈과 곱셈의 결과를 출력하는 프로그램 작성하기
#include <stdio.h>
int main(void)
{
int num1,num2;
printf("두 개의 정수를 입력하세요: ");
scanf("%d %d",&num1,&num2);
printf("%d - %d = %d \n", num1, num2, num1-num2);
printf("%d x %d = %d \n", num1, num2, num1*num2);
return 0;
}
두 개의 정수를 입력하세요 : 3 4
3 - 4 = -1
3 x 4 = 12
문제2: 세개의 정수를 순서대로 입력받은 후 다음 연산의 결과를 출력하는 프로그램을 작성하라.
(num1xnum2+num3)
#include <stdio.h>
# pragma warning(disable:4996)
int main(void)
{
int num1,num2,num3;
printf("세 개의 정수를 입력하세요: ");
scanf("%d %d %d", &num1, &num2, &num3);
printf("%dx%d+%d=%d\n", num1, num2, num3, num1*num2+num3);
return 0;
}
세 개의 정수를 입력하세요: 1 2 3
1x2+3=5
문제3: 하나의 정수를 입력받아서 그 수의 제곱의 결과를 출력하는 프로그램을 작성해보자.
#include <stdio.h>
# pragma warning(disable:4996)
int main(void)
{
int num1;
printf("정수 값을 입력하세요: ");
scanf("%d", &num1);
printf("%d 의 제곱값은 %d 입니다\n", num1, num1*num1);
return 0;
}
#include <stdio.h>
# pragma warning(disable:4996)
int main(void)
{
int num1;
printf("정수 값을 입력하세요: ");
scanf("%d", &num1);
printf("정수 값의 제곱은 %d 입니다\n", num1*num1);
return 0;
}
정수 값을 입력하세요: 3
정수 값의 제곱은 9입니다.
3의 제곱값은 9입니다.
문제4: 입력 받은 두 정수를 나누었을 때 얻게 되는 몫과 나머지를 출력하는 프로그램을 작성하세요.
#include <stdio.h>
# pragma warning(disable:4996)
int main(void)
{
int num1, num2;
printf("두 개의 정수를 입력하세요: ");
scanf("%d %d", &num1, &num2);
printf("몫의 값은 : %d , 나머지의 값은 %d 입니다", num1/num2, num1%num2);
return 0;
}
두 개의 정수를 입력하세요: 3 6
몫의 값은 0 , 나머지의 값은 3입니다.
#include <stdio.h>
# pragma warning(disable:4996)
int main(void)
{
int num1, num2;
printf("두 개의 정수를 입력하세요: ");
scanf("%d %d", &num1, &num2);
printf("%d/%d의 몫의 값은 : %d , %d % %d 나머지의 값은 %d 입니다", num1,num2,num1/num2,num1,num2, num1%num2);
return 0;
}
두 개의 정수를 입력하세요: 3 6
3/6 몫의 값은 0, 3 %d 나머지의 값은 6입니다.
흠.. 뭔가 이상하다. % %d에서 인식을 못하는 것 같다.
어떻게 풀어야할까? 잘 모르겠다…
%는 서식문자 지정기호이므로 %를 표현하려면 %%라고 해야된다고 한다.
그러면 이렇게 정리할 수 있겠다.
#include <stdio.h>
# pragma warning(disable:4996)
int main(void)
{
int num1, num2;
printf("두 개의 정수를 입력하세요: ");
scanf("%d %d", &num1, &num2);
printf("%d/%d의 몫의 값은 : %d , %d %% %d 나머지의 값은 %d 입니다", num1, num2, num1 / num2, num1, num2, num1 % num2);
return 0;
}
문제5: 입력받은 세 개의 정수를 대상으로 다음의 연산 결과를 출력하는 프로그램을 작성해보자.
(num1-num2)x(num2+num3)x(num3%num1)
#include <stdio.h>
# pragma warning(disable:4996)
int main(void)
{
int num1, num2, num3;
int result;
printf("세 개의 정수를 입력하세요: ");
scanf("%d %d %d", &num1, &num2, &num3);
result=(num1-num2)x(num2+num3)x(num3%num1);
printf("정답: %d \n", result);
return 0;
}
#include <stdio.h>
# pragma warning(disable:4996)
int main(void)
{
int num1, num2, num3;
printf("세 개의 정수를 입력하세요: ");
scanf("%d %d %d", &num1, &num2, &num3);
printf("정답: %d \n", (num1-num2)*(num2+num3)*(num3%num1);
return 0;
}
세 개의 정수를 입력하세요: 1 2 3
정답: 0
아무래도 result 를 사용하는게 나중에 더 유지보수 하기 편할 것 같기에 묶어주는 것도 좋은 것 같다.
C언어의 표준 키워드
auto,
_bool,
break,
case,
char,
_complex,
const,
continue,
default,
do,
double,
else,
enum,
extern,
float,
for,
goto,
if,
_Imaginary,
return,
restrict,
short,
signed,
sizeof,
static,
struct,
switch,
typedef,
union,
unsigned,
void,
volatile,
while,
등이 있다고 하며
프로그래머가 다른용도로 사용할 수 없도록 제한되어 있다고 한다.!
Touch background to close