std :: size_t를 언제 사용해야합니까?


201

std::size_t대신 루프와 물건에 사용해야 int합니까? 예를 들어 :

#include <cstdint>

int main()
{
    for (std::size_t i = 0; i < 10; ++i) {
        // std::size_t OK here? Or should I use, say, unsigned int instead?
    }
}

일반적으로 사용시기에 관한 모범 사례는 무엇 std::size_t입니까?

답변:


186

경험상 좋은 규칙은 루프 조건에서 자연스럽게 다른 것과 비교해야하는 모든 것입니다 std::size_t.

std::size_tsizeof표현식 의 유형이며 C ++에서 객체 (배열 포함)의 최대 크기를 표현할 수 있음을 보장합니다. 또한 확장하여 배열 인덱스에 대해 충분히 클 수 있으므로 배열에 대한 인덱스 별 루프의 자연스러운 유형입니다.

숫자를 세는 것이라면 그 숫자를 보유한 변수의 유형을 사용하는 것이 더 자연 스럽거나 기계의 크기가 자연 스럽기 때문에 ( int또는 unsigned int충분히 큰 경우) 더 자연 스럽습니다.


41
그것의 가치가 있음을 언급 하지 사용 size_t이 발생할 수 있습니다해야 할 때 보안 버그 .
BlueRaja-대니 Pflughoeft

5
int는 "자연"일뿐만 아니라 서명 된 유형과 서명되지 않은 유형을 혼합하면 보안 버그도 발생할 수 있습니다. 부호없는 인덱스는 처리하기가 어렵고 사용자 정의 벡터 클래스를 사용해야하는 좋은 이유입니다.
Jo So

2
@JoSo ssize_t부호있는 값들도 있습니다.
EntangledLoops

70

size_tsizeof연산자 의 결과 유형입니다 .

size_t배열에서 크기 또는 색인을 모델링하는 변수에 사용하십시오 . size_t의미를 전달합니다. 즉, 다른 정수가 아니라 크기 또는 바이트 단위의 크기를 나타냅니다.

또한 size_t크기를 바이트 단위로 나타내는 데 사용하면 코드를 이식성있게 만들 수 있습니다.


32

size_t유형은 지정하기위한 것입니다 크기 는 문자열의 길이를 취득하고 각 문자를 처리하는, 예를 들어, 그것을 사용하는 천연 그래서 무언가를 :

for (size_t i = 0, max = strlen (str); i < max; i++)
    doSomethingWith (str[i]);

당신은 않는 이 부호없는 형식이기 때문에, 물론 경계 조건에 대한 조심해야합니다. (비록 최대는 일반적으로 크기 때문에 맨 끝에서 경계는 일반적으로 중요하지 않다 입니다 거기에 도착하는 것이 가능). 대부분의 사람들 int은 그 용량을 초과 할만큼 충분히 큰 구조 나 배열을 거의 갖지 않기 때문에 그런 종류의 것을 위해 단지를 사용 합니다 int.

그러나 다음과 같은 것을 조심하십시오.

for (size_t i = strlen (str) - 1; i >= 0; i--)

부호없는 값의 줄 바꿈 동작으로 인해 무한 루프가 발생합니다 (컴파일러가 이것에 대해 경고하는 것을 보았지만). 이것은 또한 약간 완화 될 수 있습니다 (약간 이해하기는 어렵지만 적어도 포장 문제에 대한 면역) :

for (size_t i = strlen (str); i-- > 0; )

연속 조건 후 체크 부작용으로 감소를 이동시킴으로써,이 값에 연속 대한 검사 수행 전에 (어떤 이유에서 루프가 실행 감소를 여전히 루프 내부의 감소 값을 사용 len .. 1하는 대신 len-1 .. 0).


14
그건 그렇고, strlen루프의 각 반복 을 호출하는 것은 나쁜 습관 입니다. :) 다음과 같은 작업을 수행 할 수 있습니다.for (size_t i = 0, len = strlen(str); i < len; i++) ...
musiphil

1
부호있는 유형이더라도 부호있는 정수 오버플로는 정의되지 않은 동작이므로 경계 조건을 조심해야합니다.
Adrian McCarthy

2
다음과 같은 (유명하지 않은) 방법으로 정확하게 카운트 다운을 수행 할 수 있습니다.for (size_t i = strlen (str); i --> 0;)
Jo So

1
@JoSo, -->"goes to"연산자 ( stackoverflow.com/questions/1642028/… 참조 ) 의 도입이 마음에 들지 않지만 실제로는 정말 깔끔한 트릭 입니다. 귀하의 제안을 답변에 포함 시키십시오.
paxdiablo

당신은 간단한 작업을 수행 할 수 있습니다 if (i == 0) break;, 예를 들어, for 루프의 끝 부분 (에서 for (size_t i = strlen(str) - 1; ; --i).) 더 나은하지만 당신처럼 (하지만 이것은 단지 잘 작동하면 그냥 궁금.
RastaJedi

13

정의 size_t에 따라 sizeof연산자 의 결과입니다 . size_t크기를 참조하기 위해 만들어졌습니다.

당신이 무언가를하는 횟수 (예에서 10)는 크기에 관한 것이 아니므로 왜 사용 size_t합니까? int또는 unsigned int이어야합니다.

물론 i루프 내부에서 수행하는 작업과 관련이 있습니다 . unsigned int예를 들어을 사용 하는 함수에 전달하면을 선택하십시오 unsigned int.

어쨌든 암시 적 유형 변환을 피하는 것이 좋습니다. 모든 유형 변환을 명시 적으로 만드십시오.


10

size_t등의 문자열의 길이, 포인터가 소요 바이트의 양 또한 플랫폼에서 휴대용입니다 - - 항목의 크기 치수를 지정하는 매우 읽을 수있는 방법은 당신이 64 비트를 찾아 시스템 기능과 함께 잘 모두 행동하라를 32 비트 것 size_t- 뭔가 unsigned int예를 들어, 사용시기를 수행하지 않을 수 있습니다 (unsigned long


9

짧은 답변:

거의 없다

긴 대답 :

32 비트 시스템에서 2GB보다 큰 char 벡터가 필요할 때마다 다른 모든 사용 사례에서 부호없는 유형을 사용하는 것이 부호없는 유형을 사용하는 것보다 훨씬 안전합니다.

예:

std::vector<A> data;
[...]
// calculate the index that should be used;
size_t i = calc_index(param1, param2);
// doing calculations close to the underflow of an integer is already dangerous

// do some bounds checking
if( i - 1 < 0 ) {
    // always false, because 0-1 on unsigned creates an underflow
    return LEFT_BORDER;
} else if( i >= data.size() - 1 ) {
    // if i already had an underflow, this becomes true
    return RIGHT_BORDER;
}

// now you have a bug that is very hard to track, because you never 
// get an exception or anything anymore, to detect that you actually 
// return the false border case.

return calc_something(data[i-1], data[i], data[i+1]);

의 서명 상당 size_t하다 ptrdiff_t하지 int. 그러나 intsize_t보다 대부분의 경우 여전히 사용하는 것이 좋습니다. ptrdiff_tlong32 및 64 비트 시스템.

이것은 std :: containers와 상호 작용할 때마다 항상 size_t로 변환해야한다는 것을 의미합니다. 매우 아름답지는 않습니다. 그러나 계속되는 네이티브 컨퍼런스에서 c ++의 저자는 서명되지 않은 size_t로 std :: vector를 디자인하는 것은 실수라고 언급했습니다.

컴파일러에서 ptrdiff_t에서 size_t 로의 암시 적 변환에 대한 경고를 표시하면 생성자 구문으로 명시 적으로 만들 수 있습니다.

calc_something(data[size_t(i-1)], data[size_t(i)], data[size_t(i+1)]);

경계를 지우지 않고 컬렉션을 반복하려면 다음을 기반으로 범위를 사용하십시오.

for(const auto& d : data) {
    [...]
}

여기 Bjarne Stroustrup (C ++ 저자)의 네이티브 단어가 있습니다.

어떤 사람들에게는 STL 에서이 부호있는 / 부호없는 디자인 오류가 std :: vector를 사용하지 않고 자체 구현을하기에 충분한 이유입니다.


1
나는 그들이 어디에서 왔는지 이해하지만 여전히 쓰기가 이상하다고 생각합니다 for(int i = 0; i < get_size_of_stuff(); i++). 자, 당신은 많은 원시 루프를하고 싶지 않을 수도 있지만, 계속 사용하십시오.
einpoklum

원시 루프를 사용하는 유일한 이유는 C ++ 알고리즘 라이브러리가 상당히 나쁘게 설계 되었기 때문입니다. 스칼라와 같이 컬렉션에서 작동하는 훨씬 더 발전된 라이브러리를 가진 언어가 있습니다. 그런 다음 원시 루프의 사용 사례가 거의 제거됩니다. 새롭고 더 나은 STL을 사용하여 c ++를 개선하는 방법도 있지만 향후 10 년 내에 이러한 상황이 발생할 것으로 의심됩니다.
Arne

1
나는 부호없는 i = 0을 얻는다. 주장 (i-1, MAX_INT); 그러나 서명되지 않은 정수에 대한 산술 동작이 항상 정의되어 있기 때문에 "내가 이미 언더 플로우가있는 경우 이것이 사실이된다"고 말하는 이유를 이해하지 못합니다. 결과는 표현 가능한 가장 큰 정수의 크기 인 결과 모듈로입니다. 따라서 i == 0이면 i--는 MAX_INT가되고 i ++는 다시 0이됩니다.
mabraham

@mabraham 나는 신중하게 보았고, 옳습니다. 제 코드는 문제를 보여주기에 가장 적합하지 않습니다. 일반적으로 이것은와 x + 1 < y동일 x < y - 1하지만 unsigend 정수가 아닙니다. 이는 변형 된 것으로 간주되는 버그를 쉽게 도입 할 수 있습니다.
Arne

8

C 스타일 배열 인덱싱 / 카운팅에는 std :: size_t를 사용하십시오.

STL 컨테이너의 경우 (예를 들어) vector<int>::size_type벡터 요소를 인덱싱하고 계산하는 데 사용해야합니다.

실제로는 일반적으로 서명되지 않은 정수이지만 특히 사용자 지정 할당자를 사용할 때는 보장되지 않습니다.


2
Linux에서 gcc를 사용하면 std::size_t일반적으로 (4 바이트)가 unsigned long아니라 unisgned int( 64 비트 시스템에서 8 바이트)입니다.
rafak

5
C 스타일 배열은 size_t인덱스가 음수가 될 수 있기 때문에 인덱스 되지 않습니다 . size_t그러나 부정적인 배열을 원하지 않는다면 그러한 배열의 자신의 인스턴스에 사용할 수 있습니다 .
Johannes Schaub-litb

u64에 대한 비교는 u32에 대한 비교만큼 빠릅니까? 루프 센티넬로 u8s와 u16s를 사용하는 것에 대한 심각한 처벌을 받았지만 인텔이 64 대에서 함께 행동했는지는 알 수 없습니다.
Crashworks

2
C 스타일 배열 인덱싱은 +포인터에서 연산자를 사용하는 것과 같기 때문에 ptrdiff_t인덱스에 사용 하는 것 같습니다 .
Pavel Minaev

8
에 관해서는 vector<T>::size_type이 효과적으로 보장되기 때문에 (다른 모든 컨테이너에 대한 상동), 그것은 사실이 아니라 쓸모 size_t- 그것은에 typedef되어있어 Allocator::size_type특히, -, 존중과 그 제한에 대한 용기는 20.1.5 / 4 볼에 size_type해야합니다 이어야 size_t하고 difference_type이어야합니다 ptrdiff_t. 물론 기본값 std::allocator<T>은 이러한 요구 사항을 충족시킵니다. 따라서 짧게 사용하고 size_t나머지 부분을 귀찮게하지 마십시오 :)
Pavel Minaev

7

곧 대부분의 컴퓨터는 수십억 개의 요소가있는 컨테이너에서 작동하는 프로그램을 실행하는 64 비트 OS가 포함 된 64 비트 아키텍처가 될 것입니다. 그럼 당신은 있어야 사용 size_t대신 int, 루프 인덱스로 그렇지 않으면 지수는 것이다 랩 어라운드 2 ^ 32시 : 32 비트 및 64 비트 시스템에 번째 요소.

미래를 준비하십시오!


귀하의 인수는 지금까지 한 요구를 의미하는 것으로가는 long int오히려보다 int. 경우 size_t64 비트 OS에 관련된 그것은 32 비트 OS 다만 관련성이었다.
einpoklum

4

size_t를 사용할 때 다음 표현식에주의하십시오

size_t i = containner.find("mytoken");
size_t x = 99;
if (i-x>-1 && i+x < containner.size()) {
    cout << containner[i-x] << " " << containner[i+x] << endl;
}

x에 대한 값이 무엇이든 관계없이 if 표현식에서 false를 얻습니다. 문제의 원인을 파악하는 데 몇 분 밖에 걸리지 않지만 이것을 깨닫는 데 며칠이 걸렸습니다 (코드는 너무 간단하여 단위 테스트를 수행하지 않았습니다). 캐스트를 사용하거나 0을 사용하는 것이 더 낫습니다.

if ((int)(i-x) > -1 or (i-x) >= 0)

두 가지 방법 모두 작동합니다. 여기 내 테스트 실행입니다

size_t i = 5;
cerr << "i-7=" << i-7 << " (int)(i-7)=" << (int)(i-7) << endl;

출력 : i-7 = 18446744073709551614 (int) (i-7) =-2

다른 사람의 의견을 부탁드립니다.


2
참고 (int)(i - 7)로 캐스팅되는 언더 플로우입니다 int동안, 나중에 int(i) - 7먼저 변환 이후 언더 플로우하지 i로는 int다음 빼기를 7. 또한 귀하의 예가 혼란 스럽다는 것을 알았습니다.
hochl

내 요점은 뺄셈을 할 때 int가 일반적으로 더 안전하다는 것입니다.
Kemin Zhou

4

다양한 라이브러리에서 size_t를 반환하여 해당 컨테이너의 크기가 0이 아님을 나타냅니다. 다시 돌아올 때 사용합니다 : 0

그러나 위의 예제에서 size_t를 반복하는 것은 잠재적 인 버그입니다. 다음을 고려하세요:

for (size_t i = thing.size(); i >= 0; --i) {
  // this will never terminate because size_t is a typedef for
  // unsigned int which can not be negative by definition
  // therefore i will always be >= 0
  printf("the never ending story. la la la la");
}

부호없는 정수를 사용하면 이러한 유형의 미묘한 문제가 발생할 수 있습니다. 따라서 imho는 size_t를 필요로하는 컨테이너 / 유형과 상호 작용할 때만 size_t를 사용하는 것을 선호합니다.


이 버그에 신경 쓰지 않고 루프에서 size_t를 사용하는 것 같습니다. 저는 이것을 어려운 방법으로 배웠습니다
Pranjal Gupta

-2

size_t아키텍처에 대한 최대 정수 값을 보유 할 수있는 부호없는 유형이므로 부호 ( 0x7FFFFFFF1로 증가하면 부호가 -1) 또는 짧은 크기 (부호가없는 부호없는 짧은 정수 0xFFFF는 1 씩 증가) 로 인해 정수 오버플로로부터 보호됩니다 0).

주로 배열 인덱싱 / 루프 / 주소 산술 등에 사용됩니다. 이론적으로 당신은 (32 비트 플랫폼에서) 크기의 메모리 블록을 가질 수 있기 때문에, like memset()와 같은 기능 size_t만 받아들 2^32-1입니다.

이러한 간단한 루프의 경우 방해하지 않고 int 만 사용하십시오.


-3

size_t는 부호없는 정수 유형이며 시스템에서 가장 큰 정수를 나타낼 수 있습니다. 매우 큰 배열, 행렬 등이 필요한 경우에만 사용하십시오.

일부 함수는 size_t를 반환하고 비교를 시도하면 컴파일러에서 경고합니다.

적절한 서명 / 서명되지 않은 데이터 유형을 사용하거나 빠른 해킹을 위해 단순히 유형 변환을 사용하지 마십시오.


4
버그와 보안 허점을 피하고 싶을 때만 사용하십시오.
Craig McQueen

2
실제로 시스템에서 가장 큰 정수를 나타내지 못할 수도 있습니다.
Adrian McCarthy

-4

size_t는 부호없는 int입니다. 따라서 부호없는 int를 원할 때마다 사용할 수 있습니다.

배열의 크기를 지정하고 싶을 때 사용합니다.

void * operator new (size_t size); is a good use of it.

10
실제로 그것은 부호없는 int와 반드시 같을 필요는 없습니다. 그것은 이다 부호하지만 클 수 있습니다 (또는 나는 이것이 사실 어떤 플랫폼 모르겠어요 비록 작은 생각)하는 int보다.
Todd Gamblin

예를 들어, 64 비트 시스템 size_t에서는 부호없는 64 비트 정수일 수 있지만 32 비트 시스템에서는 부호없는 32 비트 정수일 수 있습니다.
HerpDerpington
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.