구조체 - 2
카테고리: C_grammer
태그: Grammer C Language
14. 구조체
14.15 공용체의 원리
공용체 vs 구조체
공용체는 서로 다른 자료형이 같은 메모리 주소를 공유한다. (같은 저장공간을 사용한다.)
공용체를 만들어 주소를 확인해보자.
- 공용체 멤버변수 중 저장공간이 가장 많이 필요한 double을 기준으로 메모리를 할당받았다.
- 공용체, 공용체 멤버변수는 모두 같은 주소값을 공유하고 있다.
/*
Union
- Store different data types in the same memory space
*/
union my_union
{
int i;
double d;
char c;
};
union my_union uni;
typedef union
{
int i;
double d;
char c;
}Simple_union;
Simple_union sim_uni;
printf("Size of union : %zd\n", sizeof(union my_union));
printf("address union : %p\n", &uni);
printf("each member's add : %p %p %p \n", &(uni.i), &(uni.d), &(uni.c));
union my_union union_list[10];
printf("size of union arr : %zd\n", sizeof(union_list));
Output
size of union : 8
addres union : 007AFB94
each member’s add :007AFB94 007AFB94 007AFB94
size of union arr : 80
공용체 멤버 변수에 값을 할당하고 다른 변수가 어떻게 변하는지 확인해보자.
uni.c = 'A';
printf("%c %x %d\n", uni.c, (int)uni.c, uni.i);
uni.i = 0;
uni.c = 'A';
printf("%c %x %d \n", uni.c, (int)uni.c, uni.i);
uni.d = 1.2;
printf("%c %x %d %f \n", uni.c, (int)uni.c, uni.i, uni.d);
Output A 41 -858993599 A 41 65 3 33 858993459 1.200000 // first 3 is ‘3’ = 51
Union's Assignment Operator, Pointer and Usage
/*
Initializing union
*/
// You can assigning only one member variable
union my_union uni_assignment = uni;
union my_union uni_firs_only = { 10 }; // { 10, 3.14 } <= Error!
union my_union uni_designate = { .c = 'A' };
union my_union uni_donot = { .d = 1.23, .i = 100 }; // Do not use in this way
printf("%c %f %d \n", uni_donot.i, uni_donot.d, uni_donot.c); // >> d 0.000000 100
uni.i = 123;
uni.d = 1.2;
uni.c = 'k';
printf("%c %f %d \n", uni.i, uni.d, uni.c); // >> k 1.200000 107
union my_union* uni_ptr = &uni; // Pointer to union
int x = uni_ptr->c;
uni.c = 'A'; // Don't do this uni.d has 'A's
double readl = 3.14 * uni.d; // Bianry data is converting to double
14.16 공용체와 구조체를 함께 사용하기
멤버 변수 여럿 중 단 하나만 사용할 때, 멤버 변수를 서로 배타적으로 사용할 때 공용체를 사용해보자.
주로 구조체와 함께 사용한다.
struct personal_owner
{
char rrn_h[7]; // Resident Registration Number
char rrn_t[8]; // 830422 - 1185600
};
struct company_owner
{
char crn_h[4]; // Company Registration Number
char crn_m[3]; // 111 - 22 - 33333
char crn_t[6];
};
union data
{
struct personal_owner pers; // mutually exlusive
struct company_owner comp;
};
struct car_data
{
char model[15];
int status; // 0 = personal, 1 = company
union data ownerinfo;
};
void print_car(struct car_data car)
{
printf("-------------------------------------------\n");
printf("Car model : %s\n", car.model);
if(car.status == 0) // personal = 0, company = 0
printf("Personal Owner : %s - %s\n", car.ownerinfo.pers.rrn_h, car.ownerinfo.pers.rrn_t);
else
printf("Company Owner : %s - %s - %s\n", car.ownerinfo.comp.crn_h, car.ownerinfo.comp.crn_m, car.ownerinfo.comp.crn_t);
printf("-------------------------------------------\n");
}
int main()
{
struct car_data my_car = { .model = "Avante", .status = 0, .ownerinfo.pers = { .rrn_h = "990830", .rrn_t = "1123456" };
struct car_data company_car = { .model = "Sonata", .status = 1, .ownerinfo.comp = { .crn_h = "111", .crn_m = "22", .crn_t = "33333" }};
print_car(my_car);
print_car(company_car);
return 0;
}
14.17 익명 공용체
위 코드에서 union data를 struct 안에서 익명으로 만들 수 있다. 멤버 변수를 한 번 덜 호출할 수 있어 편하다.
struct car_data
{
char model[15];
int status; // Personal = 0, Company = 1
union{
struct personal_owner pers; // mutually exclusive
struct company_owner comp;
};
};
전체 코드는 다음과 같다.
struct personal_owner
{
char rrn_h[7]; // Resident Registration Number
char rrn_t[8]; // 830422 - 1185600
};
struct company_owner
{
char crn_h[4]; // Company Registration Number
char crn_m[3]; // 111 - 22 - 33333
char crn_t[6];
};
struct car_data
{
char model[15];
int status; /* 0 = psersonal, 1 = compnay */
union{
struct personal_owner pers; // mutually exclusive
struct company_owner comp;
};
};
void print_car(struct car_data car)
{
printf("-------------------------------------------\n");
printf("Car model : %s\n", car.model);
if (car.status == 0) /* personal = 0, company = 1 */
printf("Personal Owner : %s-%s\n", car.pers.rrn_h,
car.pers.rrn_t);
else
printf("Company Owner : %s-%s-%s\n", car.comp.crn_h,
car.comp.crn_m, car.comp.crn_t);
printf("-------------------------------------------\n");
}
int main()
{
struct car_data my_car = { .model = "Avante", .status = 0,
.pers = {.rrn_h = "990830",.rrn_t = "1123456"} };
struct car_data company_car = { .model = "Sonata", .status = 1,
.comp = {.crn_h = "111", .crn_m = "22",.crn_t = "33333"} };
print_car(my_car);
print_car(company_car);
return 0;
}
같은 자료형이지만 다양한 식별자를 사용하고 싶다면 공용체를 사용하자.
- 배열 식별자와 개별 식별자를 함께 사용할 때 특히 유용하다.
struct vector2D
{
union{
struct { double x, y };
struct { double i, j };
struct { double arr[2] };
};
};
typedef struct vector2D Vec2;
Vec2 v2 = { 3.14, 2.99 };
printf("%.2f %.2f \n", v2.x, v2.y); // >> 3.14 2.99
printf("%.2f %.2f \n", v2.i, v2.j); // >> 3.14 2.99
printf("%.2f %.2f \n", v2.arr[0]. v2.arr[1]); // >> 3.14 2.99
for(int i = 0 ; i < 2 ; ++i)
printf("%.2f ", v2.arr[i]);
14.18 열거형
숫자를 이용하며 다양한 변수나 상태를 표현하라면 다음과 같은 방법이 있다.
- 숫자 - 특징 대응 표 만들기
- define으로 정의하기
// Red : 0, Orage : 1, Yellow : 2, Green : 3, ...
// this make programmer hard to remember everything
int color = 0;
if(color == 0)
printf("Red\n");
else if(color == 1)
printf("Orange\n");
else if(color == 2)
printf("Yellow\n");
// Preprocessor coping every degine words to it's usage
// Compiler can't catch any errors if define words has problem
#define RED 1
#define ORANGE 2
#define YELLOW 3
color = YELLOW;
if(color == YELLOW)
printf("Yellow\n");
열거형을 쓰면 간단하게 바뀐다.
열거형 사용해보기
- 열거형을 사용하면 정수 상수를 기호, 단어로 부를 수 있다.
- 열거형은 읽기 쉽고 유지보수에 유리하다.
- 컴파일러가 확인할 수 있어 에러 발견이 쉽다.
/*
Enumerated type
- Symbolic names to represent integer constants
- Improve readability and make it easy to maintain
- 'enum' specifier ( 'struct' specifier, 'union' specifier )
Enumerators
- The symbolic constants
*/
enum spectrum { red, orange, yellow, green, blue, violet } // each vairable is eumerator
// 0 1 2 3 4 5
enum spectrum color; // you can assign any enumerator of this type
color = blue; // like this
if(color == yellow)
printf("yello\n");
for(color = red ; color <= violet ; color++) // Note: ++operator doesn't allowed in C++
printf("%d \n", color);
printf("red = %d, orange = %d\n", red, orange);
열거형은 개별 변수의 값을 정해줄 수 있다
- 이를 활용하여 if 조건문에 특정 정수 상수를 넣어줄 수 있다. 유지보수가 쉽다.
enum levels{
low = 100,
medium = 500,
high = 2000;
};
int score = 700; // TODO : user input
if(score > high)
printf("High score!\n");
else if(score > medium)
printf("Good job!\n");
else if(socre > low)
printf("Not bad\n");
else
printf("Do your best\n");
할당해주지 않은 변수는 자동으로 할당된다. 다음 예제를 보자.
enum pet{
cat,
dog = 10,
lion,
tiger,
}
printf("cat = %d\n", cat); // >> cat = 0
printf("lion = %d\n", lion); // >> lion = 11
printf("tiger = %d\n", tiger); // >> tiger = 12
14.19 열거형 연습문제
열거형을 이용하여 for문을 쉽게 사용할 수 있다.
for(color = red; color <= blud ; ++color)
문제풀이
#define LEN 30
enum spectrum
{
red,
orange,
yellow,
green,
blue
};
const char* colors[] = { "red", "orange", "yellow", "green", "blue" };
int main()
{
char choice[LEN] = { 0, };
enum spectrum color;
bool color_is_found = false;
while(1)
{
printf("Input any color you want(Enter to quit)\n");
if(scanf("%[^\n]%*c", choice) != 1)
{
printf("Good day to see you\n");
exit(1);
}
// for(color = red ; color <= blue ; ++color) { ... }
for(int i = 0 ; i < 5 ; ++i)
{
if(strcmp(choice, colors[i]) == 0)
{
color_is_found = true;
color = i;
break;
}
}
if(color_is_found)
printf("%s is %i\n", colors[color], color);
else
printf("try it again\n");
color_is_found = false;
}
return 0;
}
14.20 이름공간 공유하기
Namespace
- Restricted space that program could recognize identifier
- Specifier’s tag and this identifier could use same name (C++ block this)
- C++ : You can declare duplicated identifier with different namespace in one place
- 같은 Scope 내에 중복된 식별자를 가질 수 없다. 자료형이 달라도 마찬가지이다.
double myname = 3.14;
int myname = 345; // Error!
enum myname = { Kim, Lee, Park };
enum myname myname = Kim;
int myname = 345; // Error
다른 Scope 라면 중복된 식별자를 가질 수 있다. 이 때 바깥 Scope에 있는 식별자는 내부에서 사용할 수 없다.
int myname = 123;
{
int myname = 345; // myname out of scope is hiding
}
- Specifier에서 사용한 tag는 해당 변수의 식별자로 사용할 수 있다. (C++에서는 금지한다.) » 사용하지 말자.
struct test
{
int i;
int j;
};
struct test test = { .i = 1, .j = 2 };
int test = 111; // Error!
struct rect { double x; double y; };
struct rect rect = { .x = 1.1, .y = 2.2 };
// Struct two and int two are in different categories
struct two { int one; int two; };
int two = 123; // OK in C(Error in C++) but not recommand
- typedef, specifier, identifier 3개 이상 겹치면 안된다.
- 아래 예제에서 thr을 three로 바꾸거나 fo를 four로 바꾸었을 때 컴파일 에러가 발생한다.
struct three
{
int one;
int two;
int three;
};
typedef struct three three;
three thr = { 1, 2 };
printf("thr %i %i \n", thr.one, thr.two); // OK
struct four
{
int one;
int two;
};
typedef struct four fo;
fo four = { 1, 2 };
printf("four %i %i \n", four.one, four.two); // OK
같은 Namespace에서 함수명과 변수명이 동일하면 Error
int iamfunction()
{
return 0;
}
int main()
{
int i = iamfunction();
int iamfunction = iamfunction(); // Error!
}
Namespace Pollution
- 전역변수는 다른 파일에서 선언되더라도 중복된 식별자를 가질 수 업다. Namespace 내부에 정의하면 해결가능하다.
// Other.c
int i = 5;
// main.c
int i = 5;
int main()
{
// Error occured at linking
return 0;
}
14.21 함수 포인터의 원리
함수 실행의 의미
- 컴파일러는 식별자를 메모리 주소로 번역하여 찾아간다. 해당 주소에 저장되어 있는 명령어를 실행한다.
프로그램은 어떻게 실행될까?
- 코드 작성 -> 컴파일 -> 실행파일 생성(하드디스크에 저장) -> OS에 요청(실행파일 실행) -> OS : 프로그램 메모리를 복사, RAM에 붙여넣기 -> RAM의 TextSegment(Read Only)에 저장된다.
위 코드에서 message 자기 자신의 주소는 stack에 저장되어 있다. message 값은 TextSegment에 저장되어있다.
f_ptr(함수 포인터) 자기 자신의 주소는 Stack에 저장되어 있다. f_ptr(함수 포인터) 값은 TextSegment에 저장되어있다.
main(함수)의 이름은 포인터이다. main 함수는 TextSegment에 저장되어있다.
예제
- 함수 포인터는 해당 함수 주소값을 저장한다.
- 함수의 이름은 포인터이므로 Ampersand(&)를 사용하지 않아도 된다.(사용해도 된다.)
- 포인터 문법대로 indirection하여 사용할 수 있다. 물론 indirection 없이도 사용가능하다.
void func1()
{
printf("func1 is working\n");
}
int func2(int i)
{
printf("func2 is working\n");
return i * -1;
}
double func3(int i, double d)
{
printf("func3 is working\n");
return i + d;
}
int main()
{
// return identifier parameter should be match to declaration
void (*pf1) () = func1 // &func1 also work
int (*pf2)(int) = func2;
double (*pf3)(int, double) = func3;
pf1();
(*pf1)();
int test2 = pf2(5);
int test2_ = (*pf2)(5);
printf("%i %i", test2, test2_);
}
14.22 함수 포인터의 사용 방법
다음 코드에서 ToUpper 함수와 ToLower 함수는 Parameter와 반환값의 자료형이 동일하다.
// ToUpper and ToLower has same form so you can use same func ptr
void ToUpper(char* str) // Convert Every char to Uppercase
{
while(*str)
{
*str = toupper(*str);
str++;
}
}
void ToLower(char* str)
{
while(*str)
{
*str = tolower(*str);
str++;
}
}
int main()
{
char str[] = "Hello, World!";
void (*func_ptr)(char*);
func_ptr = ToUpper;
// func_ptr = &ToUpper; // Acceptable
// func_ptr = ToUpper(str); // Not Acceptable in C,
// It execute function and assign it's value on func_ptr
printf("String Literal add %p\n", ("Hello, World!"));
printf("Function pointer add %p\n", ToUpper);
printf("Variable add %p\n", str);
func_ptr(str);
(*func_ptr)(str); // Accept in ANSI, Not in K&R
printf("ToUpper >> %s\n", str);
func_ptr = ToLower;
func_ptr(str);
printf("ToLower >> %s\n", str);
return 0;
}
함수 포인터를 활용하여 ToUpper 함수와 ToLower 함수를 간단하게 사용하자
void UpdateString(char* str, int (*func_ptr)(int))
{
while(*str)
{
*str = func_ptr(*str); // (*func_ptr)(str)
str++;
}
}
int main()
{
char str[] = "Hello, World!";
UpdateString(str, toupper);
printf("Update toupper >> %s \n", str);
UpdateString(str, tolower);
printf("Update tolower >> %s \n", str);
return 0;
}
14.23 자료형에게 별명을 붙여주는 typedef
특징
- 특정 자료형을 다른 이름으로 부를 수 있다.
- 새로운 자료형을 만들지 않는다.
장점
- 사용의도를 자료형에 나타낼 수 있다.
- 짧게 쓸 수 있다.
- 사용 환경에 맞게 자동으로 변환시킬 수 있다.
typedef의 장점 - Portable(이식성이 높다)
- 개발 환경마다 다른 자료형을 가지는 자료형을 쉽게 사용학 위해 typedef를 사용할 수 있다. 아래 코드에서 size_t는 x86, x64 환경에서 각각 다른 값을 가진다.
typedef unsigned char BYTE;
BYTE x, y[10] = { 0, }, *z = &x;
{
typedef unsigned char byte;
size_t s = sizeof(byte); // unsigned int (x86), unsigned long long (x64)
/*
above code solve this imcompatible code problem
// unsigned int s = sizeof(byte); // x86
// unsigned long long s = sizeof(byte); // x64
*/
// byte b; // Error! typedef belong to above scope
}
size_t의 정의를 찾아가자. 다음은 typedef의 활용법을 잘 보여주고 있다.
#ifdef _WIN64
typedef unsigned __int64 size_t;
#else
typedef unsigned int size_t;
자주 사용하는 함수 time의 반환값은 환경에 따라 달라진다. time_t가 typedef로 정의되어 있기 때문에 가변적으로 사용가능하다.
// #include <time.h>
/*
This function returns the time since 00:00:00 UTC
January 1, 197 (Unix time stamp) in sencods
*/
time_t t = time(NULL);
printf("%lld\n", t);
time_t의 정의를 찾아가면 typedef를 활용하고 있다.
// correct.h
#ifndef _CRT_NO_TIME_T
#ifdef _USE_32BIT_TIME_T
typedef __time32_t time_t;
#else
typedef __time64_t time_t;
// ...
typedef long __time32_t;
typedef __int64 __time64_t;
typedef vs #define
- 전처리기는 #define에 등록된 Macro를 해당값으로 복사/붙여넣기 한다.
- 에러가 만들기 쉬우니 typedef를 권장한다.
/*
- typedef interpretation is performed by the compiler, not the preprocessor
- mode flexible than #define
*/
typedef char* STRING;
STRING name = "John Wick", actor = "Keanu Reeves";
printf("typedef %s %s\n", name, actor);
#define STRING_C char*
STRING_C name_c = "Daenerys Targaryen", *actor_c = "Emilia Clarke";
printf("#define %s %s\n", name_c, actor_c);
STRING을 char로 대체했다. *actor_c에서 Asterisk()가 없으면 char 자료형이 된다. 실수하기 쉽다.
typedef를 활용하여 구조체 자료형 나타내기
typedef struct complex
{
float real;
float img;
} COMPLEX; // it makes skip to type "typedef struct comple COMPLEX"
typedef struct
{
double width;
double height;
} rect; // No tag
rect rec1 = { 1.1, 2.2 };
rect rec2 = rec1;
복잡한 함수포인터 typedef로 해석하기
- 복잡한 포인터를 어떻게 해석하는지 다음 장에서 다룬다.
- typedef를 사용하면 간단하게 표현할 수 있다는 점만 기억하고 넘어가자.
/*
"One Good way to understand complicated
declarations is small steps with typedef"
- K&R book chapter 5.12
*/
char char3[3] = { 'A', 'B', '\0' };
// Complicated Function Declarations
char (*complicated_func())[3] // Returns Pointer to char[3]
{
return &char3; // Returns a pointer pointing to char[3]
}
char (*(*fptr1)())[3] = complicated_func; // so messy
// bring me in peace and calm
typedef char(*FuncReturningPtr())[3]; // Function Return Pointer to char[3]
typedef char(*(*PTRFuncReturningPtr)())[3];
FuncReturningPtr* fptr2 = complicated_func;
PTRFuncReturningPtr fptr3 = complicated_func;
typedef char c_triple[3];
c_triple* complicated_func_simp()
{
return &char3;
}
int main()
{
char(*returned)[3] = fptr1();
c_triple *returned2 = fptr2();
c_triple *returned3 = fptr3();
c_triple *returned4 = complicated_func_simp();
printf("%c %c %c %c \n", (*returned)[0], (*returned2)[1], (*returned3)[0], (*returned4)[1] );
return 0;
}
14.24 복잡한 선언을 해석하는 요령
실무에서 복잡한 선언을 보기는 어렵다. 보통은 typedef로 간소화하여 사용하고 이해한다. 원리만 알고가자.
/*
* indicates a pointer
() indicates a function, not priority
[] indicates an array
Deciphering Complex Declarations (KNK 18.4)
- Always read declarations from the inside out.
- When there's a choice, always favar [] and () over *(asterisk)
*/
- identifier 식별자를 찾는다.
- 식별자를 기준으로 항상 안쪽에서 바깥쪽으로 읽어라.
- []와 ()는 *(asterisk) 보다 우선순위를 높게 해석한다.
복잡한 함수 포인터 살펴보기
int temp(int a)
{
return 0;
}
int (*g(int a))(int) // Not used in pratically
{ // Normally convert to typedef style
return temp;
}
위 사례처럼 복잡한 g 함수포인터는 사용하지 않는다. typedef를 이용하여 간소화 해줌이 바람직하다.
반환값이 복잡할 때 typedef로 간소화하기
int* ap[10]; // Identifier ap is array saving pointer
typedef int* ptr_int;
ptr_int typedef_ap[10];
float* fp(float); // fp is function takes float parameter, return float ptr
typedef float* ptr_float;
ptr_float fp(float);
복잡한 함수 포인터를 해석해보자
void(*pf)(int);
/*
void(*pf2)(int);
1 :: 1. this is pointer
2 :: 2. take int type parameter so function ptr
3 :: 3. return void type
*/
int* (*x[10])(void);
/*
int* (*x[10])(void);
1 :: 1. array save 10 elem
2 :: 2. pointer
3 :: 3. take void type parameter, so function ptr
4 :: 4. return int pointer
*/
잘못된 사례
- 함수는 배열을 반환할 수 없다.
- 배열을 가리키는 포인터는 반환할 수 있다.
// A function can't return an array
int f(int)[]; // Error - Wrong
// But it can return a pointer to an array
int(*f(int))[]
- 함수는 함수를 반환할 수 없다. (C 한정)
- 함수가 함수를 가리키는 포인터를 반환할 수 있다.
// A function can't return a function
int g(int)(int); // Error! - Wrong
// But it can return a pointer to function
int (*g(int))(int);
- 여러 함수를 담은 배열은 존재할 수 없다.
- 함수 포인터를 담은 배열은 가능하다.
// An array of functions are impossible
int a[10](int) // Error! - Wrong
// But an array of function pointers are possible
int* (*x2[10])(void)
x2는 함수포인터를 담은 배열이다. 이를 간단하게 나타내보자.
// using typedef to simplify declaration
typedef int FCN(int);
typedef FCN* FCN_PTR;
typedef FCN_PTR FCN_PTR_ARRAY[10];
FCN_PTR_ARRAY x3;
추가 예제
int board[6][4]; // an array of arrays of int
int** db_ptr;
int* risk[10]; // array saves 10 in pointer
int(*risk)[10]; // pointer pointing to array saving 10 int
int* off[3][4]; // 4 x 3 array saves 12 int pointers
int(*uff)[3][4]; // pointer pointing to 4 * 3 int array
int(*uof[3])[4]; // array saves 3 pointers pointing to 4 element int array
int(*uof[3])[4]가 해석하기 어렵다. 3개의 int pointer가 4개짜리 배열을 가리키고 있다.
char* fump(int); // function returning char*
char (*frump)(int); // function pointer, pointed function retucn char
char (*flump[3])(int); // array of 3 pointers to functions that return type char
char (flump[3])(int)가 해석하기 어렵다. parameter와 반환값은 쉽계 에측이 가능하다. (flump[3])은 배열 안에 함수 포인터가 3개 있음을 가리킨다.
typedef int arr[5];
typedef arr* p_arr;
typedef p_arr ten_p_arr[10];
arr five_arr;
p_arr ptr_five_arr;
ten_p_arr ptr_five_arr_ten_times;
14.25 qsort() 함수 포인터 연습문제
예제를 보고 float 자료형으로 구현해보자
#include <stdio.h>
#include <stdlib.h>
int compare(const void* first, const void* second)
{
if(*(int*)first > *(int*)second)
return 1;
else if(*(int*)first < *(int*)second)
return -1;
else
return 0;
}
int main()
{
int arr[] = { 8, 2, 5, 3, 6, 11 };
int n = sizeof(arr) / sizeof(arr[0]);
qsort(arr, n, sizeof(int), compare)
for(int i = 0 ; i < n ; ++i)
printf("%d ", arr[i]);
}
struct 자료형으로 구현하기
struct kid
{
char name[10];
int height;
};
// TODO : Try increasing / decreasing order
int compare_kid(const void* first, const void* second)
{
if(((struct kid*)first->height > ((struct kid*)second)->height)
return 1;
else if(((struct kid*)first-> height < ((struct kid*)second->height)
return -1;
else
return 0;
/*
typedef struct kid* kid_ptr;
kid_ptr kid_ptr_1 = (kid_ptr)first;
kid_ptr kid_ptr_2 = (kid_ptr)second;
if(kid_ptr_1->height > kid_ptr_2->height)
return 1;
else if(kid_ptr_1->height < kid_ptr_2->height)
return -1;
else
return 0;
*/
}
int main()
{
struct kid friends[] = { "Jack Jack", 40, "Genie", 300, "Aladdin", 170, "Piona", 150 };
// struct kid friends[] = { { "Jack Jack", 40 }, { "Genie", 300 }, { "Aladdin", 170 }, { "Piona", 150 } };
const int n = sizeof(friends) / sizeof(struct kid);
qsort(friends, n, sizeof(struct kid), compare_kid);
for(int i = 0 ; i < n ; ++i)
printf("%s \tis %3i height\n", friends[i].name, friends[i].height);
}
구조체 배열을 정의할 때 각 구조체 변수의 값을 직렬로 늘려놓아도 된다.
14.26 함수 포인터의 배열 연습문제
void update_string(char* str, int(*pf)(int));
void ToUpper(char* str);
void ToLower(char* str);
void Transpose(char* str);
int main()
{
char option[] = { 'u', 'l' }; // let's add 't'
int n = sizeof(option) / sizeof(option[0]);
typedef void (*FUNC_TYPE)(char*);
FUNC_TYPE operation[] = { ToUpper, ToLower }; // You can add transpose
printf("Enter a string\n>> ");
char input[100];
int scanned;
while((scanned = scanf("%[^\n]%*c", input)) != 1)
printf("Please try again\n");
while(1)
{
printf("Choose an option:\n");
printf("u ) to upper\n");
printf("l ) to lower\n");
char c;
while(scanf("%c%*[^\n]*c", &c) != 1)
printf("Please try again\n");
bool found = false;
for(int i = 0 ; i < n ; ++i)
{
if(option[i] == c)
{
(*(operation[i]))(input);
found = true;
break;
}
}
if(found)
break;
else
printf("Wrong Input, try again\n");
}
printf("result = %s \n", input);
}
void update_string(char* str, int(*pf)(int))
{
while(*str)
{
*str = (*pf)(*str);
str++;
}
}
void ToUpper(char* str)
{
while(*str != '\0')
{
*str = toupper(*str);
str++;
}
}
void ToLower(char* str)
{
while(*str != '\0')
{
*str = tolower(*str);
str++;
}
}
void Transpose(char* str)
{
while(*str)
{
if(islower(*str))
*str = toupper(*str);
else if(isupper(*str))
*str = tolower(*str);
str++;
}
}
🐢 현재 공부하고 있는 홍정모의 따라하며 배우는 C언어 를 학습하며 기록 및 정리하는 포스팅입니다. 🐢
수강 사이트
https://www.inflearn.com/course/following-c/
감사합니다.😊
댓글남기기