게시판

검색 결과: 61

 

심사문제 해설을 꼼꼼하게 읽어주세요.

char *ptr = strtok(s1, " .,");와 같이 " .,"를 지정하여 공백, 점, 콤마를 기준으로 문자열을 자릅니다(공백만 지정해서 잘라도 되지만 the.나 the,와 같이 점이나 콤마로 끝나는 경우를 대비해 점과 콤마도 잘라줍니다). 


코드를 잘 보면 구분자 3개에 대해서 문자열을 자른다고 되어 있습니다.

char *ptr = strtok(s1, " .,");
공백을 잘 못 볼 수도 있어서 해설에 '공백, 점, 콤마'라고도 설명되어 있습니다.

제대로 실행되는 코드는 아닌 것 같습니다. 포럼 상단 오른쪽에 45.8로 검색하면 이전 질문과 답변을 참조할 수 있습니다.


, 도장_ 관리자님이 작성
 

char *sArr[10]={NULL, };


char ch;

이면 ch는 char로 선언하는 것입니다.

char *ch;

이면 ch는 pointer to char로 선언하는 것입니다.

char ch[];

이면 ch는 array of char로 선언하는 것입니다.


char *ch[];

이면 ch는 array of pointer to char로 선언하는 것입니다.

char *ch[10];

이면 ch는 array 10 of pointer to char로 선언하는 것입니다.

영어로 읽는 법입니다.


pointer to char를 10개 담을 수 있는 배열이라는 뜻입니다.

포럼 상단 고정에 있는 시각화 사이트를 이용해서 단계별로 실행하면서 이해하세요.

https://pythontutor.com/render.html#code=%23define%20_CRT_SECURE_NO_WARNINGS%20%20%20%20//%20strtok%20%EB%B3%B4%EC%95%88%20%EA%B2%BD%EA%B3%A0%EB%A1%9C%20%EC%9D%B8%ED%95%9C%20%EC%BB%B4%ED%8C%8C%EC%9D%BC%20%EC%97%90%EB%9F%AC%20%EB%B0%A9%EC%A7%80%0A%23include%20%3Cstdio.h%3E%0A%23include%20%3Cstring.h%3E%20%20%20%20//%20strtok%20%ED%95%A8%EC%88%98%EA%B0%80%20%EC%84%A0%EC%96%B8%EB%90%9C%20%ED%97%A4%EB%8D%94%20%ED%8C%8C%EC%9D%BC%0A%0Aint%20main%28%29%0A%7B%0A%20%20%20%20char%20s1%5B30%5D%20%3D%20%22The%20Little%20Prince%22%3B%20%20%20%20//%20%ED%81%AC%EA%B8%B0%EA%B0%80%2030%EC%9D%B8%20char%ED%98%95%20%EB%B0%B0%EC%97%B4%EC%9D%84%20%EC%84%A0%EC%96%B8%ED%95%98%EA%B3%A0%20%EB%AC%B8%EC%9E%90%EC%97%B4%20%ED%95%A0%EB%8B%B9%0A%20%20%20%20char%20*sArr%5B10%5D%20%3D%20%7B%20NULL,%20%7D%3B%20%20%20%20//%20%ED%81%AC%EA%B8%B0%EA%B0%80%2010%EC%9D%B8%20%EB%AC%B8%EC%9E%90%EC%97%B4%20%ED%8F%AC%EC%9D%B8%ED%84%B0%20%EB%B0%B0%EC%97%B4%EC%9D%84%20%EC%84%A0%EC%96%B8%ED%95%98%EA%B3%A0%20NULL%EB%A1%9C%20%EC%B4%88%EA%B8%B0%ED%99%94%0A%20%20%20%20int%20i%20%3D%200%3B%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20//%20%EB%AC%B8%EC%9E%90%EC%97%B4%20%ED%8F%AC%EC%9D%B8%ED%84%B0%20%EB%B0%B0%EC%97%B4%EC%9D%98%20%EC%9D%B8%EB%8D%B1%EC%8A%A4%EB%A1%9C%20%EC%82%AC%EC%9A%A9%ED%95%A0%20%EB%B3%80%EC%88%98%0A%0A%20%20%20%20char%20*ptr%20%3D%20strtok%28s1,%20%22%20%22%29%3B%20%20%20//%20%EA%B3%B5%EB%B0%B1%20%EB%AC%B8%EC%9E%90%EC%97%B4%EC%9D%84%20%EA%B8%B0%EC%A4%80%EC%9C%BC%EB%A1%9C%20%EB%AC%B8%EC%9E%90%EC%97%B4%EC%9D%84%20%EC%9E%90%EB%A6%84%0A%0A%20%20%20%20while%20%28ptr%20!%3D%20NULL%29%20%20%20%20%20%20%20%20%20%20%20%20//%20%EC%9E%90%EB%A5%B8%20%EB%AC%B8%EC%9E%90%EC%97%B4%EC%9D%B4%20%EB%82%98%EC%98%A4%EC%A7%80%20%EC%95%8A%EC%9D%84%20%EB%95%8C%EA%B9%8C%EC%A7%80%20%EB%B0%98%EB%B3%B5%0A%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20sArr%5Bi%5D%20%3D%20ptr%3B%20%20%20%20%20%20%20%20%20%20%20%20%20//%20%EB%AC%B8%EC%9E%90%EC%97%B4%EC%9D%84%20%EC%9E%90%EB%A5%B8%20%EB%92%A4%20%EB%A9%94%EB%AA%A8%EB%A6%AC%20%EC%A3%BC%EC%86%8C%EB%A5%BC%20%EB%AC%B8%EC%9E%90%EC%97%B4%20%ED%8F%AC%EC%9D%B8%ED%84%B0%20%EB%B0%B0%EC%97%B4%EC%97%90%20%EC%A0%80%EC%9E%A5%0A%20%20%20%20%20%20%20%20i%2B%2B%3B%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20//%20%EC%9D%B8%EB%8D%B1%EC%8A%A4%20%EC%A6%9D%EA%B0%80%0A%0A%20%20%20%20%20%20%20%20ptr%20%3D%20strtok%28NULL,%20%22%20%22%29%3B%20%20%20//%20%EB%8B%A4%EC%9D%8C%20%EB%AC%B8%EC%9E%90%EC%97%B4%EC%9D%84%20%EC%9E%98%EB%9D%BC%EC%84%9C%20%ED%8F%AC%EC%9D%B8%ED%84%B0%EB%A5%BC%20%EB%B0%98%ED%99%98%0A%20%20%20%20%7D%0A%0A%20%20%20%20for%20%28int%20i%20%3D%200%3B%20i%20%3C%2010%3B%20i%2B%2B%29%0A%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20if%20%28sArr%5Bi%5D%20!%3D%20NULL%29%20%20%20%20%20%20%20%20%20%20%20//%20%EB%AC%B8%EC%9E%90%EC%97%B4%20%ED%8F%AC%EC%9D%B8%ED%84%B0%20%EB%B0%B0%EC%97%B4%EC%9D%98%20%EC%9A%94%EC%86%8C%EA%B0%80%20NULL%EC%9D%B4%20%EC%95%84%EB%8B%90%20%EB%95%8C%EB%A7%8C%0A%20%20%20%20%20%20%20%20%20%20%20%20printf%28%22%25s%5Cn%22,%20sArr%5Bi%5D%29%3B%20%20%20//%20%EB%AC%B8%EC%9E%90%EC%97%B4%20%ED%8F%AC%EC%9D%B8%ED%84%B0%20%EB%B0%B0%EC%97%B4%EC%97%90%20%EC%9D%B8%EB%8D%B1%EC%8A%A4%EB%A1%9C%20%EC%A0%91%EA%B7%BC%ED%95%98%EC%97%AC%20%EA%B0%81%20%EB%AC%B8%EC%9E%90%EC%97%B4%20%EC%B6%9C%EB%A0%A5%0A%20%20%20%20%7D%0A%0A%20%20%20%20return%200%3B%0A%7D&cumulative=false&curInstr=18&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=c_gcc9.3.0&rawInputLstJSON=%5B%5D&textReferences=false

단계별로 실행하면 다음과 같은 그림을 얻을 수 있습니다.

sArr 배열에는 s1에서 각 단어가 시작하는 위치만 포인터로 가리키는 것을 알 수 있습니다.
C 언어는 문자열을 \0 널 문자까지만 출력합니다.
printf("%s\n", sArr[i]);
를 실행하면 단어만 출력하는 이유죠.

상단 고정에 좋은 내용 많으니 참고하세요. 시각화 사이트도 이용하세요.

, 민 현빈님이 작성
 

#define _CRT_SECURE_NO_WARNINGS 

#include <stdio.h>

 #include <string.h>

 int main(void)

 { 

 char s[1001];

 int count = 0; 

 scanf("%[^\n]s", s);

 char* ptr = strtok(s, " .,"); 

 while (ptr != NULL) 

 { 

 if (ptr != NULL && strcmp(ptr,"the") == 0) 

 count += 1; 

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

 } 

 printf("%d", count); 

 return 0;


여기서 질문있습니다. 일단 제가 "the"만 구분해서 개수 출력하는건데

머리를 쥐어 짜내도 기억이 안나서 틀렸습니다만, strcmp 를 생각을 못하다니.... 그나저나 char* ptr = strtok(s, " .,"); 에서 문자열 자르는 기준을 마침표랑 쉼표로 잡아주는 이유가 그 두개를 기준으로 잘라서 문자열 "the"가 있는지 보고 그 다음으로 넘어가기 위함 이라고 생각해야 하나요???

 }



, 정 원이님이 작성
 

해당 글의 예제처럼 문자열 선언과 동시에 값을 넣는 방법 말고 scanf로 값을 입력 받아 문자열 자르기를 하려고 했는데 

#define _CRT_SECURE_NO_WARNINGS    // strtok 보안 경고로 인한 컴파일 에러 방지

#include <stdio.h>

#include <string.h>    // strtok 함수가 선언된 헤더 파일

#include<stdlib.h>


int main()

{

    char s1[30];    // 크기가 30인 char형 배열을 선언하고 문자열 할당

    scanf("%s", s1);

    char* sArr[10] = { NULL, };    // 크기가 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)           // 문자열 포인터 배열의 요소가 NULL이 아닐 때만

            printf("%s\n", sArr[i]);   // 문자열 포인터 배열에 인덱스로 접근하여 각 문자열 출력

    }

    return 0;

}


해당 코드에서 The Little Prince 입력시 The밖에 출력되지 않습니다.

문자열 포인터로 malloc 함수를 통해 메모리를 할당해도 동작하지 않는데 어떻게 해야하나요?

 

범위를 넘어가는 것에 대해서 C 언어는 자유롭습니다.

이로 인해서 다양한 over-write, 쓰기가 가능하고 이는 권한 탈취 등의 보안 버그로 사용됩니다.

strtok_s 같은 함수가 있습니다. 이는 C11에서만 지원합니다.

C 언어는 수많은 레거시가 있고, 여전히 과거에도 지금도 strtok 함수가 쓰입니다.

strtok 함수를 아예 쓰지 말라고 경고하는 것은 Visual Studio가 유일합니다. 그러나 현장에서 리눅스 분야에서 코딩한다면 strtok를 사용한다고 경고가 발생하지는 않습니다. GCC, Clang을 쓰세요. 그러면 경고는 없습니다.

이는 Visual Studio를 만든 MS의 정책일 뿐입니다.

scanf_s 함수는 Visual Studio에서만 지원하고, 비표준 함수라 다른 플랫폼에서는 지원하지도 않습니다. 코딩 테스트는 주로 리눅스 환경에서 이뤄지는데 리눅스 환경에서는 scanf_s 함수를 지원하는 컴파일러가 있는지 모르겠습니다. GCC, Clang에서는 지원하지 않습니다. 코딩 테스트 문제를 모두 풀어 놓고도 0점으로 탈락할 수 있습니다. Visual Studio를 따르지 마세요. 그냥 표준을 따르세요.


https://www.youtube.com/watch?v=WXYBoM20f5g

VS 2022와 큰 차이 없으니 이거 보고 컴파일러를 Clang으로 바꾸는 것도 방법입니다.


, 노 농님이 작성
 
strtok함수를 쓰기 위해서는 보안 경고 에러 방지를 위해 "#define _CRT_ SECURE_NO_WARNINGS" 작업을 따로 해줘야 하는 부분이 계속 안 외워집니다...ㅜㅜ 혹시 strtok를 사용할 때 보안 문제가 왜 생기는지 알 수 있을까요??
, 군대에서 다시해보자님이 작성
 

안녕하세요, 코딩도장에서 C언어를 공부하고 있는 학생입니다.

공부를 하던 도중 질문하고 싶은 것이 2가지 있어서 글을 남기게 되었습니다. 


1. 47.6 심사문제 N-gram 만들기

#include <stdio.h>

#include <string.h>


int main()

{

    

    int n;

    char word[11];

    scanf("%d %s",&n,word);

    

    if(strlen(word)<n)

    {

        printf("wrong\n");

    }

    else

    {

        for(int i = 0 ; i < strlen(word)-(n-1) ; i++)

        {

            printf("%c%c%c\n",word[i],word[i+1],word[i+2]);

        }

    }   

    return 0;

}


이 문제는 처음에 이렇게 작성했는데, 틀렸다고 하더군요

그래서 마지막 반복문을 

문의한 코드는 삭제합니다.

이렇게 수정하니, 정답으로 처리되더군요

여기서 궁금한 점은 위 차이점이 정답의 유무를 가질정도의 차이인지,엄연히 다른게 존재하는지 궁금합니다.


2. strcat 질문

strcat에서 두번째 인자(붙일 내용의 문자열)가 NULL값이 된 경우,

세그먼트오류(코어 덤프)가 발생했습니다.

이 오류가 뭔지 찾아보니 건드리면 안되는, 범위 밖의 메모리를 포인터로 건드린 그런 종류의 오류더군요

NULL값과 이 오류가 발생한 연관성을 찾아봐도 도저히 모르겠어서 질문드립니다.

아래는 오류가 발생한 코드입니다. (참고로 47.5 심사문제입니다.)

아래 코드에 입력값으로 "nurs  es run"를 입력했을 때 기준입니다.

(주석처리한 부분을 주석해제하면 오류가 발생합니다.

원래는 while(token[count]!=NULL)대신 주석처리된 for문으로 풀려고 했습니다)

*참고로 제 코딩 환경은 replit.com이라는 클라우드 환경입니다.



#include <stdio.h>

#include <string.h>

#include <stdbool.h>


int main()

{

    char inputword[31];

    char *token[10]={NULL,};

    char nospaceword[31]={NULL,};

    bool issame = true;

    

    scanf("%[^\n]s",inputword);

    char *ptr = strtok(inputword," ");

    int count = 0;

    

    while(ptr!=NULL)

    {

        token[count]=ptr;

        count++;

        

        ptr = strtok(NULL," ");

    }

  count = 0;

  while(token[count]!=NULL)

    {

      strcat(nospaceword,token[count]);

      count++;

    }

  //strcat(nospaceword,token[3]);//이거 왜 오류나는지 찾아보자. NULL값이 scr라서 코어덤프난 것 같은데 확실한지는 모름..

    // for(int i=0;i<strlen(token);i++)

    // {

    //     strcat(nospaceword,token[i]);

    // }

  //  ↑오류는 범위 밖 메모리 건드려서 나는 코어덤프. NULL 값 섞여서 일어난 문제같음.

    for(int i=0;i<(strlen(nospaceword)/2);i++)

    {

        if(nospaceword[i]!=nospaceword[strlen(nospaceword)-1-i])

        {

            issame = false;

            break;

        }

    }

    printf("%d",issame);

    return 0;

}


감사합니다!

(도장_ 관리자님이 수정함 - 원문 제출일: 수요일, 20 7월 2022, 11:56 오후)

태그:
, 도장_ 관리자님이 작성
 
문자열을 자른 직후에는 결과가 NULL일 수 있습니다.
다만, 문자열을 자른 위치에 따라 작성하는 코드가 다를 수 있습니다.
    while (ptrthe != NULL)

    {

        if (strcmp(ptrthe, "the") == 0)

        {

            thecount++;

        }

        ptrthe = strtok(NULL, " .,'");

    }

이것도 방법이지만, 통상은

    while (ptr != NULL)
    {
        ptr = strtok(NULL, " .,");
        if (ptr != NULL && strcmp(ptr, "the") == 0)
            count++;
    }

이와 같은 방식입니다.
문자열을 자른 이후에 strcmp를 호출하는 방식이라면 NULL 검사를 하지만,
작성한 코드처럼 문자열을 마지막에 자르고 while 조건식에서 ptr != NULL로 검사하기
때문에 if에서 검사할 필요가 없어지는 것의 차이입니다.

정답 코드를 작성하는 방식은 여러 가지가 있습니다. 해설은 일반적인 방식에 대한 것입니다.


, 양 승현님이 작성
 

#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>

#include <stdint.h>

#include <stdlib.h>

#include <string.h>


int main()

{

int8_t* s1 = malloc(sizeof(int8_t) * 1001);


scanf("%[^\n]s", s1);


int8_t* ptrthe = strtok(s1, " .,'");


int32_t thecount = 0;


while (ptrthe != NULL)

{

if (strcmp(ptrthe, "the") == 0)

{

thecount++;

}

ptrthe = strtok(NULL, " .,'");

}


printf("%d\n", thecount);


free(s1);


return 0;

}


UNIT 45.8 심사문제입니다.

위 코드에서 while문 안에 if조건문에서 ptrthe포인터값이 the와 같을 때만 thecount를 1 증가시키는데,

해셜에서는 NULL이 아닐때도 if조건문 안에 넣어야된다고 나와있습니다.


소스코드를 입력해보면 ptrthe != NULL 을 넣지 않아도 통과가 되는데 NULL이 아닐 때의 조건의 꼭 입력해야하나요?

어차피 NULL도 the문자열과 다르기 때문에 if 조건문의 코드가 실행되지 않는거 아닌가요?