uint16_t의 += 연산자가 할당된 값을 int로 승격하고 컴파일하지 않음
이것은 저에게 있어서 진정한 WTF입니다.GCC의 버그처럼 보이지만 커뮤니티가 저를 위한 해결책을 찾아주셨으면 합니다.
내가 모을 수 있는 가장 간단한 프로그램은 다음과 같다.
#include <stdio.h>
#include <stdint.h>
int main(void)
{
uint16_t i = 1;
uint16_t j = 2;
i += j;
return i;
}
GCC는 GCC로 컴파일합니다.-Werror=conversion
플래그를 사용합니다.
결과는 다음과 같습니다.
.code.tio.c: In function ‘main’:
.code.tio.c:9:7: error: conversion to ‘uint16_t {aka short unsigned int}’ from ‘int’ may alter its value [-Werror=conversion]
i += j;
이 코드에 대해서도 같은 에러가 발생합니다.
uint16_t i = 1;
i += ((uint16_t)3);
에러는
.code.tio.c: In function ‘main’:
.code.tio.c:7:7: error: conversion to ‘uint16_t {aka short unsigned int}’ from ‘int’ may alter its value [-Werror=conversion]
i += ((uint16_t)3);
^
확실히 하기 .+=
츠키노
가 발생할 수 있습니다.+=
uint16_t
망진진엉아니면 제가 뭔가 미묘한 걸 놓치고 있는 건가요?
사용 용도: MCVE
편집: 기타 동일한 항목:
.code.tio.c:8:6: error: conversion to ‘uint16_t {aka short unsigned int}’ from ‘int’ may alter its value [-Werror=conversion]
i = i + ((uint16_t)3);
★★★★★★★★★★★★★★★★★.i = (uint16_t)(i +3);
......
는 '비슷하다'가입니다.+=
.=
★★★★★★★★★★★★★★★★★」+
.
C 표준의 섹션 6.5.16.2에서:
3 E1 op= E2 형식의 복합 할당은 단순 할당식 E1 = E1 op (E2)과 동일하지만, l값 E1은 단 한 번만 평가되며, 부정 시퀀스 함수 호출에 대해서는 복합 할당의 연산은 단일 평가이다.
그래서 다음과 같이 됩니다.
i += ((uint16_t)3);
다음과 같습니다.
i = i + ((uint16_t)3);
에서는, 「」의 .+
가 오오 to로 int
및 그, 「」int
is시시 is is is 。uint16_t
.
섹션 6.3.1.1은 이러한 이유에 대해 자세히 설명합니다.
2 다음 문구를 표현하는데 사용할 수 있다.
int
★★★★★★★★★★★★★★★★★」unsigned int
사용할 수 있습니다.
- 외의 객체 또는
int
★★★★★★★★★★★★★★★★★」unsigned int
가 '정수 변환 순위'int
★★★★★★★★★★★★★★★★★」unsigned int
.- '''의
_Bool
,int
,signed int
, 「」unsigned int
.、 、
int
는 원래 수 됨). 이 은 "비트의 경우"로 됩니다.이 값은 다음과 같이 변환됩니다.int
그 이외의 는, 로 됩니다.; R/O/O/O/O/O는 R/O/O/O로 변환됩니다.unsigned int
이를 정수 프로모션이라고 합니다.다른 모든 유형은 정수 프로모션에 의해 변경되지 않습니다.
...uint16_t
.a.an)unsigned short int
는 보다 낮습니다.int
값은 " "로 +
.
하려면 , 이 문제를 해야 합니다.+=
이치노또한 프로모션으로 인해 값 3에 대한 캐스팅은 효과가 없으므로 다음과 같이 제거할 수 있습니다.
i = (uint16_t)(i + 3);
그러나 이 작업은 오버플로우될 수 있으므로 캐스트가 없을 때 경고가 표시되는 이유 중 하나입니다.를 들어, 「」의 ,i
으로 65535입니다.i + 3
은 ★★★★★★★★★★★★★int
§ 65538.가 음음음음음음으로 때uint16_t
이값 2가 은 "65536" "2" 에 됩니다.이 값은 다시 에 할당됩니다.i
이 경우는, 행선지 타입이 서명되어 있지 않기 때문에, 이 동작이 올바르게 정의되고 있습니다.행선지 타입이 서명되어 있는 경우는, 실장이 정의됩니다.
임의의 산술 연산자에 대한 인수는 N1570(최신 C11 드래프트), § 6.3.1.8에서 기술된 통상적인 산술 변환의 대상이 됩니다.이 질문과 관련된 구절은 다음과 같습니다.
[부동소수점 유형에 대한 몇 가지 규칙]
그렇지 않으면 양쪽 오퍼랜드에서 정수 프로모션이 실행됩니다.
따라서 정수 프로모션이 어떻게 정의되는지를 더 자세히 살펴보면 § 6.3.1.1 p2에서 관련 텍스트를 찾을 수 있습니다.
、 、
int
는 원래 수 됨). 이 은 "비트의 경우"로 됩니다.이 값은 다음과 같이 변환됩니다.int
그 이외의 는, 로 됩니다.; R/O/O/O/O/O는 R/O/O/O로 변환됩니다.unsigned int
이를 정수 프로모션이라고 합니다.
이 코드에도 불구하고:
i += ((uint16_t)3);
피연산자로 됩니다.int
에, 「할당」이 int
로로 합니다.i
.
은 확실히 가 있다.i + 3
수 uint16_t
.
i += ((uint16_t)3);
와 동등하다(1)
i = i + ((uint16_t)3);
으로 변환됩니다.int
))3
부터 )까지uint16_t
배역별로.그 후, 통상적인 산술(2) 변환이 양쪽 오퍼랜드에 적용됩니다.+
후 두 으로 ""로 int
의 +
은 「형」입니다.int
.
.int
in a a a a uint16_t
는 '경고', ''가 .-Wconversion
.
입니다.int
a까지uint16_t
MISRA-C를 사용하다
i = (uint16_t)(i + 3u);
(1) 이는 모든 복합 할당 연산자 C11 6.5.16.2에 대해 의무화된다.
E1 op= E2 형식의 복합 할당은 l값 E1이 한 번만 평가된다는 점을 제외하고 단순 할당식 E1 = E1 op (E2)과 동일하다.
(2) 암묵적 유형 프로모션에 대한 자세한 내용은 암묵적 유형 프로모션 규칙을 참조하십시오.
설명은 다음과 같습니다.
.com UTCjoseph[at] codesourcery.com 2009-07-15 14:15:38 UTC
"큰 경고하지 않음": -Wconversion: "Do warn for than target type": "Wconversion:" "Doon't warn for the target type" (타깃 타입보다 크지 않은 오퍼랜드에 대해 경고하지 않음)2009년 7월 15일 수요일 에어스닷컴에서 이안은 이렇게 썼다.
> 물론 랩은 가능하지만 -Wconversion은 랩 경고용이 아닙니다.
암묵적인 변환이 값을 변경하는 것에 대한 경고용입니다.산술은 (의도적으로든 다른 방법으로든) 폭이 넓어지지는 않지만 암묵적인 변환에 의해 값이 다시 char로 변경됩니다.사용자가 산술에서 int에 명시적인 캐스트를 사용했다면 경고가 적절한지 의심할 여지가 없습니다.
이 에게 보다 큰 합니다.uint16_t
)int
) 및 (정수 승진을 통해).uint16_t
「」와 같이,
uint16_t i = 0xFFFF;
i += (uint16_t)3; /* Truncated as per the warning */
이는 별도의 할당 연산자와 추가 연산자에게도 동일하게 적용됩니다.
uint16_t i = 0xFFFF;
i = i + (uint16_t)3; /* Truncated as per the warning */
작은 부호 없는 정수 유형에 대해 정수 연산을 직접 수행하는 것이 유용한 경우가 많습니다. '은 '동작이기때문입니다.ushort1 = ushort1+intVal;
모든 , 「」에 합니다.intVal
of ushort1
그러나 이 기준서의 작성자는 그러한 유형의 추가를 직접 수행한 후 그러한 상황에 대한 특별한 규칙을 작성할 필요가 없다고 보았다.나는 그들이 그러한 행동이 유용하다는 것을 분명히 인식했지만, 그들은 기준서가 의무화하든 아니든 일반적으로 이행이 그러한 방식으로 행동할 것이라고 예상했다고 생각한다.
는 타입의 인 「Gcc」, 「gcc」의 하는 경우가 .uint16_t
가 될 때 uint16_t
을 사용법를 들어, '예'를 지정하면,
uint32_t multest1(uint16_t x, uint16_t y)
{
x*=y;
return x;
}
uint32_t multest2(uint16_t x, uint16_t y)
{
return (x*y) & 65535u;
}
★★multest1()
는 모든 경우에서 곱셈 모드 65536을 일관되게 실행하는 것처럼 보이지만, 함수 multest2는 그렇지 않습니다.를를들, 음음음음음 음음음음음
void tester(uint16_t n, uint16_t *p)
{
n|=0x8000;
for (uint16_t i=0x8000; i<n; i++)
*p++= multest2(65535,i);
return 0;
}
다음과 같이 최적화됩니다.
void tester(uint16_t n, uint16_t *p)
{
n|=0x8000;
if (n != 0x8000)
*p++= 0x8000;
return 0;
}
이런 같은 것은 하지 않을 거예요.multest1
은 신뢰할 수 없다고 만, 는 gcc mod-65536의 동작 즉 gcc mod-65536의 동작의 차이를 있습니다.
gcc를 포함한 일부 컴파일러는 결과가 uint16_t에 직접 강제될 때 mod 65536 연산을 직접 수행합니다.
일부 컴파일러는 코드가 결과의 상위 부분을 완전히 무시하더라도 오류가 발생할 수 있는 방법으로 정수 오버플로를 처리합니다.따라서 가능한 모든 UB에 대해 경고하려는 컴파일러는 어떤 컴파일러가 어리석은 방식으로 처리되도록 허용되는지 플래그를 작성해야 합니다.
ushort1+=intval 형식의 문장이 오버플로를 일으킬 수 없는 경우가 많지만, 실제로 잘못된 행동을 일으킬 수 있는 문장만 확인하는 것보다 그러한 문장에서 꽥꽥거리는 것이 더 쉽다.
언급URL : https://stackoverflow.com/questions/47417034/operator-for-uint16-t-promotes-the-assigned-value-to-int-and-wont-compile