10 분 소요

값에 의한 호출 vs 참조에 의한 호출

  • 값에의한 호출: 복사본만 건드릴 수 있음.(원본은 못 건드림)
  • 참조에 의한 호출: 원본의 주소를 넘겨받기 때문에 원본을 건드림.
#pragma warning(disable : 4996)
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>

void increase(int origin, int increment)
{
	origin += increment;
}
void incbyaddress(int* origin, int increment)
{
	//*origin은 origin이 가리키는 변수 자체
	*origin += increment;
}
int main(void)
{
	int amount = 10;
	increase(amount, 20); //원본 amount가 20 증가하지 않음
	printf("%d\n", amount);

	amount = 10;
	incbyaddress(&amount, 20); //&amount: amount의 주소로 호출
	printf("%d\n", amount);

	return 0; //1을 반환하면 정상적인 종료가 아니라고 판단함.
}

배열원소 참조 방법

int i,sum = 0;

int point[] = {95, 88, 76, 54, 85, 33, 65, 78, 99, 82};

int *address = point;

int aryLength = sizeof(point) / sizeof(int);

위에서 배열의 총합을 구한다면?

// solution(1)
for(i = 0; i<aryLength; i++)
	sum += *(point+i);

전체코드

#pragma warning(disable : 4996)
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>

int main(void)
{
	int i, sum = 0;

	int point[] = { 1, 2, 3};
	// 95, 88, 76, 54, 85, 33, 65, 78, 99, 82
	int *address = point;
	int aryLength = sizeof(point) / sizeof(int);

	// solution(1)
	for (i = 0; i < aryLength; i++)
		sum += *(point + i);
	
	printf("sum: %d", sum);
	return 0; //1을 반환하면 정상적인 종료가 아니라고 판단함.
}

이때 sum: 6이 정상적으로 출력된다. 나는 처음에 i에 0이 있으니까 (point+0)과 (point+1)의 차이가 절대적인 차이로서 1이 나올줄 알았는데 확인해보니 4만큼 차이가 났다. 즉 내부에서 알아서 배열의 크기만큼 칸수와 칸 사이즈 고려해서 주소잡아주는 것 같다. 물론 이렇다고 해도 아래 내용은 안되니 주의하자!

for(i=0;i<aryLength; i++)
    sum += *(point++);

위에서는 point가 10번지라면 14번지-18번지씩 4만큼 이동하는게 아니라 1씩 이동하게 된다.

만약 char형 배열이라면 가능할수도 있겠으나 이건 4바이트이니 안된다.

물론 또! 배열의 주소를 전달받은 배열포인터라면 이야기가 다르다!

“요즘 느끼는 건데 C언어 문법이 살짝 억까가 많은 것 같다.”

// int *address = point;
// ...
for(i=0; i<aryLength; i++)
	sum += *(address++);

point가 1씩 증가해도 얘가 가리키는 배열에서는 4바이트씩 이동하게 된다. 그러니 값을 순차적으로 뽑아올 수 있다.

그러므로 다음과 같이 구현할 수가 있다.

#pragma warning(disable : 4996)
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>

int sumary(int* ary, int SIZE)
{
	int sum = 0;
	for (int i = 0; i < SIZE; i++)
	{

		sum += *ary++;  //ary 자체가 포인터로서 역할을 수행
	}
	return sum;
}

int main(void)
{
	int i, sum = 0;

	int point[] = { 1, 2, 3};

	int* address = point;
	int aryLength = sizeof(point) / sizeof(int);

	printf("함수 sumary() 호출로 구한 합은 %d\n", sumary(point, aryLength));
	return 0; //1을 반환하면 정상적인 종료가 아니라고 판단함.
}

위에서는 주소를 넘겨주었는데, 포인터로 바꾸고 넘겨주는 것도 가능할 것 같다.

#pragma warning(disable : 4996)
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>

int sumary(int* aryPointer, int SIZE)
{
	int sum = 0;
	for (int i = 0; i < SIZE; i++)
	{

		sum += *aryPointer++;  //ary 자체가 포인터로서 역할을 수행
	}
	return sum;
}

int main(void)
{
	int i, sum = 0;

	int point[] = { 1, 2, 3};

	int* address = point;
	int aryLength = sizeof(point) / sizeof(int);

	printf("함수 sumary() 호출로 구한 합은 %d\n", sumary(address, aryLength));
	return 0; //1을 반환하면 정상적인 종료가 아니라고 판단함.
}

만약 이중포인터를 써서 구현한다면 어떻게 될까?

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>

int sumary(int **aryPointer, int SIZE)
{
    int sum = 0;
    for (int i = 0; i < SIZE; i++)
    {
        sum += *(*aryPointer + i);  // 이중 포인터를 역참조하여 배열 요소에 접근
    }
    return sum;
}

int main(void)
{
    int sum = 0;
    int point[] = {1, 2, 3};
    int *address = point;
    int *pointers[] = {address};  // 첫 번째 요소가 'point' 배열을 가리키는 포인터 배열 생성
    int aryLength = sizeof(point) / sizeof(int);

    printf("함수 sumary() 호출로 구한 합은 %d\n", sumary(pointers, aryLength));
    return 0;
}

필기시험 유형

  • 출력결과

  • 코드빈칸채우기

  1. 필기(50%) + 실기(50%)
  2. 노트북 못씀. 책하고 강의자료 볼 수 있음. 태블릿 못씀. 실습실 pc써야함.
  3. 필기는 오픈북 아님. 실기만 오픈북임.
  4. 실기 5문제,

함수 매개변수에서 const의 사용방법과 의미

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>

void multiply(double* result, const double* a, const double* b)
{
    *result = *a * *b;
    // *a = *a + 1;  // const로 선언된 a를 바꾸려고 하면 오류가 발생
    // *b = *b + 1;  // const로 선언된 b를 바꾸려고 하면 오류가 발생
}

void devideandincrement(double* result, double* a, double* b)
{
    *result = *a / *b;
    ++* a;
    (*b)++;
}

int main(void)
{
    double m = 0, n = 0, mult = 0, dev = 0;

    printf("두 실수 입력: ");
    scanf("%lf %lf", &m, &n);

    multiply(&mult, &m, &n);
    devideandincrement(&dev, &m, &n);

    printf("두 실수 곱: %.2f, 나눔: %.2f\n", mult, dev);
    printf("연산 후 두 실수: %.2f, %.2f\n", m, n);

    return 0;
}

복소수를 위한 구조체

struct complex{
	double real;
	doulbe img;
};
typedef struct complex complex;

예제(1)

#include <stdio.h>

struct complex {
    double real;
    double img;
};

typedef struct complex complex;

complex addComplex(complex a, complex b);

int main(void) {
    complex num1 = { 2.5, 3.0 };
    complex num2 = { 1.5, 2.5 };

    printf("첫 번째 복소수: %5.1f + %5.1fi\n", num1.real, num1.img);
    printf("두 번째 복소수: %5.1f + %5.1fi\n", num2.real, num2.img);

    complex sum = addComplex(num1, num2);

    printf("두 복소수의 합: %5.1f + %5.1fi\n", sum.real, sum.img);

    return 0;
}

complex addComplex(complex a, complex b) {
    complex result;
    result.real = a.real + b.real;
    result.img = a.img + b.img;
    return result;
}

예제(2)

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>


typedef struct book
{
    char title[50];
    char author[50];
    int ISBN;
} book;

void print(book* b)
{
    printf("제목: %s, ", b->title);
    printf("저자: %s, ", b->author);
    printf("ISBN: %d\n", b->ISBN);
}


int main(void)
{
    book python = { "파이썬으로 배우는 누구나 코딩", "강환수", 979117 };
    book comintro;
    strcpy(comintro.title, "소프트웨어 중심사회의 컴퓨터개론");
    strcpy(comintro.author, "강환수");
    comintro.ISBN = 437894;
    
    print(&comintro);
    print(&python);

    return 0;
}

여기서 “void print(book* b)”를 보면 b가 포인터 이기 때문에 “b->”형식을 취했다. 만약 인자가 구조체의 포인터가 아닌 그냥 구조체라면 b.title, b.author, b.ISBN의 꼴로 사용했을 것이다.

그리고 구조체에 값을 입력할때는 strcpy()를 써줘야 한다.

함수 포인터

함수의 주소값을 저장하고 필요에 따라 (함수)포인터가 함수를 참조하여 불러올 수 있다.

예제(1): 퀵정렬로 오름차순 정렬, 내림차순 정렬하기

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>

int compareAscending(const void* a, const void* b)
{
    return (*(int*)a - *(int*)b);
}

int compareDescending(const void* a, const void* b)
{
    return (*(int*)b - *(int*)a);
}
void sortArray(int* arr, int size, int (*compare)(const void*, const void*))
{
    qsort(arr, size, sizeof(int), compare);
}

int main(void)
{
    int numbers[] = { 5, 3, 2, 4, 1 };
    int size = sizeof(numbers) / sizeof(numbers[0]);

    sortArray(numbers, size, compareAscending);

    for (int i = 0; i < size; i++)
    {
        printf("%d ", numbers[i]);
    }
    printf("\n");

    sortArray(numbers, size, compareDescending);

    for (int i = 0; i < size; i++)
    {
        printf("%d ", numbers[i]);
    }
    printf("\n");

    return 0;
}

위 코드에서는 오버플로우 발생 가능성 존재. 아래와 같이 수정해줄 필요가 있다.

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>

int compare_ints(const void* a, const void* b)
{
    int arg1 = *(const int*)a;
    int arg2 = *(const int*)b;

    if (arg1 < arg2) return -1;
    if (arg1 > arg2) return 1;
    return 0;
}

int main(void)
{
    int ints[] = { -2, 99, 0, -743, 2, INT_MIN, 4 };
    int size = sizeof ints / sizeof * ints;

    qsort(ints, size, sizeof(int), compare_ints);

    for (int i = 0; i < size; i++)
    {
        printf("%d ", ints[i]);
    }
    printf("\n");

    return 0;
}

함수 주소를 저장하는 함수 포인터의 선언과 사용

함수 포인터 배열

여러 함수 주소를 저장하는 함수 포인터 배열의 선언과 사용(1)

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

void add(double*, double, double);
void subtract(double*, double, double);
void multiply(double*, double, double);
void devide(double*, double, double);

int main(void)
{
    char op[4] = { '+', '-', '*', '/' };
    //함수 포인터 선언하면서 초기화 과정
    void (*fpary[4])(double*, double, double)
        = { add, subtract, multiply, devide };

    double m, n, result;
    printf("사칙연산을 수행할 실수 2개를 입력하세요. => ");
    scanf("%lf %lf", &m, &n);
    //사칙연산을 배열의 첨자를 이용하여 수행
    for (int i = 0; i < 4; i++)
    {
        fpary[i](&result, m, n);
        printf("%.2lf %c %.2lf == %.2lf\n", m, op[i], n, result);
    }

    return 0;
}
// x + y 연산 결과를 z가 가리키는 변수에 저장하는 함수
void add(double* z, double x, double y)
{
    *z = x + y;
}
// x + y 연산 결과를 z가 가리키는 변수에 저장하는 함수
void subtract(double* z, double x, double y)
{
    *z = x - y;
}
// x + y 연산 결과를 z가 가리키는 변수에 저장하는 함수
void add(double* z, double x, double y)
{
    *z = x * y;
}
// x + y 연산 결과를 z가 가리키는 변수에 저장하는 함수
void add(double* z, double x, double y)
{
    *z = x / y;
}

함수 포인터 어떻게 써?

함수 시작 주소를 저장해서 쓴다.

언제써? 함수에서 인자를 함수로 받을 수 있다. 그래서 함수의 인자를 이용해서 전달받을때 이 함수 포인터 써줘야 한다

“내가 함수를 실행할때 어떤 함수를 실행해야 하는지,

실행시간이 결정될때 이 함수포인터가 속도가 빨라서 많이 사용된다.”

함수포인터는

함수의 인자의 개수가 같고, 반환형이 같고, 입력 데이터 타입이 같을때만 사용할 수 있다.

데이터 입력 m,n을 받으면 0일때는 뭐가 수행된다.

1일때는 뭐가 수행된다. -1일때는 뭐가 수행된다. 등등

(실기는 말고 필기에서 해당 함수 포인터에 대한 문제 출제할 예정)

여러 함수 주소를 저장하는 함수 포인터 배열의 선언과 사용(2)

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

// 함수 선언
int add(int a, int b);
int subt(int a, int b);
int mul(int a, int b);
int divide(int a, int b);  // 함수 이름 수정

int main(void)
{
    // 함수 포인터 배열 초기화
    int (*pfunary[4])(int, int) = { add, subt, mul, divide };

    // 피연산자 및 연산자 설정
    int m = 9, n = 3;
    char* ops = "+-*/";
    char op;

    // 반복문을 통한 각 연산 수행
    while (op = *ops++)
    {
        switch (op)
        {
        case '+':
            printf("%c 결과: %d\n", op, pfunary[0](m, n));
            break;
        case '-':
            printf("%c 결과: %d\n", op, pfunary[1](m, n));
            break;
        case '*':
            printf("%c 결과: %d\n", op, pfunary[2](m, n));
            break;
        case '/':
            printf("%c 결과: %d\n", op, pfunary[3](m, n));
            break;
        }
    }

    return 0;
}
// 덧셈 함수
int add(int a, int b)
{
    return a + b;
}
// 뺄셈 함수
int subt(int a, int b)
{
    return a - b;
}

// 곱셈 함수
int mul(int a, int b)
{
    return a * b;
}

// 나눗셈 함수 (0으로 나누지 않도록 수정)
int divide(int a, int b)
{
    if (b != 0)
        return a / b;
    else
    {
        printf("Error: 나누는 수는 0이 될 수 없습니다.\n");
        return 0;  // 예외 처리를 위해 0 반환
    }
}

실습문제

문제(1): 열의 평균 구하기

#pragma warning(disable : 4996)
#include <stdio.h>
#include <stdlib.h>

//열의 평균을 계산하는 함수
float calculateColumnAverage(int** array, int numRows, int columnIndex) {
    float sum = 0.0;

    for (int i = 0; i < numRows; i++) {
        sum += array[i][columnIndex];
    }
    return sum / numRows;
}

int main(void)
{
    // 파일 열기
    FILE* file = fopen("f1.txt", "r");

    if (file == NULL) {
        printf("파일을 열 수 없습니다.\n");
        return 1; //프로그램 종료
    }

    int numRows, numCols;

    // 파일에서 행과 열 정보 읽기
    fscanf(file, "%d %d", &numRows, &numCols);

    // 2차원 동적 배열 선언
    int** dynamicArray = (int**)malloc(numRows * sizeof(int*));

    if (dynamicArray == NULL) {
        printf("행에 대한 메모리 할당에 실패했습니다.\n");
        fclose(file);
        return 1; //프로그램 종료
    }

    for (int i = 0; i < numRows; i++) {
        dynamicArray[i] = (int*)malloc(numCols * sizeof(int));

        if (dynamicArray[i] == NULL) {
            printf("열에 대한 메모리 할당에 실패했습니다.\n");
            
            //이전에 할당한 메모리를 해제하고 파일 닫기
            for (int j = 0; j < i; j++)
            {
                free(dynamicArray[j]);
            }
            free(dynamicArray);
            fclose(file);
            return 1; //프로그램 종료
        }
    }

    //파일에서 데이터를 읽어와 배열에 저장
    for (int i = 0; i < numRows; i++) {
        for (int j = 0; j < numCols; j++) {
            fscanf(file, "%d", &dynamicArray[i][j]);
        }
    }

    //파일 닫기
    fclose(file);

    //각 열의 평균 계산 및 출력
    printf("평균 결과: \n");
    for (int j = 0; j < numCols; j++) {
        float avg = calculateColumnAverage(dynamicArray, numRows, j);
        printf("%d: %.2f\n", j, avg);
    }

    //동적으로 할당한 메모리 해제
    for (int i = 0; i < numRows; i++) {
        free(dynamicArray[i]);
    }
    free(dynamicArray);

    return 0;
}

문제(2): FILE 포인터 배열 이중으로 선언하기

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int main() {
    FILE* files[2];
    files[0] = fopen("f21.txt", "r");
    files[1] = fopen("f22.txt", "r");

    // 파일 열기 작업 성공 여부 확인
    if (files[0] == NULL || files[1] == NULL) {
        printf("파일 열기 실패\n");
        return 1; // 실패 시 프로그램 종료
    }

    // 각 파일의 데이터 출력
    char buffer[100];

    // f21.txt 데이터 출력
    printf("f21.txt>\n");
    while (fgets(buffer, sizeof(buffer), files[0]) != NULL) {
        printf("%s", buffer);
    }

    // f22.txt 데이터 출력
    printf("f22.txt>\n");
    while (fgets(buffer, sizeof(buffer), files[1]) != NULL) {
        printf("%s", buffer);
    }

    // 파일 닫기
    fclose(files[0]);
    fclose(files[1]);

    return 0;
}

문제(3): 포인터 배열을 하나씩 출력해보기

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

void printFruits(const char* fruits[], int size) {
    for (int i = 0; i < size; i++) {
        printf("%s\n", fruits[i]);
    }
}
int main() {
    const char* fruits[] = { "Apple", "Banana", "Date", "Elderberry" };

    int size = sizeof(fruits) / sizeof(fruits[0]);

    printFruits(fruits, size);

    return 0;
}

문제(4): 각 원소의 제곱 출력해보기

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>

// 파일에서 정수 데이터를 읽어 동적 배열에 저장하는 함수
int* readFromFile(const char* filename, int* size) {
    FILE* file = fopen(filename, "r");
    if (file == NULL) {
        printf("파일을 열 수 없습니다.\n");
        exit(EXIT_FAILURE);
    }

    // 배열 크기를 파일에서 읽음
    fscanf(file, "%d", size);

    // 동적 배열 할당
    int* array = (int*)malloc(*size * sizeof(int));
    if (array == NULL) {
        printf("메모리 할당 오류\n");
        exit(EXIT_FAILURE);
    }

    // 배열에 데이터 입력
    for (int i = 0; i < *size; i++) {
        fscanf(file, "%d", &array[i]);
    }

    fclose(file);
    return array;
}

// 배열의 각 원소를 제곱하여 새로운 배열을 반환하는 함수
int* squareArray(const int* array, int size) {
    int* squaredArray = (int*)malloc(size * sizeof(int));
    if (squaredArray == NULL) {
        printf("메모리 할당 오류\n");
        exit(EXIT_FAILURE);
    }

    for (int i = 0; i < size; i++) {
        squaredArray[i] = array[i] * array[i];
    }

    return squaredArray;
}

// 배열을 출력하는 함수
void printArray(const int* array, int size) {
    for (int i = 0; i < size; i++) {
        printf("%d ", array[i]);
    }
    printf("\n");
}

int main() {
    int size;
    int* ary1 = readFromFile("f4.txt", &size);

    printf("원본: ");
    printArray(ary1, size);

    int* ary2 = squareArray(ary1, size);

    printf("\n제곱: ");
    printArray(ary2, size);

    // 메모리 해제
    free(ary1);
    free(ary2);

    return 0;
}

문제(5): 배열의 합과 평균 구하기

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>

// 동적 배열을 사용하여 데이터를 저장하는 구조체
typedef struct
{
    double *data;    // 실제 데이터를 저장할 배열
    size_t size;     // 현재 배열의 크기
    size_t capacity; // 할당된 메모리 공간의 크기
} DynamicArray;

// 배열의 합을 계산하는 함수
double sumArray(const DynamicArray *array)
{
    double sum = 0.0;
    for (size_t i = 0; i < array->size; i++)
    {
        sum += array->data[i];
    }
    return sum;
}

// 배열의 평균을 계산하는 함수
double averageArray(const DynamicArray *array)
{
    if (array->size == 0)
    {
        return 0.0; // 배열이 비어있으면 0을 반환
    }
    return sumArray(array) / array->size;
}

// 동적배열을 초기화하는 함수
void initializedArray(DynamicArray *array, size_t initialCapacity)
{
    array->data = (double *)malloc(initialCapacity * sizeof(double));
    if (array->data == NULL)
    {
        fprintf(stderr, "메모리 할당 오류\n");
        exit(EXIT_FAILURE);
    }
    array->size = 0;
    array->capacity = initialCapacity;
}

// 동적 배열의 메모리를 2배로 확장하는 함수
void expandArray(DynamicArray *array)
{
    array->capacity *= 2;
    array->data = (double *)realloc(array->data, array->capacity * sizeof(double));
    if (array->data == NULL)
    {
        fprintf(stderr, "메모리 재할당 오류\n");
        exit(EXIT_FAILURE);
    }
}

// 동적 배열에 데이터를 추가하는 함수
void pushToDynamicArray(DynamicArray *array, double value)
{
    // 배열이 가득 찼을 경우 확장
    if (array->size == array->capacity)
    {
        expandArray(array);
    }

    // 데이터 추가
    array->data[array->size++] = value;
}

// 동적 배열의 메모리를 해제하는 함수
void freeDynamicArray(DynamicArray *array)
{
    free(array->data);
    array->size = 0;
    array->capacity = 0;
}

int main()
{
    DynamicArray dataArray;
    initializedArray(&dataArray, 5);

    // 파일에서 데이터 읽어오기
    FILE *inputFile = fopen("f5.txt", "r");
    if (inputFile == NULL)
    {
        fprintf(stderr, "파일을 열 수 없습니다.\n");
        return EXIT_FAILURE;
    }
    double value;
    while (fscanf(inputFile, "%lf", &value) == 1)
    {
        // 파일에서 데이터를 읽어 동적 배열에 추가
        pushToDynamicArray(&dataArray, value);
    }

    // 파일 닫기
    fclose(inputFile);

    // 합과 평균 출력
    printf("합= %.2lf 평균= %.2lf\n", sumArray(&dataArray), averageArray(&dataArray));

    // 동적 배열의 메모리 해제
    freeDynamicArray(&dataArray);

    return EXIT_SUCCESS;
}

댓글남기기