섬지
공대생 섬인지
섬지
전체 방문자
오늘
어제
  • 분류 전체보기 (17)
    • Logical (1)
    • Computer Science (4)
      • python (1)
      • C (0)
      • DataStructure (3)
    • Machine Learning (0)
    • Deep Learning (10)
      • Review (2)
      • Pytorch (2)
      • Computer Vision (4)
    • Environment (1)
      • Colab (0)
      • Linux (1)
    • Etc (1)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

  • 머신러닝
  • YOLO
  • YOLOv5
  • Detection
  • 딥러닝
  • 컴퓨터비전
  • pytorch
  • MLOps
  • 파이토치
  • 디퓨전
  • 논문리뷰
  • C언어
  • 코딩
  • 물체탐지
  • torchvision
  • 파이썬
  • Python
  • UNET
  • wandb
  • 자료구조

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
섬지

공대생 섬인지

Computer Science/DataStructure

[자료구조] C언어로 쉽게 풀어쓴 자료구조 : 3장 배열, 구조체, 포인터

2022. 4. 12. 03:07

이전에 제가 네이버 블로그에 쓴 글을 가져와 일부 내용을 추가하였습니다. ⇒ https://blog.naver.com/racoo_oon/222434455079


구조체

C언어의 경우 한 배열에는 같은 자료형의 데이터들만 저장할 수 있다. 다양한 타입의 데이터를 묶기 위해서는 구조체가 필요하다.

C언어에서는 struct 키워드를 이용해 표기하고 형식은 다음과 같다.

 

struct student {
	char name[10];
	int age;
	double gpa;
};

 

책에 있는 예시를 그대로 들고 왔다. 구조체에 저장되는 항목은 문자열인 이름, 정수형인 나이, 실수형인 학점이다.

struct 뒤의 student는 구조체들을 서로 구분해주는 구조체 태그라고 한다.

위의 코드는 형식만 지정해 준 것이고 실제로 구조체 변수를 정의하려면 다음과 같이 하면 된다.

 

struct student smj;

 

당연히도 구조체 정의는 메인함수 밖에서, 변수선언은 메인함수 안에서 한다.

구조체 안의 항목들에 값을 저장해보겠다.

 

struct student smj;

strcpy(smj.name, "minjee seo");
smj.age = 23;
smj.gpa = 4.3;
 

학점은 내 희망사항,,

위의 예시처럼 멤버 연산자 ( . ) 를 이용하면 구조체 내의 항목 (멤버) 에 접근이 가능하다.

​

책에서는 주로 typedef를 이용해 구조체를 정의하고 있는데 이렇게 할 경우 선언한 구조체는 새로운 데이터 타입이 된다.

맨 처음 예시와 동일한 구조체를 typedef를 이용해 정의해보면 아래와 같다.

 

typedef struct {
	char name[10];
	int age;
	double gpa;
} student;

 

이 경우 student라는 새로운 데이터 타입이 생성된 것이므로 타입 이름만으로 변수 선언이 가능하다.

또한 중괄호를 사용하여 초기화할 수 있다.

 
student smj;
student smj = { "minjee seo", 23, 4.3 };

 

책에 있는 퀴즈 문제인 2차원 좌표 공간에서 하나의 점을 나타내는 구조체 point를 정의하고 구조체 변수 p1, p2를 선언한 후 각각 (1,2) , (9,8)로 초기화한 다음 두 개의 구조체 변수를 받아서 점 사이의 거리를 계산하는 함수 get_distance(point p1, point p2)를 작성하는 문제를 풀어보겠다. (한번에 쓰려니까 길다..)

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

typedef struct {
	int x;
	int y;
} point;

double get_distance(point p1, point p2) {
	double x_d, y_d;

	x_d = (p1.x - p2.x);
	y_d = (p1.y - p2.y);

	return (sqrt((x_d)*(x_d) + (y_d)*(y_d)));
}

int main() {

	point p1 = { 1,2 };
	point p2 = { 9,8 };

	printf("distance : %f\n", get_distance(p1, p2));
	
}

포인터

포인터는 다른 변수의 주소를 가지고 있는 변수이다.

변수 선언 시 변수가 메모리 공간에 저장되는데 이 때 메모리의 각 바이트에는 주소가 매겨져 있다. 포인터에는 이 주소가 저장된다.

​

이해를 위해 우선 정수형 변수 a를 선언한 뒤 100으로 초기화하고 포인터 변수도 선언해보자.

 

int a = 100;
int* p; //포인터 변수는 이렇게 선언함.

 

포인터 p가 a를 가리키게 하려면 a의 주소를 추출해 p에 대입하면 된다. 추출은 & 연산자로 한다.

 

p = &a;

 

이제 p가 가리키는 곳에 값을 새로 저장해보겠다. 저장은 * 연산자로 한다.

 

*p = 200;

 

a를 출력해보면 a의 값이 200으로 바뀐 것을 확인할 수 있을 것이다.

​

아무것도 가리키고 있지 않은 포인터는 널 포인터라고 한다. 포인터에 주소를 지정하지 않았을 때는 항상 널 포인터 상태로 만들어 두는 것이 좋은데 잘못된 포인터로 메모리를 변경하면 치명적인 결과가 발생할 수 있기 때문이다 (...) 변수를 초기화하지 않을 시 변수에는 쓰레기 값이 저장되기 때문에 당장 사용하지 않는 포인터는 널 포인터로 만들어 두자.

​

함수의 매개변수로 포인터를 사용하면 함수 외부의 변수를 변경할 수 있다.

간단하게 외부 변수의 값을 100으로 바꾸는 함수를 작성해보겠다.

 

void change_val(int *p) {
	*p = 100;
}


int main() {
	int num = 10;
	printf("value : %d\n", num);

	change_val(&num);
	printf("value : %d\n", num);
}

 

함수의 매개변수로 함수 외부 변수의 주소를 추출해 넘겨 주면 함수 안에서 해당 주소의 값을 바꿔 준다. 

또한 포인터를 통해 구조체 멤버에 접근할 때에는 다음과 같이 보다 편리한 표기법을 사용할 수 있다.

 

(*p).member; // 원래 표기법
p->member; // 편리한 표기법

배열과 포인터

배열의 이름은 배열의 시작 위치 (주소) 를 가리키는 포인터이다. 배열의 이름에 공간이 할당되지는 않지만 배열의 이름이 있는 곳이 배열의 첫 번째 요소의 주소로 대치되기 때문에 배열이 함수로 전달될 때 실제로는 포인터가 전달된다고 할 수 있다.

따라서 함수로 배열이 전달되면 함수 내부에서 배열의 내용을 변경할 수 있다.

 

함수 안에서 배열의 내용을 변경하는 간단한 함수를 구현해보자.

 

#include <stdio.h>
#define SIZE 5 // 편의상 배열의 크기를 미리 정의한다.

// 리스트의 요소를 0으로 초기화하는 함수
void list_to_zero(int list[])
{
    for (int i = 0; i < SIZE; i++) {
        list[i] = 0;
    }
}

// 리스트의 요소를 출력하는 함수
void printList(int list[]) {
    for (int i = 0; i < SIZE; i++) {
        printf("%d", list[i]);
    }
    printf("\n");
}

int main(void) {
    
    int mylist[SIZE] = {1, 2, 3, 4, 5};
    
    printList(mylist);
    
    list_to_zero(mylist);
    printList(mylist);
    
}

 

출력도 다음과 같이 잘 되는 것을 확인할 수 있다.

 

12345
00000

동적 메모리 할당

필요한 만큼의 메모리를 운영체제로부터 할당받아 사용하고 사용이 끝나면 반납하는 기능이다.

이 떄 동적 메모리가 할당되는, 운영체제가 사용하지 않는 메모리 공간을 모아 놓은 곳을 히프라고 한다.

포인터로만 사용할 수 있다!

동적 메모리를 할당해 보자.
 
int* p;
p = (int*)malloc(sizeof(int));
*p = 100;
free(p);

 

먼저 malloc()함수를 이용해 변수의 크기 (여기서는 int) 만큼의 메모리 블록을 할당하고 동적 메모리 블럭의 시작 주소를 반환해 포인터에 저장한다.

포인터를 이용해 할당된 메모리에 변수를 저장한 후 free()함수를 이용하면 메모리를 다시 반환할 수 있다.

이때 malloc 함수가 반환했던 포인터 값을 잊어버리면 동적 메모리를 반환할 수 없다!

또한 malloc이 반환한 값이 NULL이 아닌지 항상 검사해야 한다. (메모리를 할당할 수 없으면 NULL을 반환하기 때문)

 


구조체와 포인터

구조체에 대한 포인터를 선언하고 포인터를 통해 구조체 멤버에 접근해 보자. 이 때 동적 메모리 할당을 이용해 구조체를 생성한다.

 

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define SIZE 100

typedef struct myProfile {
    char name[10];
    int age;
    char major[SIZE];
}me;

int main(void) {
    
    me *m; // 구조체 me를 가리키는 포인터
    
    m = (me*)malloc((sizeof(me))); // 구조체 사이즈만큼의 동적 메모리를 할당하고 m에 주소 저장
    
    strcpy(m->name, "Minjee");
    m->age = 24;
    strcpy(m->major, "MBE");
    
    free(m); // 동적 메모리 사용이 끝나면 꼭 free해주자
    
}

'Computer Science > DataStructure' 카테고리의 다른 글

[자료구조] C언어로 쉽게 풀어쓴 자료구조 : 6장 연결 리스트 Ⅰ  (0) 2022.04.13
[자료구조] C언어로 쉽게 풀어쓴 자료구조 : 3장 연습문제  (0) 2022.04.12
    'Computer Science/DataStructure' 카테고리의 다른 글
    • [자료구조] C언어로 쉽게 풀어쓴 자료구조 : 6장 연결 리스트 Ⅰ
    • [자료구조] C언어로 쉽게 풀어쓴 자료구조 : 3장 연습문제
    섬지
    섬지
    Ewha MBE, Yonsei CSE, 인공지능하기싫다

    티스토리툴바