본문 바로가기
  • 適者生存
WorkOut/정보처리기사

C언어 | 구조체와 함수

by lcrvvxln 2024. 3. 19.

 

적자생존

11. 구조체 (Structure Type)


- 사용자가 기본 자료형으로 새롭게 정의하는 사용자 정의 자료형

  • 구조체 선언
struct 구조체명{
   자료형 변수명1;
   자료형 변수명2;
   ...
};

struct 구조체명 구조체 변수;

- 구조체 변수, 변수명 형태로 값을 선언


#include <stdio.h>

struct Student{
   char gender;
   int age;
};

int main(){
   struct Student s={'F',21};
   s.gender='M';
   printf('%c',s.gender);
   printf('%d',s.age);
   return 0;
}

# M21
  • main 함수부터 시작
  • Student 구조체 s 선언, s.gender 'F', s.age 21로 초기화
  • s.gender 값 'M'으로 변경
  • M 출력 > 21 출력

 

정수형 변수 int a; 와 같이
구조체도 struct Student s; 처럼 선언해야 사용할 수 있음

> struct [구조체명] [구조체 변수명];




 

12. 함수

(1) main 함수


- 모든 프로그램의 시작점
- main 함수 안 명령어 실행

자료형 main(파라미터){
   명령어;
}
  • void main( ) : 반환 값 없음을 의미, return;  사용 OR return 자체 사용 X
  • int main ( ) : return 반환값; 명시 필요
** void (=존재하지 않음)

함수 호출자에 결괏값을 제공하지 않는다는 의미의 자료형
반환값이 없다는 의미
  • main 함수와 사용자 정의 함수는 return 만나는 즉시 함수 종료

 

 

(2) 사용자 정의 함수


- 사용자가 직접 새로운 함수 정의해서 사용
- 사용자 정의 함수의 매개변수 & 생성된 변수는 함수 종료 시, 소멸됨

- 매개변수(Parameter)

함수 호출 시, 인수로 전달된 값으로 함수 내부에서 사용 가능하게 해주는 변수

 

[1] 사용자 정의 함수 선언

자료형 함수명(자료형 변수명, ...){
   명령어;
   return 반환값;
}
- 사용자 정의 함수 사용 시 주의할 점

lc라는 사용자 정의 함수가 있을 때, lc가 main 함수 아래에 작성되어 있고 main에서 lc를 호출한다면 main은 lc가 무엇인지 몰라 에러 발생

1. 사용자 정의 함수는 main 함수 위에 정의
2. 사용자 정의 함수 lc가 함수라는 것을 위쪽에 명시(선언)


ex.1
#include <stdio.h>
int lc (int x){  #int x 가 매개변수
   return 3; # 사용자 정의 함수 반환값
}

int main( ){
   printf('%d', lc(5); #사용자 정의 함수로의 전달인자이자 입력값
}


ex. 2
lc(int a, int b); # 사용자 정의 함수 lc 명시

  int main( ){
     lc(3,5);
}
int lc(int a, int b){
...
}

 

#include <stdio.h>

char fn(int num){
   if(num % 2==0)
     return 'Y';
   else
     return 'N';
}

int main( ){
   char a=fn(5);
   printf('%c\n',a);
   return 0;
}

# N
  • main 함수 부터 시작
  • 사용자 정의 함수 fn에 인자 5를 넣은 결과값으로 문자형 변수 a 초기화
  • fn의 매개변수 num에 5 대입 > 5%2!=0 이므로 else문을 통해 'N' 반환, a='N', return 이므로 fn 함수 종료
  • 사용자 정의 함수 fn 종료와 동시에 매개변수 num 소멸
  • 다시 main으로 돌아와서 a 값인 'N' 출력

 

[2] 매개변수 전달 방법

- 매개변수 전달은 함수가 필요로 하는 값을 매개변수로 만들어 함수 호출 쪽에서 매개변수 사용하여 함수에 변수 값과 주솟값을 전달하는 방식

  • 매개변수 전달 방법 구성요소

1. 전달인자 (Argument) : 실 매개변수라고 불림, 함수 호출 쪽에서 전달하는 변수 값 또는 변수 주솟값
2. 매개변수 (Parameter) : 형식 매개변수라고 불림, 함수 선언 쪽에서 전달받는 변수 값 또는 변수 주솟값

#include <stdio.h>

int fn(int x, int y){ => 매개변수(Parameter)
   ...
}

void main( ){
   int i,j;
   ...
   fn(i,j); => 전달인자(Argument)
}

 

  • 매개변수 전달 방법 종류

1. Call by Value : 변수 값 넘겨주고, 이 값은 새로운 공간에 할당되어 사용하는 방식
=> 형식 매개변수의 변화가 실 매개변수에 아무 영향을 미치지 않음

 

#include <stdio.h>

int fn(int x, int y){
   ...
}

void main( ){
   int i,j;
   ...
   fn(i,j);
}

- 형식 매개변수는 변수 선언과 동일하게 작성 (ex. int x, int y)
- 실 매개변수는 변수명을 작성 (ex. fn( x, y) )



2. Call by Reference : 변수 값이 아닌 변수가 실제 사용 중인 메모리 공간 주소 넘기는 방식
=> 실 매개변수의 주소를 형식 매개변수로 보내는 것

#include <stdio.h>

int fn(int* x, int*y){
...
}

void main( ){
   int i,j;
   ...
   fn(&i, &j);
}

- 형식 매개변수는 간접값 연산자(*) 이용 포인터 변수 선언과 동일하게 작성 (ex. int* x, int* y)
- 실 매개변수는 주소연산자(&) 이용 변수 주솟값 작성 (ex. fn(&i, &j) )





# Call-by-Value
#include <stdio.h>

int fn(int n){
   n=7;
   return n;
}

int main( ){
   int n=5;
   fn(n);
   printf('%d',n);
   return 0;
}

# 5
  • main 함수부터 시작
  • 정수형 변수 n 5 선언 (main 함수에서 선언되었으므로 main 지역 변수, 사용자 정의 함수의 n과 다른 변수임)
  • 사용자정의 함수 fn 호출 (전달인자 n=5)
  • 사용자정의 함수 fn 매개변수로 n에 5 전달받음 (파라미터 n=5, 사용자 정의 함수 내에서만 사용하는 n으로 main n과 다른 것) > n에 7 대입 > 7 반환
  • main 함수로 돌아왔으나 반환값 어디에도 저장되지 않음
  • main 함수의 n은 그대로 5이므로  5 출력



# Call-by-Reference
#include <stdio.h>

int fn(int* m){
   *m= 7;
}
int main( ){
   int n=5;
   int o[3]={1,2,3};
   int i;
   
   fn(&n);
   fn(&o[1]);
   
   printf('%d\n',n);
   for(i=0; i<3; i++)
     printf('%d', o[i]);
     
   return 0;
}

# 7
# 173
  • main 함수부터 시작
  • 정수형 변수 n 5 선언 (main함수의 지역변수 5로 main 함수에서만 사용 가능)
  • 정수형 배열 o 선언, o[0]=1, o[1]=2, o[2]=3
  • 정수형 변수 i 선언
  • 사용자정의 함수 fn 호출 (전달인자 n의 주소값)
  • 사용자 정의 함수 매개변수로 포인터 변수 m에 n의 주소값 전달받음 > 포인터 변수 m에 7 대입 > n 주소값에 7 대입
  • 사용자 정의 함수 fn 호출 (전달인자 배열 o의 1번째 값의 주소)
  • 사용자 정의 함수 매개변수로 포인터 변수 m에 o의 1번째 주소값 전달 받음 > 포인터 변수 m에 7 대입 > 배열 o의 1번째 주소값에 7 대입
  • main 함수로 돌아와서 정수형 변수 n 7 출력 (n의 주소값에 7을 넣었으므로 7이 들어가있음)
  • 반복문 for의 초기식에 따라 i 0으로 초기화, 0<3 조건 만족하므로 반복문 실행, 배열 o[0] 1 출력
  • 반복문 실행으로 증감식에 따라 i 1 증가(i=1), 1<3 조건 만족 반복문 실행, 배열 o[1] 7 출력 (배열 o의 1번째 주소값에 7을 넣었으므로 7이 들어가있음)
  • 반복문 실행으로 증감식에 따라 i 1 증가(i=2), 2<3 조건 만족 반복문 실행, 배열 o[2] 3 출력
  • 반복문 실행으로 증감식에 따라 i 1 증가 (i=3), 조건 불만족으로 반복문 탈출 > main 함수 종료

 

[4] 재귀 함수(Recursive Function)

- 함수 자신이 자신을 부르는 함수

  • 재귀 함수 선언
자료형 함수명(자료형 변수명, ...){
   ...
   함수명(변수명, ...)
   ...
   return 반환값;
}
- 재귀함수

함수명이 fn이면 fn 함수에서 fn 함수를 호출하는 방식

 

 

#include <stdio.h>

int fn(int n){
   if (n<=1)
     return 1;
   else;
     return n*fn(n-1);
}
int main( ){
   printf('%d', fn(3));
   return 0;
}

# 6
  • main 부터 시작
  • 사용자 정의 함수 fn 호출 (전달인자 n=3)
  • 사용자 정의 함수 fn 매개변수로 n=3 전달 받음 > 3은 1보다 크므로 else 문 실행 fn(3)= 3*fn(2)
  • fn(2)에 따라 자기 자신 호출 (전달인자 n=2) > fn이 매개변수로 n=2 전달 받음 > 2도 1보다 크므로 else 문 실행 fn(2)= 2*fn(1)
  • fn(1)에 따라 자기 자신 호출 (전달인자 n=1) > fn이 매개변수로 n=1 전달 받음 > if 조건문 성립 > 1 반환, fn(1)=1
  • 다시 거꾸로 되짚어 올라가면서 실행 > 2*fn(1)=2*1=2=fn(2)
  • 3*fn(2)= 3*2= f(3) = 6
  • main 함수로 돌아와서 fn(3) 값인 6 출력

 

- 재귀 함수

main 함수에서 사용자 정의 함수에 인자를 전달했으나 사용자 정의 함수에서 당장 해당 값을 계산할 수 없기 때문에
자기 자신을 계속 호출하면서 끝 부분을 찾고 main 함수에 결괏값을 되돌려주는 방식

 

(3) 표준 함수

 

[1] 문자열 함수

- string.h 헤더 파일

문자열 함수 사용하기 위해서는 string.h 헤더 파일 include 필요

 

#1 strcat (String Concatenate)
문자열끼리 연결하는 함수

  • strcat(dest, src) : src의 문자열을 dest 문자열 뒤에 붙임
  • strncat(dest, src, maxlen) : src 문자열에서 maxlen **개수만큼 글자수를** dest 문자열 뒤에 붙임

 

## strcat & strncat ##
# include <stdio.h>
# include <string.h>

int main(){
   char a[20]='Hello';
   char b[10]='Hoxypot';
   char c[20]='Hello';
   
   strcat(a,b);
   printf('%s %s\n',a,b);
   
   strncat(c, b, 3);
   printf('%s %s', c, b);
   
   return 0;
}

# HelloHoxypot Hoxypot
# HelloHox Hoxypot
  • main 함수부터 시작
  • 문자형 배열 a 'Hello', b 'Hoxypot', c'Hello' 선언
  • strcat에 따라 a에 b문자열 붙여서 저장 > a=HelloHoxypot, b=Hoxypot
  • a HelloHoxypot , b Hoxypot 출력
  • strncat에 따라 c에 b문자열 붙여서 저장하되 maxlen 3에 따라 3글자만 붙이기 > c=HelloHox, b=Hoxypot
  • c HelloHox, b Hoxypot 출력

 

#2 strcpy (String Copy)
문자열 복사하는 함수

  • strcpy(dest, src) : src 문자열을 dest 문자열에 복사
  • strncpy(dest, src, maxlen) : src 문자열에서 **maxlen 개수만큼 글자수를** dest 문자열에 복사
- strcpy

말 그대로 '복사'이므로 복사한 부분만 변형되는 것이지 전체가 변형되는 것 X
본 문자열이 복사된 문자열보다 길다면 본 문자열에서 덮어씌워지지 못한 부분은 원본 값이 그대로 남아있는 것

 

## strcpy & strncpy ##
#include <stdio.h>
#include <string.h>

int main(){
   char a[20]='Hello';
   char b[10]='Hoxypot';
   char c[20]='Hello';
   
   strcpy(a,b);
   printf('%s %s\n',a,b);
   
   strncpy(c,b,3);
   printf('%s %s\n', c,b);
   
   return 0;
}

# Hoxypot Hoxypot
# Hoxlo Hoxypot
  • 문자형 배열 a, b, c 선언
  • strcpy(a,b)에 따라 a에 b 문자열 복사 > a 글자수 5, b 글자수 7이므로 a 값 전부 뒤집어 씌워짐
  • a Hoxypot, b Hoxypot 출력
  • strncpy(c,b,3)에 따라 c에 b문자열 복사하되 maxlen인 3글자만 앞에서부터 복사 > a는 5글자이므로 앞에 3글자 덮어씌워지고 ** 뒤 2글자는 생존 > (Hello : Hoxlo) **
  • a Hoxlo, b Hoxypot 출력


#3 strcmp(String Compare)
문자열 비교 함수
문자열 ASCII 코드 비교

- strcmp (문자열 비교 함수)

사전 배열 방식과 유사

1. 문자열 첫 번째 문자끼리 비교 - 다르면 아스키 코드 값으로 대소 비교
2. 같으면 문자열 두 번째 문자끼리 비교
3. 같으면 문자열 세 번째 문자끼리 비교
...
n. 마지막 문까지 비교한 결과, 같으면 두 문자열이 같다고 판별
  • strcmp(s1,s2) : s1, s2 대소 비교
  • strcmp(s1, s2, maxlen) : maxlen 길이만큼만 s1, s2 대소 비교

=> s1이 크면 1, 둘이 같으면 0, s1이 작으면 -1 반환


## strcmp 문자열 비교 ##

#include <stdio.h>
#include <string.h>

int main(){
   char a[10]='HelloA';
   char b[10]='HelloB';
   
   int c=strcmp(a,b);
   printf('%d\n',c);
   
   c=strncmp(a,b,3);
   printf('%d',c);
   
   return 0;
}

# -1
# 0
  • 문자형 배열  a 'HelloA', b 'HelloB' 선언
  • strcmp(a,b)로 a와 b 비교한 결과값 정수형 변수 c에 저장
  • a와 b는 Hello는 같지만 뒤에 붙은 A와 B가 다름 > 아스키코드에서 A=65, B=66이므로 a가 더 작음 > c=-1 출력
  • strncmp(a, b, 3)로 a와 b 비교하면, 맨 앞 부터 maxlen인 3글자만 비교하므로 a와 b 모두 'Hel'로 값이 동일 > c=0 출력

 

#4 strlen (String Length)
문자열 길이 알려주는 함수

  • strlen(s) : s 길이 알려줌
#include <stdio.h>
#include <string.h>

int main( ){
   char a[20]='Hello';
   int c = strlen(a);
   printf('%d',c);
   return 0;
}

# 5
  • 문자형 배열 a 'Hello' 선언
  • 정수형 변수 c에 배열 a 문자열 길이 값 넣기  c=5
  • c 5 출력



#5 strrev (String Reverse)
문자열 거꾸로 뒤집는 함수

  • strrev(str) : str 내 문자열 거꾸로 뒤집음

 

#include <stdio.h>
#include <string.h>

int main(){
   char a[20]='Hello';
   strrev(a);
   printf('%s',a);
   return 0;
}

# olleH
  • 문자형 배열 a Hello 선언
  • strrev 함수로 거꾸로 뒤집기 a=olleH
  • olleH 출력


#6 strchr (String find Character)
문자열 내 일치 문자 있는지 검사하는 함수

  • strchr(str, c) : str 내에 c 문자 존재하는지 알려줌

 

#include <stdio.h>
#include <string.h>

int main( ){
   char a[20]='Hello';
   char* p=strchr(a, 'l');
   printf('%s',p);
   return 0;
}

# llo
  • 문자형 배열 a 'Hello' 선언
  • strchr (a, 'l') 함수로 a 내에서 첫 'l'이 나온 **위치**를 반환해서 p 포인터 변수에 대입 p=a[2]
  • 포인터 변수 p를 출력하므로 a[2] 부터 null 전인 a[4] 까지 출력 'llo'

 

[2] 수학 함수

- math.h 헤더파일

수학함수를 계산하기 위해 math.h 헤더 파일 include 해서 사용


#1 sqrt
양의 제곱근 계산하는 함수

  • sqrt(n) : 루트 n 값 계산

 

#include <stdio.h>
#include <math.h>

int main(){
   double a;
   a = sqrt(5.1);
   
   printf('%.2f',a);
   
   return 0;
}

# 2.26
  • 실수형 변수 double a 선언
  • a에 양의 제곱근 계산하는 sqrt (5.1) 값 대입 > a = 루트 5.1 값 계산
  • a 값에 소수점 이하 2자리만 반올림해서 출력 2.26
- 소수 확인에 sqrt 사용

a 변수가 소수인지 확인할 때 2~(a-1) 까지 모든 정수로 나눴을 때 나눠 떨어지는지 확인 필요
BUT, 2~루트(a) 까지 정수들만 확인해도 되기 때문에 sqrt 함수 사용

ex. 101 소수인지 확인
> 2부터 100까지 정수 나누기 확인 X
> 2부터 루트 101 (=sqrt(101)) 까지 확인 O > 루트 101(=10.05) 이므로 2부터 10까지만 확인하면 OK  

 


#2 ceil
소수점 올림 함수

  • ceil(n) : n 소수점 올림
#include <stdio.h>
#include <math.h>

int main(){
   double a=1.1;
   printf('%.2f', ceil(a));
   return 0;
}

# 2.00
  • 실수형 변수 double a 1.1 선언
  • a에 ceil 함수 적용해서 소수점 올림 > 1.1 에서 올림 하면 2
  • 2인데 소수점 이하 2자리 출력이므로 2.00 출력


#3 floor
소수점 내림 함수

  • floor(n) : n 소수점 내림
#include <stdio.h>
#include <math.h>

int main(){
   double a=1.1;
   printf('%.2f', floor(a));
   
   return 0;
}

# 1.00
  • 정수형 변수 double a 1.1 선언
  • a에 floor 함수 적용해서 소수점 내림 > 1.1 내림 시 1
  • 1의 소수점 이하 2자리 출력이므로 1.00 출력

 

[3] 유틸리티 함수

#1 rand (Random) 함수
임의의 값 생성 함수

  • rand( ) : 임의의 정숫값 1개 생성
rand( ) 는 0~32767 사이 값 중 하나 반환

** 임의 난수 생성이므로 여러 번 실행 시, 동일 숫자 나올 수 있음 **



#2 srand (Seed Random) 함수
난수 생성 알고리즘에 사용하는 seed 정해주는 함수
srand 사용 시, rand 함수에 seed 값 **해당하는 난수 패턴**으로 생성 > seed 값 동일하면 생성되는 난수도 동일함

- seed

난수 알고리즘 실행에 사용하는 수
난수 알고리즘의 seed 값 같으면 프로그램 실행 시마다 계속 같은 패턴의 난수를 만듦

> seed 값 프로그램 실행 시 마다 다르게 해주기 위해 seed에 time 함수 적용
  • srand(seed) : seed 값 따라 난수 발생기 초기화



#3 time 함수

현재 시간 가져오는 함수
1970년 1월 1일 이후로 **몇 초**가 경과했는지 나타냄

현재 시간이 1970년 1월 1일 0시 0분 1초  => 1
1970년 1월 1일 0시 0분 2초 => 2

1초 지날 때마다 1씩 증가
  • time(NULL) : time 함수 파라미터 **NULL**로 하면 현재 시간 반환

 

#include <stdio.h>
#include <stdlib.h> #rand 함수 사용
#include <time.h> #time 함수 사용

int main(){
   int a;
   int i;
   
   srand(time(NULL)); # 난수 발생기 현재 시간 seed 값으로 초기화
   
   for(i=0; i<6; i++){
      a=rand()%45+1;
      printf('%d ', a);
   }
   return 0;
}

# 29 17 18 26 24 26
  • rand 함수 사용 목적 stdlib.h 헤더 파일 include
  • time 함수 사용 목적 time.h 헤더 파일 include
  • main 부터 시작
  • 정수형 변수 a, i 선언
  • srand 함수의 seed 값 time(NULL)을 통해 현재 시간으로 설정, rand 함수가 실행 시 마다 다른 패턴 난수 생성하도록 함
  • srand 구문 삭제할 경우, 프로그램 실행 시마다 동일 결과 출력
  • for 반복문의 초기식으로 i 0 초기화, 0<6 조건 만족으로 반복문 실행 > 변수 a에 rand() 통해 생성된 난수 45로 나눈 나머지(0~44)에 1 더한 값(1~45) 대입
  • a 출력
  • 반복문 종료로 증감식에 따라 i 1 증가(i=1) > 1<6 조건 만족으로 반복문 수행 > i가 6 되기 직전까지 반복 (0~5) 총 6번 반복



#4 atoi (ASCII to Integer) 함수
문자열을 정수형으로 변환

  • atoi (str) : 문자열 str 값을 정수형(int)으로 변환

 

#include <stdio.h>
#include <stdlib.h> # atoi 함수 사용 

int main(){
   char* a = '1';
   int num = atoi(a);
   
   printf('%d', num);
   
   return 0;
}

# 1
  • atoi 함수 사용 목적 stdlib.h 헤더 파일 include
  • 자료형 포인터 변수 a 선언해서 문자열 '1' 값 대입 > 포인터 변수 a가 '1' 문자열 가리킴
  • atoi에 포인터 변수 a 적용으로 a가 가리키는 '1' 값이 atoi 함수에 적용 > '1' 을 정수형 숫자 1로 변환하여 정수형 변수 num에 대입 > num=1
  • 1 출력
- 포인터

문자열 저장에 일반적으로 배열 사용
BUT, 문자형 포인터 생성하여 대입도 가능

변수 선언 시, '*' 가 붙어있는 것이 포인터 변수

ex. char* a
> 문자형 변수 X, 문자형 포인터 변수 O



#5 atof (ASCII to Floating Point) 함수
문자열을 실수형으로 변환하는 함수

  • atof (str) : str 문자열을 실수형(float, double)으로 변환
#include <stdio.h>
#include <stdlib.h> # atof 함수 사용

int main(){
   char* str_num='1.0';
   double num = atof(str_num);
   printf('%.2f', num);
   
   return 0;
}

# 1.00
  • atof 함수 사용 목적 stdlib.h 헤더 파일 include
  • 문자형 포인터 변수 str_num 생성하여 문자열 '1.0' 대입 > 포인터 변수 str_num이 문자열 '1.0'을 가리킴
  • 정수형 변수 double num 생성하여 atof 함수에 문자형 포인터 변수 str_num이 가리키는 '1.0' 값 대입 > 문자열 '1.0'을 실수형 '1.0'으로 변환하여 num에 저장
  • num=1.0 인데 소수점 이하 2자리 출력이므로 1.00 출력



#6 itoa (Integer to ASCII) 함수
정수형을 문자열로 변환하는 함수

  • itoa (value, str, radix) : value를 변환하여 str에 radix 진수로 저장
#include <stdio.h>
#include <stdlib.h> # itoa 함수 사용

int main(){
   char buffer[4]={NULL};
   int num=123;
   
   itoa(num, buffer, 10); #num 값을 buffer에 10진수 형태로 변환하여 문자열 값으로 저장
   printf('%s', buffer);
   
   return 0;
}

# 123
  • itoa 함수 사용 목적 stdlib.h 헤더 파일 include
  • 문자형 배열 buffer 선언, 첫 번째 값 NULL로 초기화 & 배열 요소 4개이므로 2,3,4 번째도 NULL로 초기화
  • 정수형 변수 num 123 선언
  • itoa 함수를 통해 정수형 num 변수를 10진수 형태로 문자열로 변환하여 문자형 배열 buffer에 저장 > 숫자 123이 문자열 '123'으로 변형되어 buffer에 저장
  • buffer '123' 출력