안녕하세요. YKK입니다.
C프로그래밍 워크숍 4, 5일 차에서 다룬 포인터, 구조체와 문자열 처리를 공부해 보겠습니다.
4, 5일 차를 같은 게시물에 작성하게 되었습니다. 이틀간 다룬 내용들이 파이썬에서 다루는 부분과 차이가 많았기 때문에 좀 더 공부하면서 연구해 보는 시간을 보냈습니다.
포인터와 구조체, 문자열 처리가 저한테는 상당히 까다로웠습니다.
위에서도 언급했듯이, 기존에 학습했던 파이썬에서는 사용되지 않거나 다른 방식으로 C언어에서 구현되는 부분이 많았기 때문입니다.

*포인터
포인터란, 데이터가 저장된 메모리의 주소값을 저장하는 변수이며, 포인터 변수라고도 합니다. 메모리의 주소(위치 정보)를 저장하는 전용 변수입니다. 포인터는 프로그래머에게 컴퓨터 메모리에 직접 접근하여 제어할 수 있게 만들어주는 도구라고 보시면 됩니다.
포인터를 통해 프로그램의 변수에 접근하여 읽고 쓰거나 함수를 실행할 수 있습니다. 자료형을 선언할 때, 해당 변수명 앞에 *를 붙이면 앞 자료형을 가리키는 포인터 변수가 됩니다. 이렇게 선언된 포인터 변수에서 주소를 얻고 싶은 변수 앞에 &(앰퍼샌드)를 붙이면 그 변수의 주소값을 저장합니다(데이터 값 아님).
포인터 변수의 선언
int main(void)
{
int *a; //a라는 이름의 int형 포인터
char *b; // b라는 이름의 char형 포인터
double *c; //c라는 이름의 double형 포인터
}
&연산자: 변수의 주소값 반환
*연산자: 포인터가 가리키는 메모리 참조
int main(void)
{
int a = 2005; // 1. 정수형 변수 a를 선언하고, 2005라는 값을 할당
int *pA = &a; // 2. 변수 a의 메모리 주소를 포인터 pA에 할당
// 즉, pA는 a의 메모리 위치를 가리킴
printf("%d", a); // 3. 변수 a의 값을 직접 출력
// 출력: 2005
printf("%d", *pA); // 4. 포인터 pA를 사용하여 변수 a의 값을 간접적으로 출력
// 여기서 *pA는 포인터 pA가 가리키는 메모리 위치의 값을 의미
// 즉, *pA는 a의 값과 동일한 값을 가리킴
// 출력: 2005
return 0; // main 함수의 종료를 나타냄
}
printf(*pA): 그 주소에 저장되어 있는 실제 데이터값을 반환
printf(pA): 주소값 출력
&연산자와 *연산자의 차이를 명확히 이해하셔야 합니다.
아래 예시를 통해 더 명확히 보겠습니다. 이 부분은 직접 코드를 돌려보시면 더 이해하기 쉬울 거 같습니다.
#include <stdio.h>
int main(void)
{
double c = 5; // 1. double 형식의 변수 c를 선언하고 5.0 값을 할당
double *d = &c; // 2. 변수 c의 메모리 주소를 가리키는 double 포인터 d를 선언
int e = 5; // 3. int 형식의 변수 e를 선언하고 5 값을 할당
int *pa = &e; // 4. 변수 e의 메모리 주소를 가리키는 int 포인터 pa를 선언
printf("%d\n", pa); // 5. pa의 값, 즉 e의 주소를 출력(실제 변수 e의 주소가 출력)
printf("%d\n", *pa); // 6. pa가 가리키는 메모리 위치의 값을 출력(즉, e의 값인 5를 출력)
e = 100; // 7. 변수 e의 값을 100으로 변경
*pa = 10; // 8. pa가 가리키는 메모리 위치의 값을 10으로 변경(즉, 변수 e의 값을 10으로 변경)
printf("%d\n", *pa); // 9. pa가 가리키는 메모리 위치의 값을 출력(즉, 변수 e의 현재 값인 10을 출력)
printf("%d\n", e); // 10. 변수 e의 현재 값을 출력(즉, 10을 출력)
return 0;
}
포인터..... 상당히 복잡하고 어려운 파트인 거 같습니다.
아무래도 5일이라는 한정된 기간 동안 기초만 다루는 워크숍이었기 때문에 깊게 들어가기에는 시간이 부족했습니다. 진행하시는 교수님도 이 부분 많은 학생들이 이해하기 어려울 거라는 점 예상하시고 기초만 다루신 느낌이었습니다.
실제로 저도 이 부분을 살면서 어제 처음 접해봤기 때문에 워크숍을 마무리하고 따로 1시간 정도 기본 개념을 이해하고자 시간을 할애했습니다(하지만 아직도 잘 모르겠다는....)
C언어는 현대 프로그래밍 언어 중에서도 오래 살아남은 언어입니다. 높은 성능을 요구하는 프로그래밍 분야에서는 매니지드 언어가 (C언어 같은) 언매니지드 언어에 특화된 메모리 관리 등의 영역을 완전히 대체하지 못하기 때문이죠. 이 부분은 특히 C언어가 주로 사용되는 분야 중에서도 메모리를 직접 제어하는 코드를 짜야할 일이 많은 임베디드 소프트웨어 개발이나 게임/그래픽 엔진 개발 등의 분야에서는 필수적입니다. 이 분야 희망하시는 분들은 제가 작성한 글 이외에도 혼자 공부하는 시간을 많이 할애하셔야 할 거 같습니다.
*포인터 실습 문제
-실습 1. 배열에 저장된 숫자들의 평균을 계산하는 average() 함수 구현, 아래 코드를 이용.
#include <stdio.h>
// 배열의 평균을 계산하고 결과를 포인터를 통해 반환하는 함수
void average(double *arr, int size, double *result) {
double sum = 0.0; // 배열의 합계를 저장할 변수 초기화
// 배열의 각 요소를 합산
for (int i = 0; i < size; i++) {
sum += *(arr + i); // 포인터를 이용하여 배열 요소에 접근
}
// 평균 계산 후 결과를 포인터를 통해 반환
*result = sum / size;
}
int main(void) {
double val[] = {1, 2, 3, 4, 5}; // 예제 배열
double avg; // 평균을 저장할 변수
int num = sizeof(val) / sizeof(double); // 배열의 크기 계산
// average 함수를 호출하여 평균 계산
average(val, num, &avg);
// 계산된 평균 값을 출력
printf("평균: %lf\n", avg);
return 0;
}
-실습 2. 포인터를 이용하여 입력받은 다섯 개 정수의 평균과 분산을 구하는 함수를 구현, 아래 코드를 이용해서 작성.
#include <stdio.h>
??? average(???);
??? variance(???);
int num = 5; // 전역 변수로 선언
int main(void){
int val[num];
double avg, var;
// 5개 정수를 입력 받는 부분
average(val, &avg);
variance(val, &var);
printf("평균:%lf, 분산:%lf", avg, var);
return 0;
}
해답 코드
#include <stdio.h>
// 평균을 계산하는 함수
void average(int *arr, double *result) {
int sum = 0;
for (int i = 0; i < num; i++) {
sum += *(arr + i);
}
*result = (double)sum / num;
}
// 분산을 계산하는 함수
void variance(int *arr, double *result) {
double avg;
double sumOfSquares = 0;
// 평균 계산
average(arr, &avg);
// 각 요소와 평균의 차이의 제곱의 합을 계산
for (int i = 0; i < num; i++) {
sumOfSquares += (*(arr + i) - avg) * (*(arr + i) - avg);
}
// 분산 계산
*result = sumOfSquares / num;
}
int num = 5; // 전역 변수로 선언
int main(void) {
int val[num]; // 전역 변수로 선언된 배열
double avg, var; // 평균과 분산을 저장할 변수
// 5개 정수를 입력 받는 부분
printf("5개의 정수를 입력하세요: ");
for (int i = 0; i < num; i++) {
scanf("%d", &val[i]);
}
// average 함수를 호출하여 평균 계산
average(val, &avg);
// variance 함수를 호출하여 분산 계산
variance(val, &var);
// 계산된 평균과 분산 값을 출력
printf("평균: %lf, 분산: %lf\n", avg, var);
return 0;
}
제가 작성한 코드는 어디까지나 예시입니다. 직접 작성하다 보면 저와는 충분히 다른 형태로 구현하실 수 도 있으니 걱정하지 마시고 코드를 실행하여 결괏값이 옳게 나오는지만 확인해 보세요!
*구조체
구조체는 하나 이상의 변수를 묶어 그룹화하는 사용자 정의 자료형입니다. 여러 자료형을 가진 변수들을 하나로 묶어 자료형으로 사용할 수 있도록 정의하는 것을 말합니다. 객체 지향 프로그래밍 언어인 파이썬과 자바에선 클래스를 정의하여 여러 가지 객체(object)를 생성함으로써 이와 같은 기능을 구현할 수 있습니다.
구조체를 정의함으로써 사용자가 C언어의 기본 타입을 가지고 새롭게 정의할 수 있는 사용자 정의 타입이며 기본 타입만으로는 나타낼 수 없는 복잡한 데이터를 표현할 수 있습니다. 구조체는 똑같은 변수를 여러 번 사용해야 하는 경우에 효과적으로 사용할 수 있습니다. 아래처럼 'struct' 뒤에 구조체의 이름을 정해주고, 중괄호 블록 안에 필요한 변수들을 선언하여 사용합니다.
선언할때
struct 구조체이름
{
자료형 변수명;
자료형 변수명;
...
}
예시: '성적' 구조체 선언
struct Score{
char name[10]; //이름
int kor; //국어 성적
int mat; //수학 성적
int eng; //영어 성적
}S;
구조체를 선언할 때의 struct라는 명령어를 사용해서 선언합니다. 구조체 이름을 선언하여 사용하여도 되고 별칭을 지정해서 사용하여도 됩니다. 구조체의 내부에서는 구조체의 멤버를 선언합니다. 멤버 선언은 일반 변수 선언과 기본적으로 비슷하지만 초기화되지는 않습니다.
또한 구조체를 선언하는 것이 변수를 선언하는 것은 아닙니다. 예를 들어 설명하자면, 구조체를 정의하는 것은 붕어빵을 만드는 틀을 만드는 것이고, 실제로 붕어빵을 만들기 위해서는 구조체 변수를 선언하여야 만들어지는 것입니다.
아래 예시를 보겠습니다.
struct student{ //구조체 정의
int number;
char name[10];
double grade;
}
int main(void){
struct student s1; //구조체 변수 선언
...
}
첫 번째부터 다섯 번째 라인까지의 코드는 student라는 구조체를 정의하고, 그 이후 s1이라는 구조체 변수를 선언한 것입니다.
다음으로 구조체의 초기화와 참조에 대해 알아보겠습니다.
구조체의 초기화
-중괄호를 이용하여 초기값을 나열
struct student{
int number;
char name[10];
double grade;
}
struct student s1 = {24, "Kim", 4.3};//구조체 초기화, 이때 구조체 정의 속 멤버의 순서대로 입력
구조체 멤버 참조
-구조체 멤버를 참조하기 위해선 '.'연산자 사용
s1.number = 26;
strcpy(s1.name, "Kim") or s1.name = "Kim";
s1.grade = 4.3;
예제를 통해 구조체를 정리해 보겠습니다.
//구조체 선언
struct student{
int number;
char name[10];
double grade;
};
int main(void)
{
struct studen s; //구조체 변수 선언
s.number = 20070001;//구조체 멤버 참조
strcpy(s.name = "홍길동");
s.grade = 4.3;
printf("학번: %d\n", s.number);
printf("이름: %s\n", s.name);
printf(“학점: %f\n", s.grade);
return 0;
}
출력결과
학번: 20070001
이름: 홍길동
학점: 4.300000
struct student { //구조체 선언
int number;
char name[10];
double grade;
};
int main(void)
{
struct student s;//구조체 변수 선언
printf("학번을 입력하시오: ");
scanf("%d", &s.number); //구조체 멤버의 주소 전달
printf("이름을 입력하시오: ");
scanf("%s", s.name);
printf("학점을 입력하시오(실수): ");
scanf("%lf", &s.grade);
printf("학번: %d\n", s.number);
printf("이름: %s\n", s.name);
printf("학점: %f\n", s.grade);
return 0;
}
출력결과
학번을 입력하시오: 20070001
이름을 입력하시오: 홍길동
학점을 입력하시오(실수): 4.3
학번: 20070001
이름: 홍길동
학점: 4.300000
구조체를 이용하실 때 한 가지 주의할 점이 있습니다. 같은 구조체 변수끼리 대입은 가능하지만 비교는 불가능합니다.
struct point {
int x;
int y;
};
int main(void)
{
struct point p1 = {10, 20};
struct point p2 = {30, 40};
p2 = p1; // 대입 가능
if( p1 == p2 ) // 비교 -> 컴파일 오류!!
printf("p1와 p2이 같습니다.")
if( (p1.x == p2.x) && (p1.y == p2.y) ) // 올바른 비교
printf("p1와 p2이 같습니다.")
}
마무리
이렇게 해서 5일 동안 진행된 C언어 프로그래밍 워크숍이 마무리되었습니다.
1일 차 작성하면서도 썼지만, 파이썬 이외 프로그래밍 언어를 다뤄본 적이 없었기 때문에 상당히 걱정도 많았는데 비슷한 점도 많아서 재밌게 공부할 수 있었습니다.
아무래도 현재 사용되고 있는 프로그래밍 언어들 중 가장 오래 살아남아 있는 프로그래밍 언어 중 하나이기 때문에 어느 정도 익히고 있으면, 다른 프로그래밍 언어를 이용하더라도 상당히 도움이 많이 될 것 같다는 인상을 받았습니다.
종강하고 오늘까지 앞으로 나아가야 할 방향에 대해 고민하던 중 우연히 블로그를 시작하며 공부기록을 남기기로 마음먹고 처음 제대로 공부해 본 내용이기 때문에 애착이 가네요.
틈틈이 C언어에 관련된 내용이나 새롭게 학습한 지식들도 업로드하겠습니다.
작년 3월부터 코딩을 시작해 파이썬으로 프로그래밍에 발을 들여놓게 되었는데요.
이 글을 보게 되실 개발자를 꿈꾸시는 분들, 절대 포기하지 말고 열심히, 꾸준히 하시면 뭐라도 남으실 거라고 확언할 수 있겠습니다. 아직 눈곱만치 작은 보잘것없는 지식일 수 있지만, 저도 뭐라도 남기는 과정에 서게 되었으니까요ㅎㅎㅎ
2024년도 화이팅입니다.
공부 기록 계속 남길 예정입니다.
항상 댓글로 피드백은 환영입니다.
감사합니다.
'C' 카테고리의 다른 글
C언어 시작하기(기초3)-함수와 배열 (0) | 2024.01.10 |
---|---|
C언어 시작하기(기초2)-다양한 연산자들, scanf 함수, 상수와 기본 자료형, 제어문 (1) | 2024.01.10 |
C언어 시작하기(기초1)-개발 환경 구축, 변수 및 산술 연산자 (1) | 2024.01.08 |