이 함수가 올바른 문자열 길이를 반환하는 이유는 무엇입니까? (char 포인터 늘리기)


12

문자열의 문자 수를 계산하는 함수입니다.

int str_len(const char* s) {
    int i = 0;
    while(*(s++)) {
        i++;
    }
    return i;
}

이것이 올바른 길이를 반환하는 이유는 무엇입니까?

간단한 String 으로이 함수를 호출한다고 가정 해 봅시다 "a". 그런 다음 swhile 루프에서 증가하므로 s및 의 값 i은 모두 0입니다.

답변:


10

의 값은 증분 이전 s++의 원래 값으로 s, 증분은 다음 시퀀스 포인트 전의 지정되지 않은 시간에 발생합니다.

따라서 *s++*(s++) 동일 : 그들은 모두의 원래 값을 역 참조 s. 또 다른 동등한 표현은 *(0, s++)마음이 희미하지 않은 것이 아니라 다음과 같습니다.0[s++]

그러나 함수는 type size_t을 사용해야합니다 .i 과 반환 유형 합니다.

size_t str_len(const char *s) {
    size_t i = 0;
    while (*s++) {
        i++;
    }
    /* s points after the null terminator */
    return i;
}

다음은 루프 당 단일 증분이있는 잠재적으로 더 효율적인 버전입니다.

size_t str_len(const char *s) {
    const char *s0 = s;
    while (*s++) {
        /* nothing */
    }
    return s - 1 - s0;
}

두 번째 단락의 이상한 표현에 대해 궁금해하는 사람들을 위해 :

  • 0, s++,왼쪽 부분을 평가 한 다음 오른쪽 부분을 평가 하는 쉼표 연산자의 인스턴스입니다 . 그 후(0, s++) 와 같습니다 (s++).

  • 0[s++]동등 (s++)[0]하고 *(0 + s++)또는 *(s++ + 0)같은 단순화 된 *(s++). 표현식에서 포인터와 색인 표현식을 전치하는 []것은 흔하지 않거나 특히 유용하지는 않지만 C 표준을 준수합니다.


물론 쉼표 연산자가 명확하기를 바랍니다. , s++일어날 일과 나쁜 일을 치워라:)
David C. Rankin

6

간단한 문자열 "a"로이 함수를 호출한다고 가정 해 봅시다. 그런 다음 while 루프에서 s가 증가하므로 s의 값은 0이고 i도 0입니다.

이 예 s에서 'a'in을 가리 킵니다 "a". 그런 다음 증분되고 i증분됩니다. 이제 s널 종료자를 가리키고 iis 1입니다. 따라서 다음에 루프를 통과하면 *(s++)is '\0'( 0)이므로 루프가 종료되고 현재의 값 i( 1)이 반환됩니다.

일반적으로 루프는 문자열의 각 문자에 대해 한 번 실행 된 다음 널 종료 자에서 중지되므로 문자 수를 계산합니다.


s는 괄호 안에 있기 때문에 먼저 증가 할 것이라고 생각했습니다 (따라서 '/ 0'을 가리킴). 따라서 while 루프는 false이고 i는 증가하지 않습니다.
lor

2
@lor, 후행 증가 연산자가 무엇인지 기억하십시오. 증가 하기 전에s 보유한 것으로 평가합니다 . 당신이 묘사하는 것은 (실제로 하나의 카운트보다 작고 빈 문자열을 전달하면 UB를 호출하는) 동작입니다. ++s
Toby Speight

2

완벽하게 이해됩니다.

int str_len(const char* s) {
    int i = 0;
    while(*(s++)) { //<-- increments the pointer to char till the end of the string
                    //till it finds '\0', that is, if s = "a" then s is 'a'
                    // followed by '\0' so it increments one time
        i++; //counts the number of times the pointer moves forward
    }
    return i;
}

"하지만 s대괄호 안에 있습니다. 그것이 내가 먼저 증가 할 것이라고 생각한 이유입니다."

그렇기 때문에 포인터가 문자가 아닌 증분되는 이유입니다.이 경우 포인터가 아니라 (*s)++문자가 증분됩니다. 역 참조는 이제 포인터 자체가 아니라 포인터가 참조하는 값으로 작업하고 있음을 의미합니다.

두 연산자의 우선 순위는 같지만 오른쪽에서 왼쪽으로의 연관성이 있으므로 *s++대괄호없이 간단히 포인터를 사용할 수도 있습니다 .


그러나 s는 괄호 안에 있습니다. 그것이 내가 먼저 증가 할 것이라고 생각한 이유입니다. "a"와 같은 간단한 문자열이 있으면 "/ 0"을 가리 킵니다. 조건이 이제 while (0)이므로 while 루프는 입력되지 않습니다.
lor

2

사후 증분 연산자는 피연산자의 값을 1 씩 증가 시키지만 표현식의 값은 증분 연산 이전의 피연산자의 원래 값입니다.

전달 된 인수가 str_len()입니다 "a". 에서 str_len(), 포인터는 s문자열의 첫 번째 문자를 가리키고 있습니다 "a". 에서 while루프 :

while(*(s++)) {
.....
.....

가 있지만 s증가하지만 될 값 s표현은 첫 번째 문자에 포인터가 증가하기 전에 가리키는 문자에 대한 포인터 일 것이다 'a'. 포인터 s가 역 참조 되면 character가 제공됩니다 'a'. 다음 반복에서 s포인터는 널 문자 인 다음 문자를 가리 킵니다 \0. s역 참조 되면 0루프가 종료됩니다. 그 참고 s이제 문자열의 NULL 문자 과거 하나 개의 요소를 가리키는 것입니다 "a".

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.