C 포인터를 NULL로 초기화 할 수 있습니까?


90

나는 다음과 같은 것을 쓰고 있었다

char *x=NULL;

가정에

 char *x=2;

char주소 2에 대한 포인터를 생성합니다 .

그러나 GNU C 프로그래밍 튜토리얼 에서는 할당 될 때 임의의 주소에 int *my_int_ptr = 2;정수 값 2을 저장 한다고 말합니다 my_int_ptr.

이것은 내 자신 char *x=NULLNULLa에 대한 캐스트 값이 char메모리의 임의의 주소에 할당 된다는 것을 의미하는 것 같습니다 .

동안

#include <stdlib.h>
#include <stdio.h>

int main()
{
    char *x=NULL;

    if (x==NULL)
        printf("is NULL\n");

    return EXIT_SUCCESS;
}

실제로 인쇄합니다.

NULL

컴파일하고 실행할 때 정의되지 않은 동작 또는 최소한 지정되지 않은 동작에 의존하고 있으며

char *x;
x=NULL;

대신.


72
무엇을 사이에 매우 혼란 차이가있다 int *x = whatever;수행하고 무엇을 int *x; *x = whatever;하지가. int *x = whatever;실제로 int *x; x = whatever;, 아닌 *x = whatever;.
user2357112 모니카 지원

78
이 튜토리얼은 혼란스러운 구별을 잘못한 것으로 보입니다.
user2357112 모니카 지원

51
웹에 너무 많은 엉뚱한 튜토리얼! 즉시 읽기를 중지하십시오. 우리는 정말 우리가 할 수있는 공개적으로 수치 엉터리 책 ... 소위 블랙리스트가 필요
룬딘

9
@MM 2017 년에는 그다지 엉망진창이 아닙니다. 80 년대 이후 컴파일러와 컴퓨터의 진화를 감안할 때, 기본적으로 제가 의사 였고 18 세기에 쓰여진 의학 서적을 읽는 것과 같습니다.
Lundin

13
이 튜토리얼이 " The GNU C Programming Tutorial"에 해당 하지 않는다고 생각합니다 ...
marcelm

답변:


114

C 포인터를 NULL로 초기화 할 수 있습니까?

TL; DR 예, 대단히.


가이드 이루어 실제 특징은 같은 판독

반면에 단일 초기 할당 만 사용하는 경우 int *my_int_ptr = 2;프로그램은 가리키는 메모리 위치의 내용을 my_int_ptr값 2 로 채우려 고 시도 합니다. my_int_ptr가비지로 채워 지므로 모든 주소가 될 수 있습니다. [...]

글쎄, 그들은 수 있습니다 당신이 옳다 잘못.

명령문의 경우 ( 지금은 정수 변환에 대한 포인터가 구현 정의 동작이라는 사실 무시 )

int * my_int_ptr = 2;

my_int_ptr변수는 (형 포인터의 것입니다 int(: 정수에 대한 포인터의 주소 형), 당신은의 값이 저장되어있다), 그것의 자신의 주소가 2 주소를.

이제 my_int_ptr포인터 유형이므로 다음을 가리킬 수 있습니다. 메모리 위치에서 "유형"의 값 에 의해 지적 되고있는 값 my_int_ptr. 따라서 기본적으로 포인터가 가리키는 메모리 위치의 값이 아니라 포인터 변수 의 값 할당합니다 .

그래서 결론을 위해

 char *x=NULL;

포인터 변수를 초기화합니다. x 가리키는 메모리 주소NULL값이 아닌 를로 .

이건 똑같아

 char *x;
 x = NULL;    

확장:

이제 엄격하게 준수하는 것은

 int * my_int_ptr = 2;

제약 위반을 포함하므로 불법입니다. 확실하게,

  • my_int_ptr 포인터 변수, 유형 int *
  • 정수 상수, 2유형 있음int 정의에 따라 .

"호환"유형이 아니므로이 초기화는 §6.5.16.1 / P1 장에 설명 된 단순 할당 규칙을 위반하므로 유효하지 않습니다. Lundin의 대답에 .

초기화가 간단한 할당 제약 조건에 어떻게 연결되는지 관심이있는 사람은 인용 C11, §6.7.9, P11 장

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


Random832n @ 그들은 있습니다 잘못. 내 답변에서 관련 부분을 인용했습니다. 그렇지 않으면 수정하십시오. 아, 그리고 의도적 인 강조.
Sourav Ghosh

"...은 제약 조건 위반을 포함하므로 불법입니다. ... 정수 리터럴, 2는 정의상 int 유형을가집니다." 문제가 있습니다. 2는 이므로 int할당이 문제인 것 같습니다. 하지만 그 이상입니다. NULL또한 수있다 int,이 int 0. 그것은 char *x = 0;잘 정의되어 char *x = 2;있고 그렇지 않습니다. 6.3.2.3 포인터 3 (BTW : C가 정의하지 않는 정수 리터럴문자열 리터럴화합물은 그대로 . 0정수 상수 )
chux - 분석 재개 모니카

@chux 당신은 매우 정확하지만 char *x = (void *)0;준수하는 것이 아닌가요? 아니면 값을 산출하는 다른 표현들과 함께 0있습니까?
Sourav Ghosh

10
@SouravGhosh : 값 0이있는 정수 상수 는 특별합니다. 일반 정수 표현식을 포인터 유형으로 명시 적으로 캐스팅하는 일반적인 규칙과는 별도로 널 포인터로 암시 적으로 변환합니다.
Steve Jessop

1
1974 C Reference Manual에 설명 된 언어는 선언이 초기화 표현식을 지정하는 것을 허용하지 않았으며 이러한 표현식이 없기 때문에 "선언 미러 사용"이 훨씬 더 실용적입니다. 구문 int *p = somePtrExpression은 IMHO 값을 설정하는 것처럼 보이지만 *p실제로 값을 설정하고 있기 때문에 다소 끔찍합니다 p.
supercat

53

튜토리얼이 잘못되었습니다. ISO C에서는 int *my_int_ptr = 2;오류입니다. GNU C에서는 int *my_int_ptr = (int *)2;. 이것은 2컴파일러가 결정하는 방식으로 정수 를 메모리 주소 로 변환합니다 .

해당 주소 (있는 경우)로 주소가 지정된 위치에 아무것도 저장하지 않습니다. 계속해서을 쓰면 해당 주소로 주소가 지정된 위치에 *my_int_ptr = 5;번호를 저장하려고 5합니다.


1
정수에서 포인터로의 변환이 구현 정의라는 것을 몰랐습니다. 정보 주셔서 감사합니다.
taskinoor apr

1
@taskinoor이 답변과 같이 캐스트로 강제하는 경우에만 변환이 있음을 유의하십시오. 캐스트가 아니라면 코드가 컴파일되지 않아야합니다.
Lundin

2
@taskinoor : 예, C의 다양한 변환은 매우 혼란 스럽습니다. 이 Q에는 변환에 대한 흥미로운 정보가 있습니다. C : 정의되지 않은 동작이 아닌 포인터 유형 간 캐스팅은 언제입니까? .
sleske

17

튜토리얼이 잘못된 이유를 명확히하기 위해 int *my_int_ptr = 2;"제약 조건 위반"인 것은 컴파일이 허용되지 않는 코드이며 컴파일러는이를 발견하면 진단을 제공해야합니다.

6.5.16.1에 따라 단순 할당 :

제약

다음 중 하나가 유지됩니다.

  • 왼쪽 피연산자는 원자, 규정 또는 규정되지 않은 산술 유형을 가지며 오른쪽 피연산자는 산술 유형을가집니다.
  • 왼쪽 피연산자는 오른쪽 유형과 호환되는 구조 또는 공용체 유형의 원자, 규정 또는 규정되지 않은 버전을 가지고 있습니다.
  • 왼쪽 피연산자는 원자, 정규화 또는 정규화되지 않은 포인터 유형을 가지며 (왼쪽 피연산자가 lvalue 변환 후 가질 유형을 고려할 때) 두 피연산자는 모두 호환되는 유형의 정규화되거나 정규화되지 않은 버전에 대한 포인터이며 왼쪽이 가리키는 유형은 모두 오른쪽으로 가리키는 유형의 한정자;
  • 왼쪽 피연산자는 원자, 정규화 또는 정규화되지 않은 포인터 유형을 가지며 (왼쪽 피연산자가 lvalue 변환 후 가질 유형을 고려할 때) 한 피연산자는 개체 유형에 대한 포인터이고 다른 피연산자는 정규화 된 또는 정규화되지 않은 버전에 대한 포인터입니다. void, 그리고 왼쪽이 가리키는 유형은 오른쪽이 가리키는 유형의 모든 한정자를가집니다.
  • 왼쪽 피연산자는 원 자성, 규정 된 또는 규정되지 않은 포인터이고 오른쪽은 널 포인터 상수입니다. 또는
  • 왼쪽 피연산자는 원자, 정규화 또는 정규화되지 않은 _Bool 유형을 가지며 오른쪽은 포인터입니다.

이 경우 왼쪽 피연산자는 규정되지 않은 포인터입니다. 오른쪽 피연산자가 정수 (산술 유형)가 될 수 있다는 내용은 어디에도 없습니다. 따라서 코드는 C 표준을 위반합니다.

GCC는 표준 C 컴파일러라고 명시 적으로 지정하지 않는 한 제대로 작동하지 않는 것으로 알려져 있습니다. 코드를으로 컴파일 -std=c11 -pedantic-errors하면 올바르게 진단을 제공합니다.


4
-pedantic-errors 제안에 대해 찬성했습니다. 관련 -Wpedantic을 사용할 가능성이 있지만.
fagricipni

2
오른쪽 피연산자가 정수가 될 수 없다는 진술의 한 가지 예외 : 섹션 6.3.2.3은 "값이 0 인 정수 상수 표현식 또는 유형으로 캐스트 된 표현식을 void *널 포인터 상수라고합니다."라고 말합니다. 견적서에서 마지막에서 두 번째 글 머리 기호를 확인하십시오. 따라서을 int* p = 0;작성하는 합법적 인 방법 int* p = NULL;입니다. 후자는 더 명확하고 더 전통적이지만.
Davislor apr

1
병적 난독 화도 int m = 1, n = 2 * 2, * p = 1 - 1, q = 2 - 1;합법적으로 만듭니다 .
Davislor

이 답변의 표준 인용문에서 글 머리 기호 5로 덮여있는 @Davislor (나중에 요약에서 언급해야한다는 데 동의)
MM

1
@chux 잘 구성된 프로그램은를 intptr_t오른쪽에 허용 된 유형 중 하나로 명시 적으로 변환해야한다고 생각합니다 . 즉, void* a = (void*)(intptr_t)b;포인트 4에서 유효하지만 (intptr_t)b호환되는 포인터 유형도 void*아니고이거나 널 포인터 상수도 void* a아니며 산술 유형도 아니고 _Bool. 표준은 변환이 합법적이라고 말하지만 암시적인 것은 아닙니다.
Davislor 2017-04-12

15

int *my_int_ptr = 2

할당 될 때 my_int_ptr에있는 임의의 주소에 정수 값 2를 저장합니다.

이것은 완전히 잘못된 것입니다. 이것이 실제로 쓰여졌다면 더 나은 책이나 튜토리얼을 얻으십시오.

int *my_int_ptr = 2주소 2를 가리키는 정수 포인터를 정의합니다 2. 주소에 액세스하려고하면 충돌이 발생할 가능성이 큽니다 .

*my_int_ptr = 2int, 줄에가 없으면 임의의 주소 my_int_ptr가 가리키는 임의의 주소에 값 2를 저장합니다 . 이렇게 말하면 NULL포인터가 정의되면 할당 할 수 있습니다 . char *x=NULL;완벽하게 유효한 C입니다.

편집 : 이것을 작성하는 동안 정수에서 포인터로의 변환이 구현 정의 동작이라는 것을 몰랐습니다. 자세한 내용은 @MM 및 @SouravGhosh의 좋은 답변을 참조하십시오.


1
다른 이유가 아니라 제약 위반이기 때문에 완전히 잘못되었습니다. 특히 이것은 올바르지 않습니다. "int * my_int_ptr = 2는 주소 2를 가리키는 정수 포인터를 정의합니다".
Lundin

@Lundin : "다른 이유로아님" 이라는 문구 자체가 잘못되고 오해의 소지가 있습니다. 형식 호환성 문제를 해결하더라도 자습서 작성자가 포인터 초기화 및 할당이 작동하는 방식을 잘못 설명하고 있다는 사실이 여전히 남아 있습니다.
궤도의 가벼운 경주

14

C 포인터에 대한 많은 혼동은 원래 코딩 스타일과 관련하여 만들어진 매우 나쁜 선택에서 비롯되며 언어 구문에서 매우 나쁜 선택으로 입증되었습니다.

int *x = NULL;C는 맞지만, 그것은 매우 오해의 소지가 있고, 심지어 무의미하다고 말할 수도 있고, 많은 초심자에게 언어 이해를 방해했습니다. 그것은 *x = NULL;당연히 불가능한 일을 나중에 우리가 할 수 있다고 생각하게 만듭니다 . 당신은 변수의 유형이 아닌, 참조 int, 변수의 이름은하지 *x않으며, 수행 *선언에가와 공동으로 기능적 역할을한다 =. 순전히 선언적입니다. 따라서 훨씬 더 의미있는 것은 다음과 같습니다.

int* x = NULL;원래 K & R 코딩 스타일을 고수하지는 않지만 올바른 C입니다. 유형이 int*이고 포인터 변수 가라는 것을 완벽하게 명확하게 x하므로 값 NULL이에 x대한 포인터 인 에 저장되고 있음을 시작하지 않은 사람에게도 분명하게 알 수 int있습니다.

또한 규칙을 쉽게 유도 할 수 있습니다. 별이 변수 이름에서 멀어지면 선언이되고 이름에 붙은 별은 포인터 역 참조입니다.

그래서, 지금은 더 우리가 중 할 수있는 다운 것을 더 많이 이해하게 x = NULL;또는 *x = 2;그것을 쉽게 초보자 방법을 참조 할 수있게 즉 variable = expression에 리드를 pointer-type variable = pointer-expression하고 dereferenced-pointer-variable = expression. (시작된 경우 '표현식'은 'rvalue'를 의미합니다.)

언어 구문에서 불행한 선택은 지역 변수를 선언 할 때 int i, *p;정수와 정수에 대한 포인터를 선언하는 것을 말할 수 있으므로 *이름의 유용한 부분 이라고 믿을 수 있다는 것 입니다. 그러나 그렇지 않습니다.이 구문은 편의를 위해 추가 된 기발한 특수 사례 일뿐입니다. 제 생각에는 위에서 제안한 규칙을 무효화하기 때문에 존재하지 않았어야했습니다. 내가 아는 한, 언어의 다른 곳에서는이 구문이 의미가 없지만 그렇다고하더라도 C에서 포인터 유형이 정의되는 방식에 차이가 있음을 나타냅니다. 다른 모든 곳, 단일 변수 선언, 매개 변수 목록, 구조체 멤버 등에서 type* pointer-variable대신 포인터를 선언 할 수 있습니다 type *pointer-variable. 그것은 완벽하게 합법적이고 더 합리적입니다.


int *x = NULL; is correct C, but it is very misleading, I would even say nonsensical,... 동의하지 않으면 동의해야합니다. It makes one think.... 생각을 그만두고 C 책을 먼저 읽으십시오.
Sourav Ghosh

^^ 이것은 나에게 완벽하게 이해되었을 것입니다. 그래서 주관적이라고 생각합니다.
Mike Nakis 2017-04-12

5
@SouravGhosh 의견으로는 C int* somePtr, someotherPtr 두 개의 포인터 를 선언 하도록 설계 되어야 한다고 생각합니다. 사실 저는 글을 쓰곤 int* somePtr했지만 설명하는 버그로 이어집니다.
fagricipni

1
@fagricipni이 때문에 다중 변수 선언 구문 사용을 중단했습니다. 내 변수를 하나씩 선언합니다. 정말 같은 줄에 두려면 쉼표가 아닌 세미콜론으로 구분합니다. "장소가 나쁘면 그곳에 가지마."
Mike Nakis 2017

2
나는 처음부터 리눅스를 디자인 한 수 있다면 @fagricipni 음, 내가 사용하는 것 create대신에 creat. :) 요점은 그것이 어떻게되어 있고 우리는 그것에 적응하기 위해 스스로를 틀어 야한다는 것입니다. 모든 것은 결국 개인의 선택으로 귀결됩니다. 동의합니다.
Sourav Ghosh

6

많은 훌륭한 답변에 직교하는 것을 추가하고 싶습니다. 실제로로 초기화하는 NULL것은 나쁜 습관과는 거리가 멀고 포인터가 동적으로 할당 된 메모리 블록을 저장하는 데 사용되거나 사용되지 않는 경우 유용 할 수 있습니다.

int * p = NULL;
...
if (...) {
    p = (int*) malloc(...);
    ...
}
...
free(p);

받는 사람에 따라 이후 ISO-IEC 9899 표준 free 인수가 때 NOP입니다 NULL(같은 라인을 따라 더 의미 또는 무언가) 위의 코드는 합법적이다.


5
C 코드도 C ++로 컴파일되어야하는 경우가 아니면 C에서 malloc의 결과를 캐스팅하는 것은 중복됩니다.
cat

당신이 옳습니다, void*필요에 따라 변환됩니다. 그러나 C 및 C ++ 컴파일러와 함께 작동하는 코드가 있으면 이점이있을 수 있습니다.
Luca Citi

1
@LucaCiti C와 C ++는 서로 다른 언어입니다. 다른 하나를 위해 설계된 컴파일러를 사용하여 하나를 위해 작성된 소스 파일을 컴파일하려고 할 때만 오류가 발생합니다. 파스칼 도구를 사용하여 컴파일 할 수있는 C 코드를 작성하려는 것과 같습니다.
Evil Dog Pie

1
좋은 조언. 나는 항상 포인터 상수를 무언가로 초기화하려고 시도합니다. 현대 C에서 이것은 일반적으로 최종 값이 될 수 있으며 medias res에서const 선언 포인터 가 될 수 있지만 포인터 가 변경 가능해야 할 때 (루프 또는 by에 사용 된 것과 같이 realloc()), NULL이전에 사용 된 버그 를 잡도록 설정합니다. 실제 가치로 설정됩니다. 대부분의 시스템에서 역 참조 NULL는 실패 지점에서 segfault를 발생시키는 반면 (예외가 있지만) 초기화되지 않은 포인터는 가비지를 포함하고 여기에 쓰면 임의의 메모리가 손상됩니다.
Davislor

1
또한 디버거에서 포인터에 NULL. 따라서 NULL선언 시점부터 모든 포인터가 항상 유효하거나 또는임을 확인하는 것이 도움이됩니다 .
Davislor


1

맞습니다.

int main()
{
    char * x = NULL;

    if (x==NULL)
        printf("is NULL\n");

    return EXIT_SUCCESS;
}

이 기능은 그것이하는 일에 맞습니다. 문자 포인터 x에 0의 주소를 할당합니다. 즉, 포인터 x가 메모리 주소 0을 가리 킵니다.

대안 :

int main()
{
    char* x = 0;

    if ( !x )
        printf(" x points to NULL\n");

    return EXIT_SUCCESS;
}

당신이 원했던 것에 대한 나의 추측은 :

int main()
{
    char* x = NULL;
    x = alloc( sizeof( char ));
    *x = '2';

    if ( *x == '2' )
        printf(" x points to an address/location that contains a '2' \n");

    return EXIT_SUCCESS;
}

x is the street address of a house. *x examines the contents of that house.

"문자 포인터 x에 0의 주소를 할당합니다." -> 아마도. C는 포인터 의 을 지정하지 않습니다 char* x = 0; if (x == 0). 포인터는 반드시 정수일 필요는 없습니다.
chux - 분석 재개 모니카

그것은 '포인터 x가 메모리 주소 0을 가리 키지'않습니다. 포인터 값 을 0 또는 NULL과 비교하여 테스트 할 수 있는 지정되지 않은 잘못된 값으로 설정합니다 . 실제 작업은 구현에 따라 정의됩니다. 여기에는 실제 질문에 대한 답이 없습니다.
Marquis of Lorne
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.