programing

마주치는 C의 일반적인 정의되지 않은/지정되지 않은 동작은 무엇입니까?

firstcheck 2022. 7. 2. 21:55
반응형

마주치는 C의 일반적인 정의되지 않은/지정되지 않은 동작은 무엇입니까?

C 언어에서 지정되지 않은 동작의 예로는 함수에 대한 인수 평가 순서가 있습니다.왼쪽에서 오른쪽으로, 오른쪽에서 왼쪽으로 갈 수도 있어요. 그냥 모르시잖아요.은 어떻게 해야, 어떻게 해야 하는가에 을 줄 수 있습니다.foo(c++, c) ★★★★★★★★★★★★★★★★★」foo(++c, c)가됩니니다다

그 밖에 알려지지 않은 어떤 행동들이 프로그래머를 놀라게 할 수 있을까요?

언어 변호사 질문입니다.흠케이.

개인 톱3:

  1. 엄격한 에일리어스 규칙을 위반하는 모습

  2. 엄격한 에일리어스 규칙을 위반하는 모습

  3. 엄격한 에일리어스 규칙을 위반하는 모습

    :-)

편집: 두 번 잘못하는 작은 예를 다음에 나타냅니다.

(32비트 ints 및 little endian 포함)

float funky_float_abs (float a)
{
  unsigned int temp = *(unsigned int *)&a;
  temp &= 0x7fffffff;
  return *(float *)&temp;
}

이 코드는 플로트의 표현에서 직접 부호 비트와 비트 트위들링함으로써 플로트의 절대값을 얻으려고 합니다.

단, 어떤 타입에서 다른 타입으로 캐스팅하여 오브젝트에 대한 포인터를 작성한 결과는 유효하지 않습니다.컴파일러는, 다른 타입의 포인터가 같은 메모리 청크를 가리키고 있지 않다고 생각할 가능성이 있습니다.이것은 void*와 char*를 제외한 모든 종류의 포인터에 해당됩니다(부호성은 중요하지 않습니다).

위의 경우 나는 그것을 두 번 한다.float a에 대한 int-alias를 얻으려면 한 번, float으로 값을 다시 변환하려면 한 번.

같은 방법으로 3가지 방법이 있습니다.

캐스트 중에 char 또는 void 포인터를 사용합니다.이것들은 항상 모든 것에 별칭을 붙이기 때문에 안전합니다.

float funky_float_abs (float a)
{
  float temp_float = a;
  // valid, because it's a char pointer. These are special.
  unsigned char * temp = (unsigned char *)&temp_float;
  temp[3] &= 0x7f;
  return temp_float;
}

memcopy를 사용합니다.Memcpy는 보이드 포인터를 사용하기 때문에 에일리어스도 강제합니다.

float funky_float_abs (float a)
{
  int i;
  float result;
  memcpy (&i, &a, sizeof (int));
  i &= 0x7fffffff;
  memcpy (&result, &i, sizeof (int));
  return result;
}

세 번째 유효한 방법: 유니언 사용.이것은 C99 이후 명시적으로 정의되어 있지 않습니다.

float funky_float_abs (float a)
{
  union 
  {
     unsigned int i;
     float f;
  } cast_helper;

  cast_helper.f = a;
  cast_helper.i &= 0x7fffffff;
  return cast_helper.f;
}

상대적으로 경험이 부족한 프로그래머들이 다중문자 상수에 물리는 걸 많이 봐왔어요.

이것은, 다음과 같습니다.

"x"

리터럴입니다(은 스트링 리터럴입니다).char[2]~로 합니다.char*(일본어판)

이것은, 다음과 같습니다.

'x'

는 통상적인 문자 상수입니다(이력적인 이유로 타입입니다).int를 참조해 주세요.

이것은, 다음과 같습니다.

'xy'

상수이기도 그 값(도 유형인 이.int 정의되어 있습니다는 구현 정의입니다.그것은 거의 쓸모없는 언어 특성으로 대부분 혼란을 일으킨다.

제가 개인적으로 좋아하는 정의되지 않은 동작은 비어 있지 않은 소스 파일이 새로운 행으로 끝나지 않으면 동작이 정의되지 않는다는 것입니다.

내가 보게 될 컴파일러 중 경고만 보내는 것 말고는 소스 파일을 newline 종료 여부에 따라 다르게 취급한 것은 사실이라고 생각합니다.즉, 이 경고에 놀랄 수 있다는 것 말고는 깨닫지 못하는 프로그래머들을 놀라게 할 만한 것은 아닙니다.

따라서 진정한 휴대성 문제(대부분은 미지정 또는 정의되지 않은 것이 아니라 구현에 의존하지만, 이는 질문의 정신에 해당한다고 생각합니다)에 대해서는 다음과 같습니다.

  • char는 반드시 (서명이 없는) 것은 아닙니다.
  • int는 16비트부터 임의의 크기로 지정할 수 있습니다.
  • floats는 반드시 IEEE 형식 또는 적합하다고는 할 수 없습니다.
  • 정수 타입은 반드시 2의 보완은 아니며 정수 산술 오버플로는 정의되지 않은 동작을 일으킨다(현대 하드웨어는 크래시하지 않지만 일부 컴파일러 최적화는 하드웨어가 하는 일이지만 랩어라운드와는 다른 동작을 일으킨다).를 들어, 「」입니다.if (x+1 < x)가 false로 경우는 할 수 있습니다.x는 부호 을 참조해 주세요.를 참조해 주십시오.-fstrict-overflow옵션)을 선택합니다.
  • #compiler에 있는 "/", "." 및 ".."는 정의된 의미가 없으며 컴파일러마다 다르게 취급될 수 있습니다(실제로 이것은 다르며, 잘못하면 하루를 망칩니다).

동작은 부분적으로만 정의되어 있거나 지정되지 않은 상태이기 때문에 개발된 플랫폼에서도 놀라운 결과를 얻을 수 있습니다.

  • POSIX 스레드화 및 ANSI 메모리 모델.메모리에 대한 동시 액세스는 초보자들이 생각하는 것만큼 잘 정의되어 있지 않습니다.휘발성은 초보자 생각대로 되지 않아요메모리 액세스 순서는 초보자 생각만큼 잘 정의되어 있지 않습니다.액세스는 메모리 장벽을 넘어 특정 방향으로 이동할 수 있습니다.메모리 캐시 일관성은 필요하지 않습니다.

  • 프로파일링 코드는 생각만큼 쉽지 않습니다.테스트 루프가 효과가 없는 경우 컴파일러는 일부 또는 전부를 제거할 수 있습니다.inline은 정의된 효과가 없습니다.

Nils가 말한 것처럼

  • 엄격한 에일리어싱 규칙을 위반했습니다.

Clang 개발자들은 얼마 전에 모든 C 프로그래머가 읽어야 할 게시물에 몇 가지 훌륭한 예를 올렸습니다.이전에 언급되지 않은 흥미로운 사례:

  • 서명된 정수 오버플로 - 서명된 변수를 최대값으로 넘기는 것은 허용되지 않습니다.
  • NULL 포인터 참조 취소 - 정의되지 않았으며 무시될 수 있습니다. 링크의 파트 2를 참조하십시오.

또 다른 문제가 발생했습니다(정의되어 있지만 전혀 예기치 않은 문제).

char는 사악하다.

  • 컴파일러의 느낌에 따라 서명 또는 서명되지 않음
  • 8비트로 필수가 아님

printf 형식 지정자를 인수에 맞게 수정한 횟수를 셀 수 없습니다.불일치는 정의되지 않은 동작입니다.

  • 아니요, 합격할 수 없습니다.int(또는long)에 대해서%x- anunsigned int필수입니다.
  • 아니요, 합격할 수 없습니다.unsigned int로.%d- anint필수입니다.
  • 아니요, 합격할 수 없습니다.size_t로.%u또는%d- 사용%zu
  • 아니요, 포인터를 인쇄할 수 없습니다.%d또는%x- 사용%p에 캐스팅하다void *

내가 가장 좋아하는 건 이거야

// what does this do?
x = x++;

몇 가지 코멘트에 대답하자면, 그것은 표준에 따른 정의되지 않은 행동이다.이를 통해 컴파일러는 하드 드라이브 포맷까지 모든 작업을 수행할 수 있습니다.를 들어, 이 코멘트를 참조해 주세요.요점은 당신이 일부 행동에 대한 합리적인 예상이 있다는 것을 알 수 있다는 것이 아니다.C++ 표준과 시퀀스 포인트가 정의되는 방식 때문에 이 코드 행은 실제로 정의되지 않은 동작입니다.

예를 들어, 만약 우리가x = 1위의 줄 앞에, 그러면 그 뒤에 유효한 결과는 무엇일까요?어떤 분이 댓글에 이렇게 써주셨어요.

x가 1씩 증가합니다.

그 후에 x == 2를 볼 수 있습니다.그러나 실제로는 그렇지 않습니다. 나중에 x == 1 또는 x == 3을 사용하는 컴파일러도 있습니다.생성된 어셈블리를 자세히 살펴봐야 하는 이유는 알 수 있지만, 이러한 차이는 근본적인 문제에 기인합니다.기본적으로, 컴파일러가 두 개의 할당 스테이트먼트를 원하는 순서로 평가할 수 있기 때문에 컴파일러는 이 두 개의 할당 스테이트먼트를 할 수 있습니다.x++첫 번째, 또는x =첫번째.

함수 프로토타입을 사용할 수 없는 경우 컴파일러는 잘못된 파라미터 수/잘못된 파라미터 타입으로 함수를 호출하고 있다고 말할 필요가 없습니다.

포인터로 무언가를 나누는 것.어떤 이유로 컴파일이 되지 않습니다.:-)

result = x/*y;

EE는 a>>-2가 조금 곤란하다는 것을 방금 발견했습니다.

나는 고개를 끄덕이며 그것이 자연스럽지 않다고 그들에게 말했다.

변수를 사용하기 전에 항상 변수를 초기화하십시오.C를 처음 시작했을 때 몇 가지 두통이 있었습니다.

언급URL : https://stackoverflow.com/questions/98340/what-are-the-common-undefined-unspecified-behavior-for-c-that-you-run-into

반응형