c++ - 함수 - string literal javascript




출력 스트림에 '\ n'보다 '\ n'이 우선 사용되는 이유는 무엇입니까? (3)

'\ n'대신 std :: endl을 사용하여 가독성을 높이는 것이 좋습니다.

this 대답에서 우리는 그것을 읽을 수 있습니다 :

나는 '\n' 사용하거나 "\n" 사용하는 것 사이에 약간의 차이가 있다고 가정한다. 그러나 후자는 (2 개의) 문자들의 배열이며, 이는 문자 단위로 인쇄되어야한다. 단일 문자 출력보다 복잡합니다 .

강조

그건 나에게 의미가있다. 나는 const char* 을 출력하는 것이 null terminator를 테스트 할 루프를 필요로한다고 생각할 것이다. null-terminator는 간단한 putchar ( std::coutchar delegate를 호출하는 것보다 더 많은 연산을 도입 해야 한다. 예제를 소개하는 단순화).

그게 내가 사용하도록 설득했다.

std::cout << '\n';
std::cout << ' ';

오히려

std::cout << "\n";
std::cout << " ";

성능 차이가 거의 무시할 수 있음을 알고 있다는 점을 여기서 언급 할 가치가 있습니다. 그럼에도 불구하고, 어떤 사람들은 이전 접근법이 단지 한 char 씩 ( '\0' 을 계산하면 2 개의 char 길이) 된 문자열 리터럴이 아니라 실제로 하나의 문자를 전달할 의도가 있다고 주장 할 수 있습니다.

최근에 나는 후자의 접근법을 사용하고있는 누군가를 위해 약간의 코드를 수정했다. 나는 사건에 대해 작은 논평을하고 계속 전진했다. 그런 다음 개발자는 저에게 감사의 말을 전했고 그와 같은 차이점을 생각조차하지 못했다고 말했습니다 (주로 의도에 초점을 맞춘 것). 전혀 충격적이지는 않았지만, 그 변화가 채택되었습니다.

나는 그 변화가 얼마나 중요한지 궁금해지기 시작했다. 그래서 나는 대담을 보았다. 놀랍게도 -std=c++17 -O3 플래그를 사용하여 GCC (트렁크)에서 테스트했을 때 다음 결과를 보여줍니다. 다음 코드에 대해 생성 된 어셈블리 :

#include <iostream>

void str() {
    std::cout << "\n";
}

void chr() {
    std::cout << '\n';
}

int main() {
    str();
    chr();
}

chr() 이 실제로 str() 는 것보다 정확히 두 배 많은 명령어를 생성하고 있기 때문에 놀랐다.

.LC0:
        .string "\n"
str():
        mov     edx, 1
        mov     esi, OFFSET FLAT:.LC0
        mov     edi, OFFSET FLAT:_ZSt4cout
        jmp     std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
chr():
        sub     rsp, 24
        mov     edx, 1
        mov     edi, OFFSET FLAT:_ZSt4cout
        lea     rsi, [rsp+15]
        mov     BYTE PTR [rsp+15], 10
        call    std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
        add     rsp, 24
        ret

그게 왜? 왜 둘 다 결국 const char* 인자를 사용하여 동일한 std::basic_ostream 함수를 호출할까요? char 리터럴 방식이 문자열 리터럴 방식보다 좋지 않다는 것을 의미합니까?


다른 답변들 중 어느 것도 정말로 컴파일러가 왜 Godbolt 링크에서 코드를 생성하는지 설명하지 않습니다. 그래서 나는 칩을 넣을 것이라고 생각했습니다.

생성 된 코드를 보면 다음과 같은 것을 볼 수 있습니다.

std::cout << '\n';

컴파일 후, 실제로 :

char c = '\n';
std::cout.operator<< (&c, 1);

이 작업을하기 위해, 컴파일러는 많은 추가 명령어가있는 함수 인 chr() 대한 스택 프레임을 생성해야합니다.

반면에, 이것을 컴파일 할 때 :

std::cout << "\n";

컴파일러는 str() 을 간단히 'tail call' operator<< (const char *) 최적화 할 수 있습니다. 이는 스택 프레임이 필요 없음을 의미합니다.

따라서 operator<< 에 대한 호출을 별도의 함수에 넣으면 결과가 다소 왜곡됩니다. 이 전화를 인라인으로 연결하는 것이 더 분명합니다. https://godbolt.org/z/OO-8dS

이제 '\n' 을 출력하는 동안 ofstream::operator<< (char) 대한 특정 오버로드가 없기 때문에 조금 더 비싸다는 것을 알 수 있습니다. 그 차이는 예제에서보다 적습니다.


예,이 특정 구현의 경우, 예를 들어, char 버전은 문자열 버전보다 조금 느립니다.

두 버전 모두 write(buffer, bufferSize) 스타일 함수를 호출합니다. 문자열 버전의 경우 bufferSize 는 컴파일 타임 (1 바이트)으로 알려져 있으므로 0 터미네이터 런타임을 찾을 필요가 없습니다. char 버전의 경우 컴파일러는 스택에 작은 1 바이트 버퍼를 만들고 그 안에 문자를 넣고이 버퍼를 전달하여 쓰기 작업을합니다. 그래서 char 버전은 약간 느립니다.







string-literals