이 페이지 요약 

코딩도장 Unit 48 ~ 50

1. 구조체, 구조체에서 포인터 사용

2. 두 점 사이의 거리 구하기(math.h 헤더 파일의 함수들)

 

Unit 48. 구조체 사용하기

인적 정보를 처리할 경우 이름, 나이 , 주소 등을 저장할 변수가 필요하다. 각각 변수를 만들어 저장할 경우 한 사람의 정보만 저장할 수 있고, 여러명의 정보를 저장하려면 변수를 계속 만들어야 하므로 비효율 적이다.

구조체는 struct 키워드로 정의 하며 다음과 같이 사용할 수 있다.

struct Person {
    char name[20];        // 이름
    int age;              // 나이
    char address[100];    // 주소
};

이름, 나이, 주소 정보가 Person 이라는 구조체에 들어가 사람 단위로 정보를 처리할 수 있다.

구조체는 관련 정보를 하나의 의미로 묶을 때 사용한다. 목적에 맞는 자료형을 만들어서 사용하는데 기본 자료형을 조합하여 만든 자료형을 파생형이라 한다.

48.1 구조체를 만들고 사용하기

구조체는 struct 키워드로 정의한다. 정의 한 후 변수로 선언하여 사용한다.

다음은 인적 정보를 표현하는 구조체를 만들고 사용한 것이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <stdio.h>
#include <string.h>
 
struct Person {
    char name[20];
    int age;
    int address[100];
};
 
int main()
{
    struct Person p1;
    
    strcpy(p1.name, "홍길동");
    p1.age = 30;
    strcpy(p1.address, "서울시 용산구");
 
    printf("이름 : %s\n", p1.name);
    printf("나이 : %d\n", p1.age);
    printf("주소 : %s\n", p1.address);
 
    return 0;
}
cs

struct 키워드 뒤에 구조체 이름을 지정하고 중괄호 안에 변수를 선언한다. 구조체 안에 들어있는 변수를 멤버라고 부른다. 구조체를 정의할 때는 }(닫는 중괄호) 뒤에는 반드시 ; (세미클론)을 붙여줘야 한다. 구조체는 보통 main 함수 바깥에 정의한다. 함수 안에 구조체를 정의하면 해당 함수 안에서만 구조체를 사용할 수 있다.

정의한 구조체를 사용하려면 구조체 변수를 선언해야 하며 구조체 이름 앞에 struct 키워드를 붙여줘야 한다.

구조체 멤버에 접근할 때는 .(점)을 사용한다. p1.age = 30; 과 같이 구조체 멤버에 접근한 뒤 값을 할당하고 값을 가져온다. 문자열 멤버는 할당연산자로 저장할 수 없으므로 strcpy함수를 사용하면 된다.

다음과 같이 중괄호와 세미클론 사이에 변수를 지정하면 구조체를 정의하는 동시에 변수를 선언할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>
#include <string.h>
 
struct Person {
    char name[20];
    int age;
    int address[100];
} p1;
 
int main()
{   
    strcpy(p1.name, "홍길동");
    p1.age = 30;
    strcpy(p1.address, "서울시 용산구");
 
    printf("이름 : %s\n", p1.name);
    printf("나이 : %d\n", p1.age);
    printf("주소 : %s\n", p1.address);
 
    return 0;
}
cs

구조체를 정의하면서 닫는 중괄호와 세미클론 사이에 변수를 지정하면 구조체를 정의하는 동시에 변수가 선언된다. 이와 같이 선언된 변수 p1은 main함수 바깥에 선언되어 있으며 전역변수이다.

구조체 변수를 선언하는 동시에 값을 초기화 하려면 중괄호 안에 .(점)과 멤버 이름을 적고 값을 할당한다. 또한 멤버 이름과 할당 연산자 없이 값만 콤마로 구분하여 나열해도 되는데, 이러한 경우 처음부터 순서대로 값을 채워야 하며 중간에 있는 멤버만 값을 할당하거나 생략할 수 는 없다.

  • struct 구조체이름 변수이름 = { .멤버이름1 = 값1, .멤버이름2 = 값2 };
  • struct 구조체이름 변수이름 = {값1, 값2};

48.2 typedef로 struct 키워드 없이 구조체 선언하기

typedef로 구조체를 정의하며 별칭(alias)을 지정할 수 있다.

구조체 이름과 구조체 별칭은 겹쳐도 되지만 구분하기 위해 구조체 이름은 앞에 _을 붙이겠다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <stdio.h>
#include <string.h>
 
typedef struct Person_ {
    char name[20];
    int age;
    int address[100];
} Person;
 
int main()
{
    Person p1;
    
    strcpy(p1.name, "홍길동");
    p1.age = 30;
    strcpy(p1.address, "서울시 용산구");
 
    printf("이름 : %s\n", p1.name);
    printf("나이 : %d\n", p1.age);
    printf("주소 : %s\n", p1.address);
 
    return 0;
}
cs

구조체를 정의할 땐 맨 앞에 typedef를 붙이고, 구조체를 정의한다. 그리고 닫는 중괄호와 세미클론 사이에 구조체 별칭을 지정하면 된다.

구조체 변수를 선언할 때는 struct 키워드를 생략하고 구조체 별칭으로 바로 변수를 선언할 수 있다. 구조체 별칭으로 선언한 변수도 멤버에 접근할 때는 점을 사용한다.

struct 뒤에 붙는 구조체 이름은 원래 태그(tag)라 부른다.

48.3 익명 구조체 사용하기

typedef로 구조체를 정의하면서 이름을 생략할 수 있다. 변수는 구조체 별칭으로 선언하면 된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <stdio.h>
#include <string.h>
 
typedef struct{
    char name[20];
    int age;
    int address[100];
} Person;
 
int main()
{
    Person p1;
    
    strcpy(p1.name, "홍길동");
    p1.age = 30;
    strcpy(p1.address, "서울시 용산구");
 
    printf("이름 : %s\n", p1.name);
    printf("나이 : %d\n", p1.age);
    printf("주소 : %s\n", p1.address);
 
    return 0;
}
cs

typedef struct 뒤에 이름을 지정하지 않고 바로 여는 중괄호로 시작하면 된다.

이때는 반드시 구조체 별칭을 지정해야 한다.

구조체 변수는 구조체 별칭으로 선언하고, 멤버에 접근할 때는 점으로 접근한다.

48.4 퀴즈

정답은 c이다.

정답은 c이다.

정답은 d 이다.

정답은 익명 구조체 이다.

48.5 연습문제 : 좌표 구조체 정의하기

정답은 다음 코드와 같다.

// 1.
 Point2D{
    int x;
    int y;
};

// 2.
struct point2D

// 3.
p1.x = 10;

48.6 연습문제 : typedef로 좌표 구조체 정의하기

정답은 다음 코드와 같다.

// 1. 
struct _Point2D{
    int x;
    int y;
} Point2D;

// 2.
p1

// 3.
p1.y = 20;

48.7 연습문제 : 익명 구조체로 좌표 구조체 정의하기

정답은 } Point2D; 이다.

48.8 심사문제 : 자동차 계기판 구조체 선언하기

정답은 다음코드와 같다.

struct Dashboard d1;

d1.speed = 80;
d1.fuel = 'F';
d1.mileage = 5821.442871f;
d1.engineTemp = 200;
d1.rpm = 1830;

48.9 심사문제 : 자동차 계기판 구조체 정의하기

정답은 다음 코드와 같다.

typedef struct _Dashboard{
    int speed;
    char fuel;
    float mileage;
    int engineTemp;
    int rpm;
}Dashboard;

Unit 49. 구조체 포인터 사용하기

구조체는 멤버 변수가 여러 개 들어 있어 크기가 큰 편이기 때문에 구조체 변수를 일일이 선언해서 사용하는 것 보다 포인터에 메모리를 할당하여 사용하는 것이 효율적이다.

49.1 구조체 포인터를 선언하고 메모리 할당하기

다른 자료형 처럼 구조체도 포인터를 선언할 수 있으며 구조체 포인터에는 malloc함수를 사용하여 동적 메모리를 할당할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
 
struct Person {
    char name[20];
    int age;
    char address[100];
};
 
int main()
{
    struct Person *p1 = malloc(sizeof(struct Person));
 
    strcpy(p1->name, "홍길동");
    p1->age = 30;
    strcpy(p1->address, "서울시 용산구");
 
    printf("이름 : %s\n", p1->name);
    printf("나이 : %d\n", p1->age);
    printf("주소 : %s\n", p1->address);
 
    free(p1);
    return 0;
}
cs

struct 키워드와 구조체 이름을 사용하여 구조체 포인터를 선언한다. 포인터 변수 이므로 반드시 *을 붙인다. malloc함수로 메모리를 할당 할 때 sizeof(struct Person)과 같이 구조체 크기를 구하여 넣어준다.

구조체의 멤버에 접근할 때는 점이 아닌 -> (화살표 연산자)를 사용한다.

p1->name 같은 문자열 멤버는 할당연산자로 저장할 수 없기 때문에 strcpy 함수를 사용하면 된다.

사용이 끝났으면 free 함수를 사용하여 할당한 메모리를 해제한다.

(*p1).age 처럼 역참조를 사용하면 점으로 멤버에 접근할 수 있다.

49.2 구조체 별칭으로 포인터를 선언하고 메모리 할당하기

구조체별칭 *포인터이름 = malloc(sizeof(구조체별칭)); 의 형태로 포인터를 선언하고 메모리를 할당할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
 
typedef struct _Person {
    char name[20];
    int age;
    char address[100];
} Person;
 
int main()
{
    Person *p1 = malloc(sizeof(Person));
 
    strcpy(p1->name, "홍길동");
    p1->age = 30;
    strcpy(p1->address, "서울시 용산구");
 
    printf("이름 : %s\n", p1->name);
    printf("나이 : %d\n", p1->age);
    printf("주소 : %s\n", p1->address);
 
    free(p1);
    return 0;
}
cs

구조체 별칭을 사용하여 포인터를 바로 선언한 뒤 malloc 함수로 메모리를 할당하면 된다. 할당하는 메모리 크기도 구조체 별칭으로 바로 구할 수 있다. 구조페를 다 사용했으면 free 함수로 메모리를 해제해주면 된다.

익명 구조체도 사용하려면 구조체 별칭을 지정해줘야 하므로 메모리 할당 방법은 위와 동일하다.

49.3 구조체 포인터에 구조체 변수의 주소 할당하기

동적 메모리를 할당하지 않고 구조체 변수에 &(주소 연산자)를 사용하여 구조체 포인터를 사용할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
 
struct Person {
    char name[20];
    int age;
    char address[100];
};
 
int main()
{
    struct Person p1;
    struct Person *ptr;
 
    ptr = &p1;
    
    ptr->age = 30;
 
    printf("나이 : %d\n", p1.age);
    printf("나이 : %d\n", ptr->age);
    return 0;
}
cs

먼저 구조체 변수를 선언하고, 구조체 포인터를 선언한다.

구조체 변수는 주소 연산자 &를 사용하여 메모리 주소를 구할 수 있으며 메모리 주소는 구조체 포인터에 할당할 수 있다.

ptr은 구조체 포인터이므로 -> 으로 멤버에 접근하여 값을 할당하였다.

ptr은 p1의 메모리 주소 이므로 ptr의 멤버를 수정하면 결국 p1의 멤버도 바뀐다.

49.4 퀴즈

정답은 4 이다.

정답은 d 이다.

정답은 d 이다.

49.5 연습문제 : 학생 구조체 포인터에 메모리 할당하기

정답은 다음코드와 같다.

// 1.
malloc(sizeof(struct Student))

// 2.
strcpy(s1->name, "고길동")
s1->grade = 1;
s1->class = 3;
s1->average = 65.389999f;

49.6 연습문제 : 3차원 좌표 구조체 포인터에 메모리 할당하기

정답은 다음 코드와 같다.

// 1. 
malloc(sizeof(Point3D));

// 2.
p1->x = 10.0f;
p1->y = 20.0f;
p1->z = 30.0f;

49.7 연습문제 : 구조체 포인터에 구조체 주소 할당하기

정답은 다음 코드와 같다.

// 1. 
struct Item *ptr;

// 2.
ptr = &item1;

49.8 심사문제 : 사람과 자동차 구조체 포인터에 메모리 할당하기

정답은 다음 코드와 같다.

struct Person *p1 = malloc(sizeof(struct Person));
Car *c1 = malloc(sizeof(Car));

strcpy(p1->name, "고길동");
p1->age = 40;
strcpy(p1->address, "서울시 서초구 반포동");

strcpy(c1->name, "스텔라");
c1->number = 3421;
c1->displacement = 2000;

49.9 심사문제 : 구조체 포인터에 구조체 변수의 주소 할당하기

정답은 다음 코드와 같다.

ptr = &p1;

Unit 50. 두 점 사이의 거이 구하기

50.1 두 점 사이의 거리 구하기

2차원 평면에서 위치를 표현하려면 x와 y값이 필요하다. 구조체를 이용하여 한 점에 x와 y값을 선언하여 점을 표현할 수 있다.

두 점 사이의 거리를 구하려면 피카고라스의 정리를 이용하면 된다.

점 두 개가 있을 때 직각 삼각형을 그리면 다음과 같다.

피타고라스의 정리를 이용하여면 먼저 선 a 와 b의 길이를 구해야 하는데 구조체 변수에 두 점의 좌표가 들어있다고 가정하면 다음과 같이 구할 수 있다.

int a = p2.x - p1.x;    // 선 a의 길이
int b = p2.y - p1.y;    // 선 b의 길이

피타고라스 정리에서 c의 길이를 구하려면 제곱근을 구해야 한다.

C에서 루트는 math.h 헤더파일에 선언된 sqrt 함수를 사용하여 구할 수 있다.

C 코드로 두 점의 거리를 구하는 방법은 다음같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <stdio.h>
#include <math.h>
 
struct Point2D {
    int x;
    int y;
};
 
int main()
{
    struct Point2D p1;
    struct Point2D p2;
 
    p1.x = 30;
    p1.y = 20;
 
    p2.x = 60;
    p2.y = 50;
 
    int a = p2.x - p1.x;
    int b = p2.y - p1.y;
 
    double c = sqrt((a*a) + (b*b));
    
    printf("%f\n", c);
    return 0;
}
cs

sqrt 함수를 사용하면 넣은 값의 제곱근을 구할 수 있다. 제곱근은 소수로 나오기 때문에 double형 변수에 저장했다.

제곱을 구할 때는 math.h 헤더파일에 선언되있는 pow 함수를 사용해도 된다. a^2를 구하고 싶으면 pow(a,2)와 같이 사용한다.

abs, fabs, fabsf 함수를 사용하면 각각 정수, double형 실수, float형 실수에 대한 절댓값을 구할 수 있다.

 

50.2 연습문제 : 사각형의 넓이 구하기

정답은 다음 코드와 같다.

int w = abs(rect.x2 - rect.x1);
int h = abs(rect.y2 - rect.y1);
area = w * h;

밑변과 높이를 구하면서 음수가 나올수 있기 때문에 절댓값을 구하는 abs 함수를 사용하였다.

 

50.3 심사문제 : 두 점 사이의 거리 구하기

정답은 다음 코드와 같다.

int a = p2.x - p1.x;
int b = p2.y - p1.y;
distance = sqrt((a*a) + (b*b));

이 페이지 내용 요약

1. 문자열 관련 함수( 문자열 자르기, 문자열 <-> 숫자 변환)

2. 회문, N-gram 구현 

 

Unit 45. 문자열 자르기

45.1 문자를 기준으로 문자열 자르기

strtok 함수로 특정 문자를 기준으로 문자열을 자를 수 있다. string.h 헤더 파일에 선언되어 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#define _CRT_SECURE_NO_WARNINGS    // strtok 보안 경고로 인한 컴파일 에러 방지
#include <stdio.h>
#include <string.h>
 
int main()
{
    char s1[30= "The Littel Prince";
    char *ptr = strtok(s1, " ");
 
    while(ptr != NULL)
    {
        printf("%s\n", ptr);
        ptr = strtok(NULL" ");
    }
 
    return 0;
}
cs

strtok함수는 지정된 문자를 기준으로 문자열을 자른 다. 기준 문자는 큰 따옴표로 묶어야 한다. 위의 경우 공백 문자를 넣어 공백을 기준으로 잘랐다. 잘린 문자열을 한 번에 얻을 수 없어서 while 반복문으로 문자열을 계속 자르다가 문자열이 나오지 않으면 반복문을 끝내는 방식을 사용한다.

while 반복문 안의 strtok 함수는 자를 문자열 부분에 NULL을 넣어 주었다. NULL은 직전 strtok 함수에서 처리했던 문자열에서 잘린 문자열 만큼 다음 문자료 이동한 뒤 다음 문자열을 자른다. ptr = strtok(ptr," "); 처럼 잘린 문자열의 포인터를 다시 넣으면 다음 문자로 이동하지 못하고 처음에 나오는 문자열만 자르게 된다. strtok 함수를 사용할 때는 처음에만 자를 문자열을 넣어주고 그 다음부터는 NULL을 넣어줘야 한다.

처음 호출되는 strtok는 공백문자를 찾아서 NULL로 채운 뒤 문자열의 첫 부분인 The를 자른다.

반복문 안의 strtok에 NULL을 넣어주면 앞에서 잘린 문자열 만큼 다음 문자로 이동한 뒤 NULL로 채운뒤 Little를 자른다. 이런 식으로 반복하여 문자열 맨 끝에 있는 NULL문자를 만나면 반복문을 종료한다.

strtok 함수는 문자열을 새로 생성해서 반환하는 것이 아니라 자르는 부분으로 널문자로 채운 뒤 잘린 문자열의 포인터를 반환하는 것이다. 원본 문자열의 내용을 바꾸므로 사용에 주의해야 한다.

45.2 문자열 포인터 자르기

문자열 포인터에 문자열 리터럴이 들어 있어서 읽기 전용인 상태이면 strtok 함수는 사용할 수 없다.

문자열 포인터를 자를때는 동적 메모리를 할당하고, 문자열을 복사하여 이 문제를 해결할 수 있다.

1
2
3
4
5
6
7
8
9
char *s1 = malloc(sizeof(char* 30);    
strcpy(s1, "The Little Prince");  
char *ptr = strtok(s1, " ");   
while (ptr != NULL)
{
   printf("%s\n", ptr);
   ptr = strtok(NULL" ");
}
free(s1);    
cs

45.3 날짜와 시간값 자르기

strtok함수는 다양한 특수문자와 알파벳 영문자를 기준으로 문자열을 자를 수 있다. 또한 기준 문자는 한 번에 여러개를 지정할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#define _CRT_SECURE_NO_WARNINGS    // strtok 보안 경고로 인한 컴파일 에러 방지
#include <stdio.h>
#include <string.h>
 
int main()
{
    char s1[30= "2015-06-10T15:32:19";
    char *ptr = strtok(s1, "-T:");
 
    while(ptr != NULL)
    {
        printf("%s\n", ptr);
        ptr = strtok(NULL"-T:");
    }
 
    return 0;
}
cs

-, T, : 기준으로 문자열을 자르므로 기준 문자를 여러 개 넣을 수 있다.

45.4 자른 문자열 보관하기

문자열을 자르는 while 반복문 안에서 모든 처리를 할 수 없는 상황이 있을 수 있기 때문에 자른 문자열을 보관해야 한다. 다음 코드와 같이 문자열 포인터 배열에 자른 문자열을 보관할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#define _CRT_SECURE_NO_WARNINGS    // strtok 보안 경고로 인한 컴파일 에러 방지
#include <stdio.h>
#include <string.h>
int main()
{
    char s1[30= "The Little Prince";
    char *sArr[10= {NULL, };
    int i = 0;
    char *ptr = strtok(s1, " ");
    while (ptr != NULL)
    {
        sArr[i] = ptr;
        i++;
        ptr = strtok(NULL" ");
    }
    for(int i = 0; i < 10; i++)
    {
        if(sArr[i] != NULL)
            printf("%s\n", sArr[i]);
    }
    return 0;   
}
cs

while 반복문 안에서는 자른 문자열의 메모리 주소를 배열에 저장하고, 배열의 인덱스를 증가시킨다.

ptr에 저장된 메모리 주소가 바뀌기 전에 다른 곳에 보관하면 자른 문자열을 나중에도 계속 사용할 수 있다. 이후 for 반복문에서 배열을 출력하였다.

*45.5 퀴즈 *

정답은 d 이다.

정답은 a, c이다. 기준 문자열은 여러 개 지정할 수 있으며 잘린 문자열의 포인터를 반환한다.

정답은 b 이다.

45.6 연습문제 : 문자열 자르기

정답은 다음 코드와 같다.

1. char *ptr = strtok(s1. " ");

2. while(ptr != NULL)

3. ptr = strtok(NULL, " ");

45.7 심사문제 : 문자열 자르기

정답은 다음 코드와 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <stdio.h>
#include <string.h>
 
int main()
{
    char s[61];
    scanf("%s", s);
    
    char *ptr = strtok(s, ".");
    
    while(ptr != NULL)
    {
        printf("%s\n", ptr);
        ptr = strtok(NULL".");
    }
    
    return 0;
}
cs

45.8 심사문제 : 특정 단어 개수 세기

정답은 다음 코드와 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <stdio.h>
#include <string.h>
 
int main()
{
    int cnt = 0;
    char s[1001];
    scanf("%[^\n]s", s);
    
    char *ptr = strtok(s, " .,");
    
    while(ptr != NULL)
    {
        if(strcmp(ptr, "the"== 0)
        {
            cnt++;
        }
        ptr = strtok(NULL" .,");
    }
    
    printf("%d\n", cnt);
    return 0;
}
cs

공백, 점, 쉼표를 기준으로 문자열을 잘랐고 strcmp 함수를 이용해 자른 문자열이 the 이면 cnt 변수에 1씩 누적하고, 마지막에 cnt 변수를 출력하였다.

Unit 46. 문자열과 숫자를 서로 변환하기

46.1 문자열을 정수로 변환하기

atoi 함수를 사용하면 10진법으로 표기된 문자열을 정수로 바꿀 수 있다. stdlib.h 헤더파일에 선언되어 있다.

1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h>
#include <stdlib.h>
 
int main()
{
    char *s1 = "283";
    int num1;
    
    num1 = atoi(s1);
    printf("%d\n", num1);
    return 0;
}
cs

atoi 함수에 문자열을 넣으면 정수가 반환된다. 문자열 정수로 되어 있어야 하며 알파벳, 특수문자 등이 포함되면 해당 문자부터는 변환하지 않는다. 또한 처음부터 숫자가 아니면 0으로 반환된다. 다음은 정수에 영문자, 특수문자가 섞여 있을 때 반환 예 이다.

46.2 특정 진법으로 표기된 문자열을 정수로 변환하기

strtol 함수를 사용하여 16진법으로 표기된 문자열을 정수로 바꿀 수 있다. strtol(문자열, 끝포인터, 진법)의 형태로 사용한다. stdlib.h 헤더파일에 선언되어 있다.

1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h>
#include <stdlib.h>
 
int main()
{
    char *s1 = "0xaf10";
    int num1;
 
    num1 = strtol(s1, NULL16);
    printf("%x %d\n", num1, num1);
    return 0;
}
cs

변환할 문자열을 저장한 배열을 넣어주고 16을 지정하면 16진법으로 표기된 문자열을 정수로 변환할 수 있다. 10을 지정하면 10진법으로 표기된 문자열을 정수로 변환할 수 있다.

두 번째 인수는 여러개의 정수로 된 문자열을 변환할 때 사용한다. 여러개의 정수로된 문자열을 각각 변환하는 것은 다음과 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>
#include <stdlib.h>
 
int main()
{
    char *s1 = "0xaf10 42 0x27C 9952";
    int num1;
    int num2;
    int num3;
    int num4;
    char *end;
 
    num1 = strtol(s1, &end16);
    num2 = strtol(end&end10);
    num3 = strtol(end&end16);
    num4 = strtol(endNULL10);
 
    printf("%x\n%d\n%X\n%d\n", num1,num2,num3,num4);
    return 0;
}
cs

s1에는 16진법으로 표기된 숫자 2개와 10진법으로 표기된 숫자 2개사 있다. 두 번째 인수로 끝 포인터를 선언하여 메모리 주소를 넣으면 strtol 함수가 실행된 뒤에는 끝 포인터가 " 42 0x27C 9952" 처럼 이전 숫자의 끝 부분부터 시작하게 된다.

두번째 부터는 문자열에 end를 넣어 이전 숫자의 끝 부분부터 변환하면 된다. 이 과정을 그림으로 확인하면 다음과 같다.

46.3 문자열을 실수로 변환하기

atof 함수를 사용하여 문자열을 실수로 바꿀 수 있다. stdlib.h 헤더 파일에 선언되어 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>
#include <stdlib.h>
 
int main()
{
    char *s1 = "35.212345";
    float num1;
 
    num1 = atof(s1);
 
    printf("%f\n", num1);
    return 0;
}
cs

atof 함수에 문자열을 넣으면 실수가 반환된다. 문자열은 실수로 되어있어야 하며 알파벳 영문자, 특수문자가 포함되면 해당 문자부터는 변환을 하지 않는다. 또한 처음부터 숫자가 아니면 0으로 변환된다.

다음과 같이 알파벳 e를 사용한 지수 표기법으로 된 문자열도 실수로 바꿀 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>
#include <stdlib.h>
 
int main()
{
    char *s1 = "3.e5";
    float num1;
 
    num1 = atof(s1);
 
    printf("%e %f\n", num1, num1);
    return 0;
}
cs

strtof 함수를 사용하여 여러개의 실수로 된 문자열을 실수로 바꿀수 있다. stdlib.h 헤더파일에 선언되어 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <stdio.h>
#include <stdlib.h>   
 
int main()
{
    char *s1 = "35.283672 3.e5 9.281772 7.e-5";    
    float num1;
    float num2;
    float num3;
    float num4;
    char *end;   
 
    num1 = strtof(s1, &end);     
    num2 = strtof(end&end);    
    num3 = strtof(end&end);   
    num4 = strtof(endNULL);    
 
    printf("%f\n", num1);   
    printf("%e\n", num2);    
    printf("%f\n", num3);  
    printf("%e\n", num4);    
    return 0;
}
cs

처음에는 s1을 넣어 문자열을 실수로 변환하고, 끝 포인터는 &end처럼 메모리 주소를 넣어 strtof함수가 실행된 후 끝 포인터가 이전 숫자의 끝 부분부터 시작하게 했다. 두 번째 부터는 end를 넣어 이전 숫자의 끝 부분버터 변환한다. double 형 실수로 변환하는 strtod 함수도 있다.

46.4 정수를 문자열로 변환하기

sprintf 함수를 사용하여 정수를 문자열로 변환할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
#include <stdio.h>
 
int main()
{
    char s1[10];
    int num1 = 283;
 
    sprintf(s1, "%d", num1);
    printf("%s", s1);
    return 0;
}
cs

변환한 문자열을 저장할 배열을 선언하고, sprintf 함수에 서식지정자로 %d를 설정한 뒤 정수를 문자열로 저장하였다. atoi를 반대로 변환하는 itoa 같은 함수도 있지만 C 표준 함수는 아니다.

다음과 같이 16진법으로 표기된 문자열로 변환하려면 서식지정자 %x를 사용하면 된다.

1
2
3
4
5
6
7
8
9
10
11
#include <stdio.h>
 
int main()
{
    char s1[10];
    int num1 = 283;
 
    sprintf(s1, "0x%x", num1);
    printf("%s\n", s1);
    return 0;
}
cs

sprintf 함수에서 서식지정자로 %x를 사용하면 16진법으로 표기된 문자열로 변환할 수 있다. 16진수라는 것을 명확하게 나타내기 위해 앞에 0x를 붙여준 것이다. 서식지정자를 %X로 지정하면 16진수 알파벳 부분이 대문자로 저장된다.

46.5 실수를 문자열로 변환하기

실수를 문자열로 변환할 때도 sprintf 함수를 사용한다. 다음과 같이 서식지정자로 %f를 지정하면 된다. 또한 %e를 지정하여 지수 표기법으로 된 문자열로 변환할 수도 있다.

sprintf(s1, "%f", num1);
sprintf(s1, "%e", num1);

변환된 문자열이 길어질 수 있기 때문에 배열의 크기 또는 동적메모리로 할당한 크기를 잘 확인해야 한다.

46.6 퀴즈

정답은 b이다.

정답은 b이다.

정답은 c 이다.

정답은 e 이다.

정답은 c, g 이다.

정답은 b이다.

46.7 연습문제: 문자열을 10진 정수로 변환하기

정답은 다음 코드와 같다.

1. #include <stdlib.h>

2. num1 = atoi(s1);

46.8 연습문제 : 문자열을 16진 정수로 변환하기

정답은 다음 코드와 같다.

1. num1 = strtol(s1, NULL, 16)

2. "0x%X\n"

대문자로 출력하므로 서식지정자 %X를 사용한다.

46.9 연습문제 : 문자열을 실수로 변환하기

정답은 다음과 같다.

1. "97.527824"

2. num1

46.10 연습문제: 여러개의 실수로 된 문자열을 실수로 변환하기

정답은 다음 코드와 같다.

1. num1 = strtof(s1, &end);

2. num2 = strtof(end, NULL);

46.11 연습문제 : 숫자를 문자열로 변환하기

정답은 다음 코드와 같다.

1. char s1[30];

2. sprintf(s1, "%f 0x%x", num1, num2);

46.12 심사문제 : 문자열을 정수와 실수로 변환하기

정답은 다음 코드와 같다.

num1 = strtol(s1, &end, 16);
num2 = strtol(end, &end, 10);
num3 = strtof(end, NULL);

46.3 심사문제 : 정수와 실수를 문자열로 변환하기

정답은 다음 코드와 같다.

sprintf(s1, "%d", num1);
sprintf(s2, "%f", num2);

Unit 47. 회문 판별과 N-gram 만들기

회문은 유전자 염기 서열 분석에서 많이 쓰이고, N-gram은 빅데이터 분석, 검색 엔진에서 많이 쓰인다.

47.1 회문 판별

회문은 level, SOS 등과 같이 순서를 거꾸로 읽어도 제대로 읽은 것과 같은 단어와 문장을 말한다.

회문인지 판별하려면 첫번째 글자와 마지막 글자가 같고, 안쪽으로 한 글자씩 좁혔을 때 글자가 서로 같으면 회문이다.

다음은 문자열을 배열에 넣은뒤 반복문으로 각 글자를 검사한 것이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
 
int main()
{
    char word[30];
    int length;
    bool isPalindrome = true;
 
    printf("단어를 입력하세요 : ");
    scanf("%s", word);
 
    length = strlen(word);
 
    for(int i = 0; i < length / 2; i++)
    {
        if(word[i] != word[length - 1 -i])
        {
            isPalindrome = false;
            break;
        }
    }
 
    printf("%d\n", isPalindrome);
    return 0;
}
cs

회문일 경우 1이 출력되고, 회문이 아닐 경우 0이 출력된다.

회문 판별에서 가장 중요한 것은 문자열의 길이이다.

문자열 길이의 절반만큼 반복하며 왼쪽 문자와 오른쪽 문자들을 검색한다. 반복문 안에서 왼쪽문자와 오른쪽 문자를 비교하며 문자가 다르면 isPalindrome 에 false를 넣고 반복문을 끝낸다. 문자열의 마지막 문자는 word[length-1] 이므로 인덱스를 i 만큼 빼주면 오른쪽에서 왼쪽으로 진행할 수 있다.

47.2 N-gram 만들기

N-gram은 문자열에서 N개의 연속된 요소를 추출하는 방법이다. Hello의 경우 2-gram으로 추출하면 다음과 같다.

문자열의 처음부터 끝까지 한 글자씩 이동하며 2 글자씩 추출한다.

#include <stdio.h>
#include <string.h>

int main()
{
    char text[30] = "Hello";
    int length;

    length = strlen(text);

    for(int i = 0; i < length - 1; i++)
    {
        printf("%c%c\n", text[i], text[i+1]);
    }

    return 0;
}

2-gram이므로 문자열의 끝에서 한 글자 앞까지만 반복하며 현재 문자와 그 다음문자 두 글자씩 출력한다.

만약 3-gram이라면 조건식은 i < length -2가 될 것이고, 문자열 끝에서 두 글자 앞까지 반복하면 된다.

단어 단위 N-gram도 있다. 다음은 공백을 기준으로 구분하여 단어단위 2-gram을 출력하는 코드이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <stdio.h>
#include <string.h>
 
int main()
{
    char text[100= "this is c language";
    char *tokens[30= {NULL, };
 
    int cnt = 0;
    char *ptr = strtok(text, " ");
 
    while (ptr != NULL)
    {
        tokens[cnt] = ptr;
        cnt++;
        ptr = strtok(NULL" ");
    }
 
    for(int i = 0; i < cnt -1; i++)
    {
        printf("%s %s\n", tokens[i], tokens[i+1]);
    }
 
    return 0;
}
cs

strtok 함수로 문자열을 자른 뒤 각 단어들을 배열에 넣고, 배열의 마지막에서 요소 한 개 앞까지만 반복하면서 현재 문자열과 그 다음 문자열을 출력하면 된다.

47.3 연습문제 : 정수 회문 판별하기

정답은 다음 코드와 같다.

1. sprintf(text, "%lld", num1)

2. begin++;

3. end--;

입력값이 정수 이므로 sprintf 함수로 문자열로 변환하여 회문을 판별한다.

47.4 연습문제 : 4-gram 만들기

정답은 다음 코드와 같다.

1. length < n

2. i < length - (n-1)

3. j < n

문자열을 반복할 때는 배열의 범위를 벗어나지 않도록 i < length(n-1)로 조건식을 사용했다.

47.5 심사문제 : 공백이 포함된 회문 판별

정답은 다음 코드와 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
 
int main()
{
    char str[31];
    int length;
    bool isP = true;
    
    scanf("%[^\n]s", str);
    length = strlen(str);
    
    for(int i = 0, j = length-1; i < j; i++, j--)
    {
        while(str[i] == ' ')
            i++;
        while(str[j] == ' ')
            j--;
        if(str[i] != str[j])
        {
            isP = false;
            break;
        }
    }
    printf("%d\n", isP);
    return 0;
}
cs

반복문에서 문자열의 처음부터, 끝에서부터 반복시키며 공백이 있을 경우 한 칸씩 증가, 감소 시켜서 공백을 지우고, 문자열 앞에서 부터 온 문자와 뒤에서 부터 온 문자가 다르면 회문을 거짓으로 하고, 반복문을 끝낸다.

47.6 심사문제 : N-gram 만들기

정답은 다음 코드와 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <stdio.h>
#include <string.h>
 
int main()
{
    char text[11];
    int n;
    scanf("%d %s"&n, text);
    int length = strlen(text);
    
    if(length < n)
    {
        printf("wrong\n");
    }
    else
    {
        for(int i = 0; i < length - (n-1); i++ )
        {
            for(int j = 0; j < n; j++)
            {
                printf("%c", text[i+j]);
            }
            printf("\n");
        }
    } 
    
    return 0;
}
cs

2중 for문의 안쪽 반복문은 입력한 숫자만큼 반복하므로 입력한 숫자만큼의 문자를 출력할 수 있고, 바깥쪽 반복문으로 1씩 증가시키면서 문자열에서 문자를 한 칸씩 앞으로 가도록 했다.

 

 

이번 페이지 요약

코딩도장 C언어 unit 41~44

1. 문자열 관련 함수(길이, 복사, 문자열 만들기, 검색)

 

Unit 41. 문자열의 길이를 구하고 비교하기

41.1 문자열의 길이 구하기

문자열의 길이는 strlen 함수로 구할 수 있으며 string.h 헤더 파일에 선언되어 있다. 문자열 포인터와 문자 배열의 길이 모두 구할 수 있으며 사용법은 다음과 같다.

1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h>
#include <string.h>
 
int main()
{
    char *s1 = "Hello";
    char s2[10= "Hello";
 
    printf("%d\n", strlen(s1));
    printf("%d\n", strlen(s2));
    return 0;
}
cs

strlen에 문자열 포인터나 문자 배열을 넣으면 문자열의 길이가 반환된다 Hello는 5글자 이기 때문에 5가 출력되었으며 NULL 부분은 포함하지 않는다. 배열의 경우 배열의 크기는 관계없이 문자열의 길이만 구한다. 위 코드의 경우도 배열의 크기는 10이지만 출력값은 문자열의 길이인 5가 나왔다.

41.2 문자열 비교하기

strcmp 함수를 사용하면 두 문자열이 같은 지 비교할 수 있다. string.h 헤더 파일에 선언되어 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>
#include <string.h>
 
int main()
{
    char *s1 = "Hello";
    char s2[10= "Hello";
    
    int ret = strcmp(s1, s2);
 
    printf("%d\n", ret);
    return 0;
}
cs

strcmp 함수에 비교할 문자열을 넣으면 결과를 정수로 반환하며 반환 되는 값의 종류는 다음과 같다.

  • -1: ASCII 코드 기준으로 문자열2(s2)가 클 때
  • 0: ASCII 코드 기준으로 두 문자열이 같을 때
  • 1: ASCII 코드 기준으로 문자열1(s1)이 클 때

배열 문자열, 문자열 포인터 등 문자열의 저장 방식은 문자열 비교에 영향을 주지 않는다.

strcmp 함수는 문자열에서 첫 번째 문자부터 차례대로 비교하며 비교 기준은 각 문자의 ASCII 코드이다.

1
2
3
4
5
6
7
8
9
10
#include <stdio.h>
#include <string.h>
 
int main()
{
    printf("%d\n", strcmp("aaa""aaa"));
    printf("%d\n", strcmp("aab""aaa"));
    printf("%d\n", strcmp("aab""aac"));
    return 0;
}
cs

aaa는 아스키 코드로 97 97 97 이고, aab 는 97 97 98 이기 때문에 aab 가 더 크다. 또한 aac는 97 97 99 이므로 aac가 aab 보다 크다. 앞의 것이 크면 1, 뒤의 것이 크면 -1을 반환한다. 이것은 윈도우 기준이고, 리눅스나 OS X에서는 코드값의 차이를 반환한다. 따라서 입력받는 두 문자열의 크기를 비교할 땐 윈도우에서는 switch문을 사용하면 되고, 리눅스나 OS X 에서는 if 문을 사용해야 한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <stdio.h>
#include <string.h>
 
int main()
{
    char s1[20];
    char s2[20];
 
    printf("문자열 두 개를 입력하세요 : ");
    scanf("%s %s", s1, s2);
 
    int ret = strcmp(s1, s2);
    printf("반환값 : %d\n", ret);
 
    if(ret == 0)
    {
        printf("같음\n");
    }
    else if(ret > 0)
    {
        printf("%s가 %s보다 큼\n", s2, s1);
    }
    else if(ret < 0)
    {
        printf("%s가 %s보다 큼\n", s1, s2);
    }
 
    return 0;
}
cs

리눅스나 OS X에서 strcmp는 문자열1에서 문자열 2의 아스키 코드값을 뺏을 때 양수가 나오면 1이 크고, 음수가 나오면 2가 크다. 두 문자열이 같으면 0이 나온다.

41.3 퀴즈

정답은 c 이다.

정답은 c,d,f 이다.

41.4 연습문제 : 문자열 길이 구하기

정답은 strlen(s1) 이다.

41.5 연습문제 : 문자열 비교하기

정답은 char *s1 = "Pachelbel Canon"; 이다.

41.6 심사문제 : 문자열 길이 구하기

정답은 다음 코드와 같다.

1
2
3
4
5
6
7
8
9
10
#include <stdio.h>
#include <string.h>
 
int main()
{
    char s[30];
    scanf("%s", s);
    printf("%d", strlen(s));
    return 0;
}
cs

41.7 심사문제 : 문자열 비교하기

정답은 다음 코드와 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>
#include <string.h>
 
int main()
{
    char s1[30];
    char s2[30];
    
    scanf("%s %s", s1, s2);
    
    printf("%d", strcmp(s1, s2));
    return 0;
}
cs

Unit 42. 문자열을 복사하고 붙이기

42.1 문자열 복사하기

strcpy 함수를 이용하여 문자열을 다른 배열이나 포인터(메모리)로 복사할 수 있다. string.h 헤더 파일에 선언되어 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#define _CRT_SECURE_NO_WARNINGS    // strcpy 보안 경고로 인한 컴파일 에러 방지
#include <stdio.h>
#include <string.h>
 
int main()
{
    char s1[10= "Hello";
    char s2[10];
 
    strcpy(s2, s1);
 
    printf("%s\n", s2);
    return 0;
}
cs

배열 s2에는 아무것도 들어있지 않았지만 strcpy 함수로 s1의 문자열이 복사되어 s2에도 저장되었다.

복사된 결과가 저장된 배열의 크기는 반드시 NULL까지 들어갈 수 있는 크기여야 한다. 따라서 Hello 라는 문자열이 복사되려면 NULL문자까지 크기는 최소 6 이상이 되어야 한다.

문자열 포인터는 복사할 공간도 없으며 읽기만 가능하기 때문에 문자열을 복사할 수 없다. 문자열 포인터에 문자열을 복사하려면 다음과 같이 메모리를 할당한 뒤 복사해야 한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#define _CRT_SECURE_NO_WARNINGS    // strcpy 보안 경고로 인한 컴파일 에러 방지
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
 
int main()
{
    char *s1 = "Hello";
    char *s2 = malloc(sizeof(char* 10);
 
    strcpy(s2, s1);
 
    printf("%s\n", s2);
    free(s2);
    return 0;
}
cs

s2에 char 10개 크기만큼 동적 메모리를 할당하여 문자열을 복사하고, free로 할당한 메모리를 해제하였다.

42.2 문자열 붙이기

문자열은 strcat 함수를 이용하여 서로 붙일 수 있다. string.h 헤더파일에 선언되어 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#define _CRT_SECURE_NO_WARNINGS    // strcat 보안 경고로 인한 컴파일 에러 방지
#include <stdio.h>
#include <string.h>
 
int main()
{
    char s1[10= "world";
    char s2[20= "Hello";
 
    strcat(s2, s1);
 
    printf("%s\n", s2);
    return 0;
}
cs

strcat함수에 최종 결과가 나올 문자열과 붙일 문자열을 넣는다. 위의 경우 s2 뒤에 s1 이 붙어서 Helloworld가 나온다.

문자열을 붙이더라도 배열이 모자라지 않도록 최종 결과가 나올 문자열의 배열 크기를 넉넉하게 만들어야 한다.

문자열 포인터에 사용하려면 붙일 문자열 포인터에 동적 메모리를 할당해 줘야 한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#define _CRT_SECURE_NO_WARNINGS  
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
 
int main()
{
    char *s1 = "world";
    char *s2 = malloc(sizeof(char* 20);
 
    strcpy(s2, "Hello");
    strcat(s2, s1);
 
    printf("%s\n", s2);
    free(s2);
    return 0;
}
cs

최종 결과가 나올 문자열 포인터에 char 20개 크기만큼 동적 메모리를 할당하고, 메모리가 할당된 문자열 포인터는 문자열을 직접 할당할 수 없으므로 strcpy 함수를 이용하여 "Hello"를 복사하고, s2 뒤에 s1을 붙인다. 문자열 사용이 끝났으면 free로 동적 메모리를 해제한다.

42.3 배열 형태의 문자열을 문자열 포인터에 복사하기

다음과 같이 배열 형태의 문자열을 문자열 포인터로 복사할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#define _CRT_SECURE_NO_WARNINGS  
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
 
int main()
{
    char s1[10= "Hello";
    char *s2 = malloc(sizeof(char* 10);
 
    strcpy(s2, s1);
 
    printf("%s\n", s2);
    free(s2);
    return 0;
}
cs

malloc 함수로 s2에 메모리를 복사할 문자열이 충분히 들어갈 수 있을 정도로 할당하고, strcpy로 복사하면 된다.

42.4 배열 형태의 문자열을 문자열 포인터에 붙이기

다음과 같이 배열 문자열을 문자열 포인터에 붙일 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#define _CRT_SECURE_NO_WARNINGS   
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
 
int main()
{
    char s1[10= "world";
    char *s2 = malloc(sizeof(char* 20);
 
    strcpy(s2, "Hello");
    strcat(s2, s1);
 
    printf("%s\n", s2);
    free(s2);
    return 0;
}
cs

문자열 포인터 s2 뒤에 문자열을 붙여야 하므로 메모리가 부족하지 않도록 넉넉히 할당하고, strcpy함수로 s2에 "Hello"를 복사한다. 이후 s2 뒤에 strcat 함수로 문자열 배열 s1을 붙이면 된다. 문자열 사용이 끝나면 free로 메모리를 해제해줘야 한다.

42.5 퀴즈

정답은 NULL문자까지 6이다.

정답은 NULL문자까지 11바이트 이다.

정답은 c 이다.

42.6 연습문제 : 문자열 포인터를 배열에 복사하기

문자열을 복사하는 것이므로 strcpy 함수를 사용하여 정답은 strcpy(s2, s1); 이다.

42.7 연습문제 : 문자열 포인터를 동적 메모리에 복사하기

정답은 다음 코드와 같다.

1. malloc(sizeof(char) * 20);

2. strcpy(s2, s1);

42.8 연습문제 : 문자 배열을 붙이기

정답은 strcat(s2, s1); 이다.

42.9 연습문제 : 문자열 리터럴과 동적 메모리 붙이기

정답은 다음 코드와 같다.

1. strcpy(s2, "Alice in");

2. strcat(s2, s1);

42.10 심사문제 : 문자 배열 복사하기

정답은 다음 코드와 같다.

scanf("%s", s1);
strcpy(s2, s1);

s1에는 값을 입력받고, s2에는 s1의 값을 복사해서 가져왔다.

42.11 심사문제 : 두 문자열 붙이기

scanf("%s", s1);
strcat(s1, "th");

문자열을 입력받아 strcat 함수로 문자열 끝에 th를 붙였다.

Unit 43. 문자열 만들기

43.1 서식을 지정하여 배열 형태로 문자열 만들기

sprintf 함수를 사용하면 서식을 지정하여 문자열을 만들 수 있다. sprintf(배열, 서식, 값);의 형태로 사용하며 서식에 값이 들어간 것을 배열에 저장할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
#define _CRT_SECURE_NO_WARNINGS    // sprintf 보안 경고로 인한 컴파일 에러 방지
#include <stdio.h>
 
int main()
{
    char s1[20];
 
    sprintf(s1, "Hello %s""world!");
 
    printf("%s\n", s1);
    return 0;
}
cs

sprintf 함수에 문자열을 저장할 배열과 문자열을 만들 서식, 문자열을 만들 값(문자열)을 순서대로 넣어 %s부분이 "word!"로 바뀌게 된다. 위 코드에서 s1과 같이 문자열을 저장할 빈 배열을 버퍼(buffer)라고 부른다. 서식지정자로 C언어의 다양한 자료형들을 문자열로 만들 수 있다.

1
2
3
4
5
6
7
8
9
10
#include <stdio.h>
 
int main()
{
    char s1[30];
 
    sprintf(s1, "%c %d %f %e"'a'103.2f, 1.123456e-21f);
    printf("%s\n", s1);
    return 0;
}
cs

sprintf()함수에 %c, %d, %f, %e를 지정하여 각 서식지정자에 맞는 문자, 정수, 소수점 표기 실수, 지수 표기법 실수를 넣었다. 위 코드에서는 함수에 값을 바로 넣었지만, 변수를 넣어도 된다.

43.2 서식을 지정하여 문자열 포인터에 문자열 만들기

문자열 포인터를 사용하려면 malloc 함수로 메모리를 할당한 뒤 sprintf함수로 문자열을 만들면 된다.

1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h>
#include <stdlib.h>
 
int main()
{
    char *s1 = malloc(sizeof(char* 20);
    sprintf(s1, "Hello, %s""world!");
    
    printf("%s\n", s1);
    free(s1);
    return 0;
}
cs

char 20개 크기의 동적 메모리를 할당하고 sprintf함수에 서식을 지정하여 문자열을 만들면 된다. 배열과 마찬가지로 문자열을 생성할 메모리 공간은 버퍼이다. 문자열 사용이 끝나면 free 함수 동적 할당한 메모리를 해제 해야 한다.

다음과 같이 다양한 종류의 서식지정자를 사용하여 다양한 자료형도 문자열로 만들 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h>
#include <stdlib.h>
 
int main()
{
    char *s1 = malloc(sizeof(char* 30);
    sprintf(s1, "%c %d %f %e"'a'103.1f, 1.23e-23f);
    
    printf("%s\n", s1);
    free(s1);
    return 0;
}
cs

char 30개 크기만큼 동적으로 메모리를 할당하고, sprintf함수에 서식을 지정하여 문자, 정수, 소수점 표기 실수, 지수 표기 실수를 문자열로 만들었다. 문자열 사용이 끝나면 free함수로 할당한 메모리를 해제해야 한다.

43.3 퀴즈

정답은 d이다.

정답은 "%c %d" 이다.

정답은 버퍼 이다.

43.4 연습문제 : 숫자와 문자열을 조합하여 문자열 만들기

숫자와 문자열이 순서대로 들어가므로 정답은 9, "Symphony" 이다.

43.5 연습문제 : 서식에 맞게 문자열 만들기

정수 3개 ,문자 1개 정수 1개가 순서대로 들어가므로 정답은 "%d %d %d %c %d" 이다.

43.6 심사문제 : 서수 줄임말 문자열 만들기

정답은 다음코드와 같다.

scanf("%d %s", &number, name);

if(number == 1)
    sprintf(result, "%dst %s", number, name);
else if(number == 2)
    sprintf(result, "%dnd %s", number, name);
else if(number == 3)
    sprintf(result, "%drd %s", number, name);
else
    sprintf(result, "%dth %s", number, name);

숫자에 따라 숫자 뒤에 붙는것이 다르기 때문에 if 조건문을 사용하여 조건에 맞게 출력하도록 하였다.

Unit 44. 문자열 검색하기

44.1 문자열 안에서 문자로 검색하기

strchr 함수를 사용하여 문자열에서 특정 문자로 검색할 수 있다. string.h 헤더 파일에 선언되어 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <stdio.h>
#include <string.h>
 
int main()
{
    char s1[30= "A Garden Diary";
    char *ptr = strchr(s1, 'a');
 
    while(ptr != NULL)
    {
        printf("%s\n", ptr);
        ptr = strchr(ptr + 1'a');
    }
    return 0;
}
cs

strchr 함수에 문자열과 검색할 문자를 넣어주면 해당 문자로 시작하는 문자열의 위치(포인터)를 반환한다.

'a' 로 시작하는 부분을 더 찾기 위해 while 반복문을 사용하여 검색된 문자여릐 포인터에 1을 더해 다음부터 또 검색해서 찾는다. NULL이 나오면 검색할 문자열이 없는 것이므로 반복을 끝낸다.

'a' 가 들어간 문자를 처음 찾으면 arden Diary를 찾을 수 있다. 포인터에 1을 더하면 rden Diary 가 되므로 다음번 a를 찾을 수 있게 된다.

strchr 함수는 대소문자를 구분하므로 A는 찾지 않고 넘어간다.

44.2 문자열의 오른쪽 끝부터 문자로 검색하기

strrchr 함수는 문자열의 끝에서 부터 문자를 검색한다. string.h 헤더 파일에 선언되어 있다.

1
2
3
4
5
6
7
8
9
10
11
#include <stdio.h>
#include <string.h>
 
int main()
{
    char s1[30= "A Garden Diary";
    char *ptr = strrchr(s1, 'a');
 
    printf("%s\n", ptr);
    return 0;
}
cs

strrchr 함수에 문자열과 검색할 문자를 넣으면 문자열 끝에서 부터 검색하여 'a'로 시작하는 문자열의 포인터를 반환한다. strrchr함수도 대소문자를 구분한다.

44.3 문자열 안에서 문자열로 검색하기

strstr 함수는 문자열 안에서 문자열을 검색할 수 있는 함수이며 string.h 헤더 파일에 선언되어 있다.

워드나 메모장에서 검색할 때 단어로 검색하는 경우가 많은것처럼 프로그램을 만들때도 문자열 찾는 함수를 더 많이 사용한다.

1
2
3
4
5
6
7
8
9
10
11
#include <stdio.h>
#include <string.h>
 
int main()
{
    char s1[30= "A Garden Diary";
    char *ptr = strstr(s1, "den");
 
    printf("%s\n", ptr);
    return 0;
}
cs

strstr 함수에 검색할 문자열을 넣으면 해당 문자열로 시작하는 문자열의 위치(포인터)를 반환한다. 검색한 문자만 나오지 않고, 검색한 문자를 포함하여 뒤에 오는 모든 문자열이 나온다. strstr 함수도 대소문자를 구분하며 다음과 같이 반복문을 사용하여 문자열을 계속 검색할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <stdio.h>
#include <string.h>
 
int main()
{
    char s1[100= "A Garden Diary A Garden Diary A Garden Diary";
    char *ptr = strstr(s1, "den");
 
    while(ptr != NULL)
    {
        printf("%s\n", ptr);
        ptr = strstr(ptr+1"den");
    }
    return 0;
}
cs

44.4 퀴즈

정답은 c, e 이다.

정답은 b 이다.

44.5 연습문제 : 문자열 안에서 문자로 검색하기

정답은 다음 코드와 같다.

1. char *ptr = strchr(s1, 'n');

2. while(ptr != NULL)

3. ptr = strchr(ptr + 1, 'n');

44.6 연습문제 : 문자열의 오른쪽 끝 부터 문자로 검색하기

정답은 char *ptr = strrchr(s1, 'i'); 이다. 마지막의 ince만 출력하려면 오른쪽 부터 검색하는 strrchr 함수를 사용해야 한다.

44.7 심사문제 : 공백 개수 세기

정답은 다음 코드와 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <stdio.h>
#include <string.h>
 
int main()
{
    char str[1001];
    scanf("%[^\n]s", str);
    char *ptr = strchr(str, ' ');
    int cnt = 0;
    
    while(ptr != NULL)
    {
        cnt++;
        ptr = strchr(ptr + 1' ');
    }
    printf("%d\n", cnt);
    return 0;
}
cs

정수형 변수를 선언한 후 공백을 찾을 때 마다 1씩 누적하고 반복문이 끝난 뒤 누적한 값을 출력하였다.

 

+ Recent posts