C & C++/C

[C] 4년이 지나서 다시보는 C언어 - 핵심 정리

Razelo 2021. 3. 6. 15:52

윤성우님께서 쓰신 "열혈 자료구조"를 보려고 너무 오랜만에 책을 펼쳤는데, 순간 당황했다. 

 

c언어에서 사용되는 ->  기호가 무슨 뜻이었는지 기억이 가물 가물 하면서 기억나질 않았다. 그래서 당황해서 급하게 서현우씨께서 쓰신 "이것이 c언어다" 책을 펼쳤다. (기억이 안날만도 하다. 2년의 공백이 너무 컸다.)

 

지금 4년 만에 다시 펼쳐보는 것 같다. 기본기를 소홀히 한 대가다. 요즘 들어서 느끼는 게 있는데, 아무리 서비스 위주의 기술들 중에서 핫한 기술들이 뜨고 이것저것 나오더라도 기본기는 언제나 중요하다는 것을 느낀다. 아무리 빠르고, 성능 좋은 라이브러리라 하더라도 c++과 c를 기반으로 작성되어있는 경우가 많았고, 성능이라는 면에서 아직도 이 둘을 따라올 언어가 없다고 생각한다. 그러니 기초적으로 반드시 알고 있어야 한다고 생각한다. (근데 기억이 가물가물하니 대참사)

 

생산성을 주목한다면 얘기가 달라지겠지만, 비즈니스 모델은 시간이 흐르면 흐를수록, 생산성보다는 안정성과 뛰어난 성능에 집착할 수 밖에 없기 때문에 다시 초심으로 돌아오기 마련이라고 생각한다. 뭐 아무튼 그렇고... 다시 back to the past... 다시 공부하자. 기억날때까지 

 

그리고 사실 앞의 모든 이유를 제외하고서라도, 기본기에 충실하자는 말은 모든 분야에 해당되는 말이니, 다시 예전공부 해봐도 손해볼일은 없을 듯 싶다. 

 

 

 

#include<stdio.h>

void input_ary(double* pa, int size);
double find_max(double* pa, int size);

int main(void){
	double ary[5];
	double max;
	int size = sizeof(ary) / sizeof(ary[0]);
	input_ary(ary, size);
	max = find_max(ary, size);
	printf("배열의 최댓값 : %lf", max);

	return 0;
}

void input_ary(double* pa, int size) {
	int i;
	printf("%d개의 실수값 입력 : ", size);
	for (i = 0; i < size; i++) {
		scanf_s("%lf", pa + i);
	}
}
double find_max(double* pa, int size) {
	double max;
	int i;
	
	max = pa[0];
	for (i = 1; i < size; i++) {
		if (pa[i] > max) max = pa[i];
	}
	return max;
}

 

구조체 완전 기본 

#include<stdio.h>

struct student {
	int num;
	double grade;
};

int main(void) {
	struct student s1;
	s1.num = 2;
	s1.grade = 2.7;

	printf("학번 : %d\n", s1.num);
	printf("학점 : %.1lf\n", s1.grade);

	return 0;
}

 

malloc 동적 할당 기본 

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

int main(void) {
	int* pi;
	double* pd;

	pi = (int*)malloc(sizeof(int));
	if (pi == NULL) {
		printf("#으로 메모리가 부족합니다. \n");
		exit(1);
	}
	pd = (double*)malloc(sizeof(double));
	//malloc 함수는 (void*) 형을 반환하므로 용도에 맞는 포인터형으로 형변환하여 사용합니다.

	*pi = 10;
	*pd = 3.4;

	printf("정수형으로 사용 : %d\n", *pi);
	printf("실수형으로 사용 : %lf\n", *pd);

	free(pi);
	free(pd);
	//동적으로 할당한 저장공간은 함수가 반환된 후에도 자동으로 회수되지 않습니다. 
	//따라서 반환되기 전에 free함수로 직접 반환해야 합니다. 
	return 0;
}

 

매개변수로 구조체 넘기기

#include<stdio.h>
struct vision {
	double left;
	double right;
};

struct vision exchange(struct vision);

int main(void) {
	struct vision robot;

	printf("시력 입력 : ");
	scanf_s("%lf%lf", &(robot.left), &(robot.right));
	robot = exchange(robot);

	printf("바뀐 시력 : %.1lf,%.1lf \n", robot.left, robot.right);

	return 0;
}

struct vision exchange(struct vision robot) {
	double temp;
	temp = robot.left;
	robot.left = robot.right;
	robot.right = temp;

	return robot;
}

 

비트필드 구조체

구조체 포인터

구조체 배열

자기 참조 구조체 

공용체 

열거형

typedef 

 

 

구조체 포인터

#include<stdio.h>
struct score {
	int kor;
	int eng;
	int mat;
};
int main(void) {
	struct score yuni = { 90,80,70 };
	struct score* ps = &yuni;

	printf("%d \n", ps->kor);
	printf("%d \n", ps->eng);
	printf("%d \n", ps->mat);

	return 0;
}

 

구조체 포인터 연결 리스트 

#include<stdio.h>
struct list {
	int num;
	struct list* next;
};

int main(void) {
	struct list a = { 10,0 }, b = { 20,0 }, c = { 30,0 };
	struct list* head = &a, * current;

	a.next = &b;
	b.next = &c;

	printf("head->num : %d \n", head->num);
	printf("head->next->num : %d \n", head->next->num);

	printf("list all : ");
	current = head;
	while (current != NULL) {
		printf("%d ", current->num);
		current = current->next;
	}
	printf("\n");

	return 0;
}

 

 

union 공용체

#include<stdio.h>

union student {
	int num;
	double grade;
};

int main(void) {
	union student s1 = { 315 }; 

	printf("학번 : %d\n", s1.num);
	s1.grade = 4.4;
	printf("학점 : %.1lf\n", s1.grade);
	printf("학번 : %d\n", s1.num);

	return 0;
}

결과: 

 

공용체는 모든 멤버가 하나의 저장공간을 같이 사용합니다.

공용체 변수의 크기는 멤버 중에서 크기가 가장 큰 멤버로 결정됩니다. 

num과 grade 멤버가 하나의 공간을 공유합니다. 

공용체 변수의 초기화는 중괄호를 사용하여 첫번째 멤버만 초기화합니다. 

그렇기 때문에 공용체 멤버는 언제든지 다른 멤버에 의해 값이 변할 수 있으므로 항상 각 멤버의 값을 확인해야 하는 단점이 있습니다. 하지만 여러 멤버가 하나의 저장 공간을 공유하므로 메모리를 절약할 수 있고, 특히 같은 공간에 저장된 값을 여러가지 형태로 사용할 수 있는 장점이 있습니다. 

 

 

열겨형

#include<stdio.h>
enum season {SPRING,SUMMER,FALL,WINTER};

int main(void) {
	enum season ss;
	char *pc;

	ss = SPRING;
	switch (ss) {
	case SPRING:
		pc = "inline"; break;
	case SUMMER:
		pc = "swimming"; break;
	case FALL:
		pc = "trip"; break; 
	case WINTER:
		pc = "skiing"; break;
	}
	printf("나의 레저 활동 => %s\n", pc);

	return 0;

}

열겨형은 변수에 저장할 수 있는 정수값을 기호로 정의하여 나열합니다. 

 

 

typedef  형재정의 

#include<stdio.h>
typedef struct {
	int num;
	double grade;
}Student;
void print_data(Student* ps);

int main(void) {
	Student s1 = { 315,4.2 };
	print_data(&s1);
	return 0;
}
void print_data(Student * ps) {
	printf("학점 : %d\n", ps->num);
	printf("학점 : %.1lf\n", ps->grade);
}

 

 

typedef 으로 재정의 예제 

#include<stdio.h>

typedef union {
	int ea;
	double kg;
	double liter;
}Unit;

typedef struct {
	char name[20];
	enum { EGG = 1, MILK, MEAT } kind;
	Unit amount;
}Gift;

void print_list(Gift a);

int main(void) {
	Gift list[5];
	int i;

	for (i = 0; i < 5; i++) {
		printf("이름 입력: ");
		scanf_s("%s", list[i].name);
		printf("품목 선택(1.계란, 2.우유, 3.고기) : ");
		scanf_s("%d", &list[i].kind);

		switch (list[i].kind) {
		case EGG: list[i].amount.ea = 30; break;
		case MILK: list[i].amount.liter = 4.5; break;
		case MEAT: list[i].amount.kg = 0.6; break;
		}
	}

	printf("# 세번째 경품 당첨자... \n");
	print_list(list[2]);

	return 0;
}

void print_list(Gift a) {
	printf("이름 : %s, 선택 품목 : ", a.name);
	switch (a.kind) {
	case EGG: printf("계란 %d개\n", a.amount.ea); break;
	case MILK: printf("우유 %.1lf리터\n", a.amount.liter); break;
	case MEAT: printf("고기 %.1lfkg\n", a.amount.kg
	); break;
	}
}

 

#define

#include<stdio.h>
#define PRINT_EXPR(x) printf(#x " = %d\n",x)
#define NAME_CAT(x,y) (x##y)

// #은 매크로 함수의 인수를 문자열로 치환하고, ##은 두 인수를 붙여서 치환합니다.  

int main(void){
	int a1, a2;
	
	NAME_CAT(a,1) = 10;
	NAME_CAT(a,2) = 20;
	PRINT_EXPR(a1+a2);
	PRINT_EXPR(a2-a1);
	
	return 0;
}
#include<stdio.h>
#define VER 7
#define BIT16

int main(void){
	int max;
	
#if VER >6 
	printf("버전 %d 입니다. \n",VER);
#endif


#ifdef BIT16
	max = 32767;
#else
	max = 2147483647;
#endif 

	printf("int형 변수의 최댓값 : %d\n",max);
	
	return 0;
} 
#include<stdio.h>
#define VER 7
#define BIT16

int main(void){
	int max;
	
#if VER >6 
	printf("버전 %d 입니다. \n",VER);
#endif

#undef BIT16

#ifdef BIT16
	max = 32767;
#else
#error BIT16 이 정의되어있어야 합니다.  
#endif 

	printf("int형 변수의 최댓값 : %d\n",max);
	
	return 0;
} 

 

 

#pragma 지시자

#include<stdio.h>
#pragma warning(disable:4996) //4996번 경고 메시지는 모두 표시하지 않음. 

#pragma pack(push,1) //바이트 얼라인먼트를 1로 바꿈  
typedef struct{
	char ch;
	int in;
}Sample1;
 
#pragma pack(pop)   //바꾸기 전의 바이트 얼라인먼트 적용  
typedef struct{
	char ch;
	int in;
}Sample2;

int main(void){
	printf("Sample1 구조체의 크기 : %d 바이트\n",sizeof(Sample1));
	printf("Sample2 구조체의 크기 : %d 바이트\n",sizeof(Sample2));
	
	//Sample1 구조체의 크기 : 5 바이트
	//Sample2 구조체의 크기 : 8 바이트
	
	return 0;
} 

 

visual studio 2019를 써서 컴파일 했는데, cpp 컴파일에서 해서 그런지 예제들 따라해보면서 자잘한 워닝이 자주 떴다. 마지막 예제같은 경우는 작동도 되지 않았다. 그래서 dev c++를 사용해서 c파일로 만들어서 작성해주니 잘 작동하더라.

 

c 공부할 때는 그냥 dev 사용하는 게 더 좋을 듯 하다.

 

커뮤니티를 보면 visual studio에서 c 컴파일러 완벽히 지원안하네 어쩌네 댓글로 본적이 몇 번있었는데, 그것때문에 그런건가 싶기도 하다. 물론 표준 탓하기 이전에 내 코드가 맞는건지 확인하는 게 먼저인듯싶다. 

 

 

 

반응형