2 차원 배열 앨리어싱시 예기치 않은 strlen 최적화


28

내 코드는 다음과 같습니다.

#include <string.h>
#include <stdio.h>

typedef char BUF[8];

typedef struct
{
    BUF b[23];
} S;

S s;

int main()
{
    int n;

    memcpy(&s, "1234567812345678", 17);

    n = strlen((char *)&s.b) / sizeof(BUF);
    printf("%d\n", n);

    n = strlen((char *)&s) / sizeof(BUF);
    printf("%d\n", n);
}

를 제외한 모든 최적화 수준에서 gcc 8.3.0 또는 8.2.1을 사용하면 예상했을 때 -O0출력 0 2됩니다 2 2. 컴파일러는 그 결정 strlen에 묶여 b[0]있으므로 동일하거나 의해 분할되는 값을 넘지 않을 수있다.

이것은 내 코드의 버그입니까 아니면 컴파일러의 버그입니까?

이 명확하게 표준 철자,하지만 난 포인터 출처의 주류 해석은 어떤 개체에 대한 것이 었습니다 생각하지 않습니다 X, 코드 (char *)&X의 전체 반복 할 수있는 포인터를 생성해야 X- 경우에도 보유해야이 개념을 X가지고 일을 내부 구조로 하위 배열.

(Bonus 질문,이 특정 최적화를 해제하는 gcc 플래그가 있습니까?)



4
참고 : 내 gcc 7.4.0 2 2은 다양한 옵션으로 보고합니다 .
chux-복원 Monica

2
@Ale 표준은 동일한 주소에 있음을 보증합니다 (구조는 초기 패딩을 가질 수 없음)
MM

3
@ DavidRankin-ReinstateMonica "char (*) [8]의 범위에서 b [0]으로 제한됩니다. 그러나 그것은 내가 아는 한"나는 그것을 못 박는 것 같아요. 이후 s.b에 한정되는 b[0]것이 8 개 문자, 따라서 두 가지로 제한된다 : (1) UB 8 비 - 널 문자있다 아웃 오브 바운드 경우 접속, (2)는 널 문자하는, 거기 len은 8보다 작으므로 8로 나누면 0이됩니다. 그래서 두 경우 모두에 동일한 결과를 제공하기 위해 UB를 사용할 수 있습니다 함께 (1) + (2) 컴파일러를 넣어
user2162550

3
& s == & s.b라고 가정하면 결과가 다를 수있는 방법은 없습니다. @ user2162550에서 알 수 있듯이 strlen ()은 호출되지 않으며 컴파일러는 컴파일러에서 알 수없는 경우 godbolt.org/z/dMcrdy 의 경우에도 결과가 무엇인지 추측 합니다. 컴파일러 버그 입니다.
Ale

답변:


-1

내가 볼 수있는 몇 가지 문제가 있으며 컴파일러가 메모리를 레이아웃하기로 결정한 방법에 영향을받을 수 있습니다.

    n = strlen((char *)&s.b) / sizeof(BUF);
    printf("%d\n", n);

위 코드에서 s.b 에는 8 개의 문자 배열로 구성된 23 개의 항목 배열이 있습니다. 참고로 s.b23 바이트 배열의 첫 번째 항목 (8 문자 배열의 첫 번째 바이트) 주소를 얻습니다. 코드 &s.b에이 표시되면 배열 주소의 주소를 묻는 것입니다. 커버 아래에서 컴파일러는 로컬 스토리지를 생성하여 어레이의 주소를 저장하고 로컬 스토리지의 주소를에 제공합니다 strlen.

두 가지 가능한 솔루션이 있습니다. 그들은:

    n = strlen((char *)s.b) / sizeof(BUF);
    printf("%d\n", n);

또는

    n = strlen((char *)&s.b[0]) / sizeof(BUF);
    printf("%d\n", n);

또한 프로그램을 실행하고 문제를 보여 주려고했지만 clang과 -O옵션 이있는 gcc 버전 모두 예상대로 작동했습니다. 가치있는 것을 위해, x86_64-pc-linux-gnu에서 clang 버전 9.0.0-2 및 gcc 버전 9.2.1을 실행하고 있습니다).


-2

코드에 오류가 있습니다.

 memcpy(&s, "1234567812345678", 17);

예를 들어, s가 b로 시작하더라도 다음과 같이 위험하지만 위험합니다.

 memcpy(&s.b, "1234567812345678", 17);

두 번째 strlen ()에도 오류가 있습니다

n = strlen((char *)&s) / sizeof(BUF);

예를 들어 다음과 같아야합니다.

n = strlen((char *)&s.b) / sizeof(BUF);

문자열 sb는 올바르게 복사 된 경우 17 자 길이 여야합니다. 구조체가 정렬 된 경우 구조체에 메모리가 어떻게 저장되는지 확실하지 않습니다. sb에 실제로 복사 된 17자가 포함되어 있는지 확인 했습니까?

그래서 strlen (sb)는 17을 보여 주어야합니다

printf는 % d가 정수이고 변수 n이 정수로 선언되므로 정수만 표시합니다. sizeof (BUF)는 8이어야합니다

따라서 n을 정수로 선언하면 17을 8 (17/8)로 나눈 값은 2를 인쇄해야합니다. memcpy가 sb가 아닌 s에 데이터를 복사하는 데 사용되었으므로 메모리 정렬과 관련이 있다고 생각합니다. 하나의 메모리 주소에 8자를 사용할 수있는 것보다 64 비트 컴퓨터라고 가정합니다.

예를 들어, 다음 "여유 공간"이 정렬되지 않은 것보다 누군가 malloc (1)을 호출했다고 가정합니다.

두 번째 strlen 호출은 문자열 복사가 sb 대신 s 구조체에 수행되었으므로 올바른 번호를 표시합니다.

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