Program Tip

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

programtip 2020. 10. 4. 13:12
반응형

"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

이것은 왜 segfault입니까?

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

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

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

In either case (assign multiple values to a pointer or an int variable), what happens then is that the variable will get the first value which is 5, while remaining values are ignored. This code complies but you would get warnings for each additional value that is not supposed to be in the assignment:

warning: excess elements in scalar initializer.

For the case of assigning multiple values to pointer variable, the program segfaults when you access nums[0], which means you are deferencing whatever is stored in address 5 literally. You did not allocate any valid memory for pointer nums in this case.

It'd be worth noting that there is no segfault for the case of assigning multiple values to int variable (you are not dereferencing any invalid pointer here).


SCENARIO 2

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

This one does not segfault, because you are legally allocating an array of 4 ints in the stack.


SCENARIO 3

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

This one does not segfault as expected, because you are printing the value of the pointer itself - NOT what it's dereferencing (which is invalid memory access).


Others

It's almost always doomed to segfault whenever you hardcode the value of a pointer like this (because it is the operating system task to determine what process can access what memory location).

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

So a rule of thumb is to always initialize a pointer to the address of some allocated variable, such as:

int a;
int *nums = &a;

or,

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

int *nums = {5, 2, 1, 4}; is ill-formed code. There is a GCC extension which treats this code the same as:

int *nums = (int *)5;

attempting to form a pointer to memory address 5. (This doesn't seem like a useful extension to me, but I guess the developer base wants it).

To avoid this behaviour (or at least, get a warning) you could compile in standard mode, e.g. -std=c11 -pedantic.

An alternative form of valid code would be:

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

which points at a mutable literal of the same storage duration as nums. However , the int nums[] version is generally better as it uses less storage, and you can use sizeof to detect how long the array is.


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

nums is a pointer of type int. So you should make this point to some valid memory location. num[0] you are trying to dereference some random memory location and hence the segmentation fault.

Yes the pointer is holding value 5 and you are trying to dereference it which is undefined behavior on your system. (Looks like 5 is not a valid memory location on your system)

Whereas

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

is a valid declaration where you are saying nums is an array of type int and memory is allocated based on number of elements passed during initialization.


By assigning {5, 2, 1, 4}

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

you are assigning 5 to nums (after an implicit typecast from int to pointer to int). Dereferring it makes an access call to memory location at 0x5. That might not be allowed for your program to access.

Try

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

참고URL : https://stackoverflow.com/questions/35266987/int-nums-5-2-1-4-causes-a-segmentation-fault

반응형