[TIL]20210926

박창현·2021년 9월 26일
0

TODAY I LEARNED

목록 보기
49/53

C

문자열 상수 관련.

int main(void){

    char *dessert= "apple"; //문자열을 주소이다!!. 주소에 접근한 후 상자를 열어 값을 수정해야하지, 주소 값을 apple, banana로 바꿀려니까 안되지.
   
    printf("%s %p\n",dessert,dessert);
    dessert="banana"; 
    printf("%s %p\n",dessert,dessert); // 문자열은 상수. 포인터로 주소에 접근해서 상자 내부의 값을 변경하는 것이 아니라, 다른 상자 주소를 가리키도록 만드는 것.
}
int main(void){

    char str[80]; //char* 을 이용해 칸 수 제한을 없애든지, str[80]등으로 칸 수 제한을 두던지.
    scanf("%s",str); // scanf();는 공백이 없는 연속된 문자열을 입력받음.
    printf("%s\n",str);
    scanf("%s",str);
    printf("%s\n",str);
    // "apple jam" 입력하면
    // apple
    // jam
    // 이렇게 출력됨.
}

apple jam 을 입력하면 버퍼에 두 단어가 임시저장되고, scanf에서는 공백을 기준으로 끊기때문에 apple 만을 가져간다. 그 후, 다시 scanf에서 jam을 가져간다.

gets

공백이나 탭 문자도 입력이 필요할 경우 gets() 을 이용한다.

int main(void){

    char str[80]; //char* 을 이용해 칸 수 제한을 없애든지, str[80]등으로 칸 수 제한을 두던지.
    gets(str);
    printf("%s\n",str);
}

apple jam 을 입력하면 그대로 출력된다.

scanf는 스페이스바, 탭, 엔터 입력값을 /0 으로 변환하고, gets는 엔터 입력값만 /0으로 바꾸고 나머지는 다 본래의 값을 보관한다.

fgets

앞의 scanf, gets는 버퍼에서 가져오는 문자열의 크기와 선언한 배열의 크기를 비교하지 않기때문에, 입력받다가 할당되지 않은 메모리 공간을 침범하는 문제가 발생할 수 있다. 이를 예방하기위해 fgets를 사용한다.

fgets(str, sizeof(str), stdin);

배열명, 배열의 크기, 표준 입력 순으로 작성한다.
배열 크기는 -1을 하지 않아도 알아서 마지막 한칸은 \0으로 생각하고 입력을 받아준다.
그리고 마지막의 표준 입력은, scanf와 gets는 기본적으로 표준입력을 사용하지만, fgets는 입력 버퍼를 선택할 수 있다. 다른 것과 마찬가지로 표준입력을 사용할 것이기에 stdin으로 사용.


fgets는 엔터 입력값인 \n\0으로 바꾸는 앞의 함수들과 다르게 개행문자까지 저장하고 마지막에 널 문자를 붙인다. 그렇기 때문에 밑의 경우처럼 %s뒤에 문자를 더 작성하면 개행 된 후 "입니다"가 출력된다. 이를 예방하기위해 str[strlen(str)-1]='\n'; 을 이용한다. strlen(str)의 자리에는 \0이 있고 그 한 칸앞에 \n 이 있기에 -1을 붙여준다.

int main(void){

    char str[80]; //char* 을 이용해 칸 수 제한을 없애든지, str[80]등으로 칸 수 제한을 두던지.
    fgets(str,sizeof(str),stdin);
    str[strlen(str)-1]='\n';
    printf("%s입니다.",str);
}

scanf, getchar | gets, fgets의 버퍼 공유 문제와 해결방안.

scanf() 함수는 \n(줄바꿈문자)를 가져오지 않고, 마지막에 \0(널문자)를 붙인다.

getchar 함수 또한 scanf와 비슷할듯?( 얘는 잘 모르겠음)

gets() 함수는 \n(줄바꿈문자)까지 가져오고, \n을 \0으로 대체 한다.

fgets() 함수는 \n(줄바꿈문자)까지 가져오고, 추가적으로 \0을 붙인다.

즉, scanf와 getchar는 버퍼에 \n을 남긴다. 개행문자 전까지의 값을 메모리에 가져온 후에 \0을 붙인다. gets나 fgets는 \n까지 가져온다.
이 차이로 인해 문제가 생긴다.
scanf나 getchar로 문자를 입력받으면 버퍼에 \n이 남아서 다음번에 버퍼에서 값을 가져오려할때 이 \n만 입력받고 함수를 끝낸다.

이를 해결하기 위해

1.
getchar();
2.
scanf("%*c");
3.
fgetc(stdin);

함수를 이용.
위 코드들은 작동방식에서 조금의 차이는 있지만, 결론적으로 버퍼의 맨 앞 한칸을 가져와서 버린다.

puts, fputs

put(str); // 자동 줄 바꿈.
put(str,stdout); // 줄 바꾸지 않음.

strcat, strncat

문자열을 뒤에 이어 붙인다.

char str[80]= "straw";
strcat(str, "berry"); --> strawberry
strncat(str, "piece" ,3); --> strawpie

주의사항, 사용할 배열은 초기화 되있어야함. 이어붙일때 널 문자 위치를 기준으로 하기 때문.

strcmp, strncmp

문자열 비교.
strcmp(str1, str2);
str1과 str2가 똑같이 대 or 소 문자일 경우만 정확한 비교가 됨.
각 자리 수마다 비교해서 str1이 str2보다 사전에 나중에 나오면(아스키 코드값이 크면) 1을 반환, 그 반대면 -1을 반환. 같으면 0을 반환한다.

strcpy 함수 만들어보기.

char *my_strcpy(char *pd, char *ps);

int main(void){

    char str[80]="starwberry";

    printf("before : %s\n", str);
    my_strcpy(str,"apple");
    printf("after : %s\n", str);

    return 0;
}

char *my_strcpy(char *pd, char *ps){
    char *po  = pd; //예전 파이썬 알고리즘 할때 공부한 내용. pd는 주소값이 계속 변하기때문에, 초기 메모리 위치를 기억할 친구가 하나 필요.

    while(*ps != '\0'){
        *pd=*ps; //배열이 아니기 때문에 문자열 상수가 아님. 그럼으로 내부수정이 가능. *pd로 주소의 상자를 열고 *ps로 다른 상자의 내용물을 그대로 복사.
    pd++;
    ps++;

    }
    *pd='\0'; 
    return po;
}
profile
개강했기에 가끔씩 업로드.

0개의 댓글