일부 컴퓨터에서 long int가 12 바이트를 사용하는 이유는 무엇입니까?


26

내 컴퓨터 에서이 코드를 컴파일 한 후 이상한 것을 발견했습니다.

#include <stdio.h>

int main()
{
    printf("Hello, World!\n");

    int a,b,c,d;

    int e,f,g;

    long int h;

    printf("The addresses are:\n %0x \n %0x \n %0x \n %0x \n %0x \n %0x \n %0x \n %0x",
        &a,&b,&c,&d,&e,&f,&g,&h);

    return 0;
}

결과는 다음과 같습니다. 모든 int 주소 사이에는 4 바이트의 차이가 있습니다. 그러나 마지막 int와 long int 사이에는 12 바이트 차이가 있습니다.

 Hello, World!
 The addresses are:

 da54dcac 
 da54dca8 
 da54dca4 
 da54dca0 
 da54dc9c 
 da54dc98 
 da54dc94 
 da54dc88

3
또 다른 넣어 inth소스 코드입니다. 컴파일러는 이전에 공백에 넣을 수 있습니다 h.
ctrl-alt-delor

32
크기를 결정하기 위해 메모리 주소의 차이를 사용하지 마십시오. 그 sizeof기능이 있습니다. printf("size: %d ", sizeof(long));
Chris Schneider

10
을 사용하여 주소의 하위 4 바이트 만 인쇄합니다 %x. 운이 좋으면 플랫폼에서 올바르게 작동하여 형식 문자열이 예상되는 포인터 인수를 전달 unsigned int하지만 포인터와 int의 크기는 많은 ABI에서 다릅니다. %p휴대용 코드로 포인터를 인쇄하는 데 사용 합니다. (코드가 8 개 모두의 절반이 아닌 처음 4 개의 포인터의 상하 절반을 인쇄하는 시스템을 상상하기 쉽습니다.)
Peter Cordes


2
@luu는 잘못된 정보를 퍼 뜨리지 않습니다. 괜찮은 컴파일러는 C에서 변수가 선언되는 순서를 신경 쓰지 않습니다. 신경 쓰이면 설명하는 방식으로 변수를 수행 할 이유가 없습니다.
gnasher729

답변:


81

12 바이트는 걸리지 않았지만 8 개만 걸렸습니다. 그러나이 플랫폼에서 8 바이트 길이 int 의 기본 정렬 은 8 바이트입니다. 따라서 컴파일러는 long int를 8로 나눌 수있는 주소로 이동해야했습니다. "분명한"주소 da54dc8c는 8 바이트로 나눌 수 없으므로 12 바이트 간격이 아닙니다.

이것을 테스트 할 수 있어야합니다. long 전에 다른 int를 추가하여 8 개가 있으면 long int가 이동하지 않고 정렬됩니다. 이제 이전 주소에서 8 바이트 밖에되지 않습니다.

이 테스트가 작동하더라도 이러한 방식으로 구성되는 변수에 의존해서는 안된다는 점을 지적 할 가치가 있습니다. AC 컴파일러는 변수 정렬 순서를 포함하여 프로그램을 신속하게 실행하기 위해 모든 종류의 펑키 작업을 수행 할 수 있습니다 (일부 경고 사항 포함).


3
차이가 아닌 차이.
중복 제거기

10
"재주문 변수 포함". 컴파일러가 동시에 두 개의 변수를 사용하지 않기로 결정하면, 변수도 부분적으로 겹치거나 완전히 겹칠 수 있습니다 ...
Roger Lipscombe

8
또는 실제로 스택 대신 레지스터에 유지하십시오.
Harming Monica 중지

11
@OrangeDog 나는이 경우와 같이 주소를 가져 가면 일어날 것이라고 생각하지 않지만 일반적으로 당신은 물론 정확합니다.
Alex

5
@ 알렉스 : 당신은 주소를 취할 때 메모리와 레지스터로 재미있는 것들을 얻을 수 있습니다. 주소를 취한다는 것은 메모리 위치를 제공해야한다는 것을 의미하지만 실제로 사용하지는 않습니다. 주소를 가져 와서 3을 할당하고 다른 함수에 전달하면 3을 RDI에 쓰고 호출하여 메모리에 쓰지 않을 수 있습니다. 때때로 디버거에서 놀랍습니다.
Zan Lynx

9

컴파일러가 변수 사이에 추가 패딩을 생성하여 메모리에 올바르게 정렬되도록하기 때문입니다.

대부분의 최신 프로세서에서 값에 여러 크기의 주소가 있으면 값에 액세스하는 것이 더 효율적입니다. 이 두었다면 h가능한 첫 번째 자리에, 그 주소는 8의 배수가 아닌, 그래서 사용에 덜 효율적이었을 0xda54dc8c했을 것이다. 컴파일러는 이것에 대해 알고 있으며 마지막 두 변수 사이에 약간의 사용되지 않은 공간을 추가하여 발생하는지 확인합니다.


설명 주셔서 감사합니다. 여러 크기의 변수에 액세스하는 것이 더 효율적인 이유에 관한 자료를 알려 주시겠습니까? 왜 이런 일이 일어나고 있는지 알고 싶습니다.
yoyo_fun

4
@yoyo_fun 그리고 당신이 정말로 기억을 이해하고 싶다면, futuretech.blinkenlights.nl/misc/cpumemory.pdf
Alex

1
@yoyo_fun 아주 간단합니다. 일부 메모리 컨트롤러는 프로세서 비트 폭의 배수에만 액세스 할 수 있습니다 (예 : 32 비트 프로세서는 0-3, 4-7, 8-11 등의 주소 만 직접 요청할 수 있음). 정렬되지 않은 주소를 요청하면 프로세서는 두 개의 메모리 요청을 한 다음 데이터를 레지스터로 가져와야합니다. 따라서 32 비트로 돌아가서 주소 1에 저장된 값을 원하면 프로세서는 주소 0-3, 4-7을 요청한 다음 1, 2, 3 및 4에서 바이트를 가져와야합니다. 메모리 읽기가 낭비되었습니다.
phyrfox

2
사소한 점이지만 잘못 정렬 된 메모리 액세스는 성능 저하 대신 복구 할 수없는 결함 일 수 있습니다. 아키텍처에 따라 다릅니다.
존 체스터 필드

1
@JonChesterfield-예. 그렇기 때문에 제가 제공 한 설명은 대부분의 최신 아키텍처 (주로 x86과 ARM을 의미 함)에 적용됩니다. 다른 방식으로 행동하는 다른 사람들도 있지만 덜 일반적입니다. (흥미롭게 : ARM은 사용 액세스를 정렬 필요한 구조의 하나로하지만 나중에 수정에 정렬되지 않은 액세스의 자동 처리를 추가)

2

이 지역 변수의 주소를 서로 관련시키는 데 언어가 필요하지 않기 때문에 테스트는 반드시 생각한 것을 테스트하지는 않습니다.

스토리지 할당에 대해 무언가를 추론하려면 구조체에 필드로 필드를 넣어야합니다.

지역 변수는 특정 방식으로 서로 옆에 스토리지를 공유 할 필요가 없습니다. 예를 들어, 컴파일러는 스택 내의 임의의 위치에 임시 변수를 삽입 할 수 있습니다 (예 :이 두 로컬 변수 사이에있을 수 있음).

반대로, 임시 변수를 구조체에 삽입 할 수 없으므로 구조체 필드의 주소를 대신 인쇄하면 동일한 논리적 메모리 척 (구조)에서 할당 된 항목을 비교하게됩니다.

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