[c] “int * nums = {5, 2, 1, 4}”로 인해 세그멘테이션 오류가 발생합니다.

int *nums = {5, 2, 1, 4};
printf("%d\n", nums[0]);

세그 폴트를 일으키는 반면

int nums[] = {5, 2, 1, 4};
printf("%d\n", nums[0]);

하지 않습니다. 지금:

int *nums = {5, 2, 1, 4};
printf("%d\n", nums);

인쇄 5.

이를 바탕으로 저는 배열 초기화 표기법 {}이이 데이터를 왼쪽에있는 변수에 맹목적으로로드한다고 추측했습니다. int []이면 배열이 원하는대로 채워집니다. int * 일 때 포인터는 5로 채워지고 포인터가 저장된 이후의 메모리 위치는 2, 1, 4로 채워집니다. 따라서 nums [0]은 5를 deref하려고 시도하여 segfault를 발생시킵니다.

내가 틀렸다면 정정하십시오. 그리고 내가 맞다면 자세히 설명해주세요. 왜 배열 이니셜 라이저가 작동하는 방식을 이해하지 못하기 때문입니다.



답변

C에는 일반 변수가 마치 배열 인 것처럼 중괄호로 묶인 이니셜 라이저 목록으로 초기화 될 수 있다는 (멍청한) 규칙이 있습니다.

예를 들어 int x = {0};, int x = 0;.

따라서 작성할 때 int *nums = {5, 2, 1, 4};실제로 단일 포인터 변수에 이니셜 라이저 목록을 제공합니다. 그러나 그것은 하나의 단일 변수 일뿐이므로 첫 번째 값 5 만 할당되고 나머지 목록은 무시됩니다 (실제로 과도한 이니셜 라이저가있는 코드는 엄격한 컴파일러로 컴파일해야한다고 생각하지 않습니다)-그렇지 않습니다 전혀 기억에 기록됩니다. 코드는 int *nums = 5;. 즉, 주소를nums 가리켜 야 합니다 5 .

이 시점에서 이미 두 개의 컴파일러 경고 / 오류가 발생했을 것입니다.

  • 캐스트없이 포인터에 정수 할당.
  • 이니셜 라이저 목록에 초과 요소가 있습니다.

그리고 물론은 5역 참조가 허용되는 유효한 주소가 아닐 가능성이 높기 때문에 코드가 충돌하고 타 버릴 것입니다 nums[0].

참고로, 지정자를 printf사용하여 주소를 포인터로 지정 해야 %p합니다. 그렇지 않으면 정의되지 않은 동작을 호출합니다.


여기서 무엇을 하려는지 잘 모르겠지만 포인터가 배열을 가리 키도록 설정하려면 다음을 수행해야합니다.

int nums[] = {5, 2, 1, 4};
int* ptr = nums;

// or equivalent:
int* ptr = (int[]){5, 2, 1, 4};

또는 포인터 배열을 생성하려는 경우 :

int* ptr[] = { /* whatever makes sense here */ };

편집하다

몇 가지 조사 후 “과도한 요소 이니셜 라이저 목록”이 실제로 유효한 C가 아니라고 말할 수 있습니다 . 이것은 GCC 확장 입니다.

표준 6.7.9 초기화 는 다음과 같이 말합니다.

2 이니셜 라이저는 초기화되는 엔티티에 포함되지 않은 객체에 대한 값을 제공하려고 시도해서는 안됩니다.

/-/

11 스칼라의 이니셜 라이저는 선택적으로 중괄호로 묶인 단일 표현식이어야합니다. 객체의 초기 값은 변환 후 표현식의 값입니다. 단순 할당과 동일한 유형 제약 및 변환이 적용되며 스칼라 유형을 선언 된 유형의 정규화되지 않은 버전으로 취합니다.

“스칼라 유형”은 배열, 구조체 또는 공용체 유형 ( “집계 유형”이라고 함)이 아닌 단일 변수를 나타내는 표준 용어입니다.

따라서 일반 영어에서 표준은 “변수를 초기화 할 때 가능하기 때문에 이니셜 라이저 표현식 주위에 추가 중괄호를 넣어도됩니다.”라고 말합니다.


답변

시나리오 1

int *nums = {5, 2, 1, 4};    // <-- assign multiple values to a pointer variable
printf("%d\n", nums[0]);    // segfault

이것은 왜 세그 폴트입니까?

당신은 numsint에 대한 포인터로 선언 했습니다 . 이것은 메모리 nums하나의 정수 주소를 가지고 있어야합니다 .

그런 다음 여러nums의 배열 로 초기화하려고했습니다 . 따라서 많은 세부 사항을 파헤 치지 않으면 개념적으로 잘못된 것입니다. 하나의 값을 보유해야하는 변수에 여러 값을 할당하는 것은 의미가 없습니다. 이와 관련하여 이렇게하면 정확히 동일한 효과를 볼 수 있습니다.

int nums = {5, 2, 1, 4};    // <-- assign multiple values to an int variable
printf("%d\n", nums);    // also print 5

두 경우 모두 (포인터 또는 int 변수에 여러 값 할당) 그러면 변수가 첫 번째 값 5인를 가져 오지만 나머지 값은 무시됩니다. 이 코드는 준수하지만 할당에 포함되지 않아야하는 각 추가 값에 대해 경고를받습니다.

warning: excess elements in scalar initializer.

포인터 변수에 여러 값을 할당하는 경우에, 프로그램 세그먼테이션 폴트 (segfault) 당신은에 액세스 할 때 nums[0],에 저장되어 당신이 무엇을 deferencing하는 수단 주소 (5) 문자를. nums이 경우 포인터 에 유효한 메모리를 할당하지 않았습니다 .

int 변수에 여러 값을 할당하는 경우에는 segfault가 없다는 점에 주목할 가치가 있습니다 (여기서는 잘못된 포인터를 역 참조하지 않습니다).


시나리오 2

int nums[] = {5, 2, 1, 4};

이것은 합법적으로 스택에 4 개의 int 배열을 할당하고 있기 때문에 segfault가 아닙니다.


시나리오 3

int *nums = {5, 2, 1, 4};
printf("%d\n", nums);   // print 5

이것은 포인터 자체의 값을 인쇄 하고 있기 때문에 예상대로 segfault가 아닙니다 -참조가 아닌 (잘못된 메모리 액세스) 포인터 자체의 값을 인쇄하기 때문 입니다.


기타

이와 같은 포인터의 값하드 코딩 할 때마다 거의 항상 segfault가 운명적입니다 (어떤 프로세스가 어떤 메모리 위치에 액세스 할 수 있는지 결정하는 것이 운영 체제 작업이기 때문입니다).

int *nums = 5;    // <-- segfault

따라서 경험상의 규칙은 다음과 같이 할당 된 변수 의 주소에 대한 포인터를 항상 초기화하는 것입니다.

int a;
int *nums = &a;

또는,

int a[] = {5, 2, 1, 4};
int *nums = a;


답변

int *nums = {5, 2, 1, 4};형식이 잘못된 코드입니다. 이 코드를 다음과 같이 처리하는 GCC 확장이 있습니다.

int *nums = (int *)5;

메모리 주소 5에 대한 포인터를 형성하려고 시도합니다. (이것은 나에게 유용한 확장처럼 보이지 않지만 개발자 기반이 원하는 것 같습니다).

이 동작을 피하려면 (또는 최소한 경고를 받으려면) 표준 모드에서 컴파일 할 수 있습니다 (예 : -std=c11 -pedantic.

유효한 코드의 대체 형식은 다음과 같습니다.

int *nums = (int[]){5, 2, 1, 4};

와 동일한 저장 기간의 변경 가능한 리터럴을 가리 킵니다 nums. 그러나 int nums[]스토리지를 덜 사용하므로 일반적으로 버전이 더 좋으며 sizeof어레이의 길이를 감지하는 데 사용할 수 있습니다 .


답변

int *nums = {5, 2, 1, 4};

nums유형의 포인터입니다 int. 따라서이 지점을 유효한 메모리 위치로 지정해야합니다. num[0]임의의 메모리 위치를 역 참조하려고하므로 분할 오류가 발생합니다.

예, 포인터는 값 5를 보유하고 있으며 시스템에서 정의되지 않은 동작을 역 참조하려고합니다. ( 5은 시스템에서 유효한 메모리 위치가 아닌 것 같습니다. )

이므로

int nums[] = {1,2,3,4};

nums유형의 배열이고 int초기화 중에 전달 된 요소 수에 따라 메모리가 할당 되는 유효한 선언 입니다.


답변

할당함으로써 {5, 2, 1, 4}

int *nums = {5, 2, 1, 4};

당신은 5를 할당하고 있습니다 nums(int에서 int에 대한 포인터로 암시 적 유형 변환 후). 그것을 연기하면에서 메모리 위치에 대한 액세스 호출이 생성됩니다 0x5. 귀하의 프로그램에서 액세스가 허용되지 않을 수 있습니다.

시험

printf("%p", (void *)nums);


답변