C

C기초 플러스 chap.11 문자열과 문자열 함수

Barbarian developer 2024. 8. 12.

고생들 많았다!! 포인터한테 탈탈 털리고 여기 까지 온 바바리안들 모두 칭찬한다!

 

근데 아직 우리 더 맞아야 한다. 중첩 포인터랑 포인터의 연산 같은 깊은 곳까지 공략하지 못한채로 넘어와 버렸다!

 

하지만 걱정마라!! 포기하지 않는다!! 맞아도 일어나라!!

 

11장 겁나 많다!! 나중에 천천히 포스팅 한다!! 이거 다하면 죽는다 나,, 아파서 병원갔다왔따,,

 

계속 두들기면 언젠간 무너진다! 모르는건 넘겨라! 우리의 목표는 1회독 완주다!!

 

멈추지말라!! 자신의 무능함에 대해 분노하라!!

 

베헤헤에에에엘라아아아아아아아!!


문자열 표현과 문자열 입출력

puts 함수

  • 형식 : puts(변수);
  • 기능 : 문자열을 화면에 출력한다위와 같은 코드를 실행 했을 때,
#include<stdio.h>

int main(void)
{
char s1[ ]="Computer";
char s2[ ]="Science";
puts(s1);				// 변수 s1이 갖고 있는 문자열을 출력
puts(s2);				// 변수 s2가 갖고 있는 문자열을 출력
printf("%s", s1);
printf("%s", s2);

return 0;
}

//출력 결과
//Computer
//Science

puts () 함수는 자동으로 \n이 입력 된 것처럼 한 줄 띄어져서 출력이 되나,
() 함수 \n이 없기 때문에 한 줄 띄어지지 않음.

//  strings1.c
#include <stdio.h>
#define MSG "I am a symbolic string constant." //상수 선언
#define MAXLENGTH 81 //상수 선언
int main(void)
{
    char words[MAXLENGTH] = "I am a string in an array."; //문자열 초기화
    const char * pt1 = "Something is pointing at me.";// 포인터형 변수 선언 및 대입
    puts("Here are some strings:");
    puts(MSG); //msg가 가지고 있는 문자열 출력
    puts(words); //words가 가지고 있는 문자열 출력
    puts(pt1); //pt1이 가지고 있는 문자열 출력
    words[8] = 'p';//words의 8번째 t를 p로 바꿈
    puts(words); //words의 문자열 출력


    return 0;
}
//출력 결과
//Here are some strings:
//I am a symbolic string constant.
//I am a string in an array.
//Something is pointing at me.
//I am a spring in an array.

포인터로서의 문자열들

/* strptr.c -- 포인터로서의 문자열들 */
#include <stdio.h>
int main(void)
{
    printf("%s, %p, %c\n", "We", "are", *"space farers");//문자열 출력, are의 주소 출력, c의 주소 값 출력(포인터는 항상 주소 첫번값 부터 출력한다.)
    
    return 0;
}

//출력 결과
//We, 0x5f6877e3e004, s

문자열의 주소들

//  addresses.c  -- 문자열의 주소들
#define MSG "I'm special." //상수 선언

#include <stdio.h>
int main()
{
    char ar[] = MSG; //문자열 초기화 후 MSG대입
    const char *pt = MSG; //포인터형 변수 선언
    printf("address of \"I'm special\": %p \n", "I'm special"); //I'm special의 주소 출력
    printf("              address ar: %p\n", ar); //ar의 주소 출력. ar은 문자열이기 때문에 MSG와 같지 않다.
    printf("              address pt: %p\n", pt); //pt의 주소 출력  pt와 MSG의 주소는 서로 같다.
    printf("          address of MSG: %p\n", MSG);//MSG의 주소 출력 pt와 MSG의 주소는 서로 같다.
    printf("address of \"I'm special\": %p \n", "I'm special");//I'm special의 주소 출력

    return 0;
}

//같은 값이라도 다른 형태의 열이라면, 저장하는 주소는 다르다.
//address of "I'm special": 0x5b6e62397015 
//              address ar: 0x7fff0801cc4b
//              address pt: 0x5b6e62397008
//          address of MSG: 0x5b6e62397008
//address of "I'm special": 0x5b6e62397015

포인터의 배열, 문자열의 배열

//  arrchar.c -- 포인터의 배열, 문자열의 배열
#include <stdio.h>
#define SLEN 40
#define LIM 5
int main(void)
{
    const char *mytalents[LIM] = { //포인터형 mytalents 선언 후 LIM 주소에 저장. 
        "Adding numbers swiftly",
        "Multiplying accurately", "Stashing data",
        "Following instructions to the letter",
        "Understanding the C language"
    }; //문자열을 선언할때는 중괄호를 열고 닫을 때 세미콜론
    char yourtalents[LIM][SLEN] = { //이중배열 (LIM*SLEN)
        "Walking in a straight line",
        "Sleeping", "Watching television",
        "Mailing letters", "Reading email"
    };
    int i; //인트형 i 선언
    
    puts("Let's compare talents.");
    printf ("%-36s  %-25s\n", "My Talents", "Your Talents");
    for (i = 0; i < LIM; i++) //i 초기화, i가 lim 미만일때, i+1 증가
        printf("%-36s  %-25s\n", mytalents[i], yourtalents[i]);
    printf("\nsizeof mytalents: %zd, sizeof yourtalents: %zd\n",
           sizeof(mytalents), sizeof(yourtalents)); //mytalents는 포인터형의 주소 크기, 포인터형은 운영체제 만큼의 바이트를 가지며, 5개의 배열을 가지기 때문에, 40바이트의 크기를 가진다.
                                                    //yourtalents의 크기는 [LIM]과 [SLEN]의 곱셈 이기 때문에 두 바이트의 크기를 곱하면 크기는 200바이트.
    return 0;
};

//결과 출력
//Let's compare talents.
//My Talents                            Your Talents             
//Adding numbers swiftly                Walking in a straight line
//Multiplying accurately                Sleeping                 
//Stashing data                         Watching television      
//Following instructions to the letter  Mailing letters          
//Understanding the C language          Reading email

 gets( ) 함수

  • 형식 : gets(변수);
  • 기능 : 문자열을 키보드로부터 입력 받는다 gets ()함수 로 문자열을 입력 받으면 공백" " 값도 입력됨.
#include <stdio.h>
#pragma warning(disable:4996)
void main() {
char s[50];						// 문자열 저장을 위해 배열명 s인 배열 선언
printf("문자열 입력?");
gets(s);						// gets()를 이용하여 문자열을 받아들임
printf("gets()로 문자열 입력= %s\n", s);
printf("\n문자열 입력?");
scanf("%s", s);					// scanf()를 이용하여 문자열을 받아들임
printf("scanf()로 문자열 입력= %s\n", s);
}

 

gets()과 puts()의 사용

/*  getsputs.c  -- gets()과 puts()의 사용*/
#include <stdio.h>
#define STLEN 81
int main(void)
{
    char words[STLEN];
     
    puts("문자열을 입력해 주세요.");//문자열 출력
    gets(words);  // words에 문자열 입력(저장)
    printf("당신의 문자열이 두 번 반복될 것입니다.:\n");//문자열 출력
    printf("%s\n", words);//words 출력
    puts(words);//words 출력
    puts("종료.");//문자열 출력
    
    return 0;
}

//출력 결과
//문자열을 입력해 주세요.
//warning: this program uses gets(), which is unsafe.
//나는 바바리안 개발킹이다!
//당신의 문자열이 두 번 반복될 것입니다.:
//나는 바바리안 개발킹이다!
//나는 바바리안 개발킹이다!
//종료.
  • 두번째 출력결과에서 warnig이라는 경고 메세지가 뜬다!
  • 이유는 gets()함수는 입력하는 행이 실제로 배열에 맞게 들어가는지 점검을 할 수 없기 때문이다!
  • 이 놈은 쓰기는 편리하지만, 배열이 어디서 시작하는지만 알고 원소가 몇개가 있는지 알지 못하는 바보다!
  • 그리하여 문자열이 너무 길게 되면 오버플로우가 일어난다는 경고!!
  • 때문에 gets()함수는 시스템 보안상 위험이 되고, 그로 인해 C11위원회로부터 표준에서 제외된 함수라고 한다!
  • 결론은 gets()는 쓰지말자!

gets()의 대안! fgets()와 fputs()

fgets()란?

fgets()함수는 읽을 문자들의 최대 개수를 지정함으로써 두 번째 전달인자를 취할 때 생길 수 있는 오버 플로우 문제를 해결한다.

fgets()함수는 파일 입출력용으로 설계된 것이기에, 키보드 입력에서는 gets()보다 약간 불편하다.

fgets()는 다음과 같은 세 가지 측면에서 gets()와 다르다.

  1. 읽을 문자들의 최대 갯수를 지정하기 위해 두 번 째 전달인자로 사용한다. 그 전달인자가 값 n이라면, fgets()는 n-1개까지 문자들을 읽거나 개행 문자가 나올 때 까지 읽는다. 둘 중 먼저 일어나는 것을 수행한다.
  2. fgets()는 개행 문자를 읽어 그 문자열에 저장한다. gets()는 읽고 그냥 버린다.
  3. 어느 파일을 읽을 것인지 지정하기 위해 세번째 전달인자를 사용한다. 키보드로 부터 읽으려면(standard input을 의미하는) stdin을 전달인자로 사용한다. 이 식별자는 stdio.h에 정의되어 있기 때문에, 다른 헤더파일을 불러오지 않아도 된다.

fputs()란?

fgets()함수가 문자열(입력 행에 꼭 맞는 것으로 추정되는)의 부분으로서, 개행을 포함하기 때문에 이 함수는 종종 puts()와 같이 일하는 fputs()와 짝을 이뤄 사용되는데, fputs() 함수는 자동으로 개행을 추가하지는 않는다. 어느  파일에 쓸지 가르키는 두 번째 전달인자를 사용한다. 컴퓨터 모니터의 경우 stdout(표준 출력용)을 전달인자로 사용하면 된다. 

/*  fgets1.c  -- fgets()과 fputs()의 사용 */
#include <stdio.h>
#define STLEN 14
int main(void)
{
    char words[STLEN];
    
    puts("문자열 입력하세요.");//문자열 출력 \n
    fgets(words, STLEN, stdin);//words에 14개 배열까지 입력받는다.(apple pie를 입렵하면 \n\0까지 저장할만큼 공간이 남는다.)
    printf("당신 문자열 두 번 (puts(), 그리고 나서 fputs()):\n");//문자열 프린트
    puts(words);//puts()는 문자열을 나타내 출력 자체 개행을 추가하므로, apple pie 다음에 빈 출력행을 만들어 낸다.
    fputs(words, stdout);//futs()는 개행을 추가하지 않기 때문에 빈 행을 만들지 않는다.
    puts("다른 문자열을 입력하세요.");//문자열 출력 \n
    fgets(words, STLEN, stdin);//strawberry shortcake는 크기한계를 초과하므로, fget()는 첫 문자 13개를 읽어 배열에 strawbarry sh\0을 추가한다.
    printf("당신의 문자열 두 번 (puts(), 그리고 나서 fputs()):\n");//문자열 출력 \n
    puts(words);//문자열 출력\n
    fputs(words, stdout);//fputs()함수는 \n을 추가하지 않으므로 마지막 put()값과 함께 나란히 출력된다.
    puts("종료.");
    
    return 0;
}
//여기서 다시한번 짚고 넘어가지만, 문자열을 대입할 때, 문자열의 마지막엔 항상'\0'이 들어가므로, 14개의 배열자리를 만들어도, 13자까지만 문자를 입력한다.
//출력 결과
//문자열 입력하세요.
// apple pie
//당신 문자열 두 번 (puts(), 그리고 나서 fputs()):
//apple pie

//apple pie
//다른 문자열을 입력하세요.
//strawberry shortcake
//당신의 문자열 두 번 (puts(), 그리고 나서 fputs()):
//strawberry sh
//strawberry sh종료.

fgets()와 fputs()의 활용 ( 문자열의 크기를 넘어서게 출력하는 법)

/*  fgets2.c  -- fgets()와 fputs()의 사용 */
#include <stdio.h>
#define STLEN 10 //상수 선언
int main(void)
{
    char words[STLEN]; //문자열 배열 크기 선언
    
    puts("문자열을 입력하세요. (탈출하려면빈행):"); //문자열 출력\n
    while (fgets(words, STLEN, stdin) != NULL && words[0] != '\n') //변수words에 STLEN의 상수 10만큼의 배열까지 입력받고, 입력받은 문자열이 null이 아닐때 무한 루프
        fputs(words, stdout);//words에 저장된 문자열 출력
    puts("종료.");
    
    return 0;
}
//STLEN이 10이지만, 프로그램은 그것보다 훨씬 길게 입력행을 처리한다.
//어떻게 가능하나면, fgets()에 입력받는 9개의 문자열과 \0을 넘어선 다른 문자열은 또 다시 9개의 문자열과 \0으로 버퍼에 저장되있는다.
//그 후, 그 버퍼에 남는 것이 마지막 우리가 입력한 enter의 개행 문자가 나올때까지 루프해서 출력한다.
//그러므로, 이런 형태의 fgets는 선언한 배열 크기 이상의 문자열도 이를 출력할 수 있는 것이다.
//출력 결과
//문자열을 입력하세요. (탈출하려면빈행):
//By the way, the ges() function
//By the way, the ges() function
//also retruns a null pointer if it.
//also retruns a null pointer if it.
//encounters end-of-file.
//encounters end-of-file.
//
//종료.

fgets()와 fputs()의 활용 ( 문자열을 넘어서는 문자를 처리하는 법)

/*  fgets3.c  -- fgets()사용 */
#include <stdio.h>
#define STLEN 10
int main(void)
{
    char words[STLEN]; //words문자열 선언 배열크기 STLEN
    int i; //인트형 선언
    
    puts("문자열을 입력하세요. (탈출하려면 빈행):"); //문자열 출력
    while (fgets(words, STLEN, stdin) != NULL //입력받은 words가 Null이 아니면서,
                          && words[0] != '\n') //words의 0의 배열이 \n이 아닐때 무한루프
    {
        i = 0; //i 초기화
        while (words[i] != '\n' && words[i] != '\0')//words[i]가 \n이 아니고, words[i]가 \0이 아닐때,
            i++; //i를 1 증가
        if (words[i] == '\n') //words[i] 가 \n라면
            words[i] = '\0'; //words[i]를 0으로 변경
        else  //words[i] == '\0'이어야 한다.
            while (getchar() != '\n') //입력값이 \n이 아니라면,
                continue; //다시 반복한다. 여기서 getchar는, 버퍼에 남아 있는 값이 \n이 나올때까지 모두 처리해버린다.
        puts(words); //words출력
    }
    puts("종료");
    return 0;
}

//출력결과
//This
//This
//program seems
//program s
//unwilling to accept long lones.
//unwilling
//But it desen't get stuck on long.
//But it de
//lines either.
//lines eit

 

 

댓글