문자열 상수에서 'char *'로의 더 이상 사용되지 않는 변환


16

이 오류는 무엇을 의미합니까? 어떤 식 으로든 해결할 수 없습니다.

경고 : 문자열 상수에서 'char *'로의 더 이상 사용되지 않는 변환 [-Wwrite-strings]


이 질문은 Arduino가 아니라 StackOverflow에 있어야합니다. :)
Vijay Chavda

답변:


26

내 말 그대로, 나는이 오류의 이유와 이유에 대한 약간의 배경 기술 정보를 제공 할 것입니다.

C 문자열을 초기화하는 네 가지 방법을 살펴보고 그 차이점이 무엇인지 살펴 보겠습니다. 다음과 같은 네 가지 방법이 있습니다.

char *text = "This is some text";
char text[] = "This is some text";
const char *text = "This is some text";
const char text[] = "This is some text";

이제 이것을 위해 세 번째 문자 "i"를 "o"로 변경하여 "일부 텍스트"로 만들려고합니다. 그것은 모든 경우 (당신이 생각할 것입니다)에 의해 달성 될 수 있습니다 :

text[2] = 'o';

이제 문자열을 선언하는 각 방법이하는 일과 그 text[2] = 'o';문장이 어떻게 영향을 미치는지 살펴 보겠습니다 .

가장 일반적으로 보이는 방식 : char *text = "This is some text";. 이것은 문자 그대로 무엇을 의미합니까? C에서는 문자 그대로 " text읽기 전용 (코드) 공간에있는이 문자열 리터럴에 대한 읽기 / 쓰기 포인터 인 변수 만들기"를 의미 합니다. 옵션 -Wwrite-strings이 켜져 있으면 위의 질문에 표시된대로 경고가 표시됩니다.

기본적으로 이것은 "경고 : 쓰기-쓰기가 가능한 변수를 쓸 수없는 영역을 가리 키도록 시도했습니다"를 의미합니다. 세 번째 문자를 "o"로 설정 한 다음 실제로 읽기 전용 영역에 쓰려고하면 문제가되지 않습니다. Linux가 설치된 기존 PC에서 다음과 같은 결과가 발생합니다.

세그멘테이션 오류

이제 두 번째 것 : char text[] = "This is some text";. 말 그대로 C에서 "char"유형의 배열을 만들고 "This is some text \ 0"데이터로 초기화합니다. 배열의 크기는 데이터를 저장하기에 충분히 클 것입니다. " 따라서 실제로 RAM을 할당하고 런타임에 "This is some text \ 0"값을 RAM에 복사합니다. 경고, 오류가 없으며 완벽하게 유효합니다. 데이터를 편집 할 수 있는 올바른 방법 입니다. 명령을 실행 해 봅시다 text[2] = 'o':

토스는 일부 텍스트입니다

완벽하게 작동했습니다. 좋은.

이제 세 번째 방법 : const char *text = "This is some text";. 리터럴 의미 : " 읽기 전용 메모리에서이 데이터에 대한 읽기 전용 포인터 인"text "라는 변수를 작성하십시오 ." 포인터와 데이터는 모두 읽기 전용입니다. 오류, 경고가 없습니다. 테스트 명령을 시도하고 실행하면 어떻게됩니까? 글쎄, 우리는 할 수 없습니다. 컴파일러는 이제 지능적이며 우리가 나쁜 일을하려고한다는 것을 알고 있습니다.

오류 : 읽기 전용 위치 '* (text + 2u)'지정

컴파일조차하지 않습니다. 컴파일러에게 포인터가 읽기 전용 메모리라는 것을 말했기 때문에 읽기 전용 메모리에 쓰려고 시도하는 것이 이제 보호됩니다. 물론, 그것은하지 않습니다 읽기 전용 메모리를 가리키는 할 수 있지만, 메모리 (RAM) 읽기 - 쓰기를 가리 경우 그 메모리는 여전히 컴파일러에 의해 기록되는 것을 보호됩니다.

마지막으로 마지막 형태 : const char text[] = "This is some text";. 이전과 마찬가지로 []RAM에 배열을 할당하고 데이터를 여기에 복사합니다. 그러나 이제 이것은 읽기 전용 배열입니다. 포인터가로 태그되어 있으므로 쓸 수 없습니다 const. 쓰려고하면 다음과 같은 결과가 발생합니다.

오류 : 읽기 전용 위치 '* (text + 2u)'지정

우리가 어디에 있는지에 대한 간단한 요약 :

이 양식은 완전히 유효하지 않으므로 모든 비용을 피해야합니다. 그것은 모든 종류의 나쁜 일에 대한 문을 열어줍니다.

char *text = "This is some text";

데이터를 편집 가능하게하려면이 양식이 올바른 양식입니다.

char text[] = "This is some text";

이 양식은 편집하지 않는 문자열을 원하는 경우 올바른 양식입니다.

const char *text = "This is some text";

이 형식은 RAM을 낭비하는 것처럼 보이지만 그 용도는 있습니다. 그래도 지금은 잊어 버려요.

const char text[] = "This is some text";

6
그것은 아두 이노 (적어도 AVR 기반의 것들)에, 문자열 리터럴 당신이 같은 매크로를 선언하지 않는 한, RAM에 살고 있음을 주목할 필요가있다 PROGMEM, PSTR()또는 F(). 따라서 const char text[]보다 많은 RAM을 사용하지 마십시오 const char *text.
Edgar Bonet

Teensyduino 및 기타 최신 아두 이노 호환 장치는 자동으로 코드 리터럴에 문자열 리터럴을 배치하므로 보드에 F ()가 필요한지 여부를 확인하는 것이 좋습니다.
Craig.6

@ Craig.Feied 일반적으로 F ()는 관계없이 사용해야합니다. "필요하지 않은"것은 단순한 (const char *)(...)캐스팅 으로 정의하는 경향이 있습니다. 보드가 필요하지 않으면 실제 효과는 없지만 코드를 보드에 코드를 이식하면 크게 절약됩니다.
Majenko

5

Makenko의 탁월한 답변을 자세히 설명하기 위해 컴파일러가 이에 대해 경고하는 이유는 충분합니다. 테스트 스케치를 만들어 봅시다 :

char *foo = "This is some text";
char *bar = "This is some text";

void setup ()
  {
  Serial.begin (115200);
  Serial.println ();
  foo [2] = 'o';     // change foo only
  Serial.println (foo);
  Serial.println (bar);
  }  // end of setup

void loop ()
  {
  }  // end of loop

여기에는 foo와 bar라는 두 가지 변수가 있습니다. setup ()에서 그중 하나 를 수정 하지만 결과가 무엇인지 확인하십시오.

Thos is some text
Thos is some text

그들은 둘 다 바뀌었다!

실제로 경고를 보면 다음과 같습니다.

sketch_jul14b.ino:1: warning: deprecated conversion from string constant to char*’
sketch_jul14b.ino:2: warning: deprecated conversion from string constant to char*’

컴파일러는 이것이 방해가된다는 것을 알고 있습니다. 그 이유는 컴파일러가 문자열 상수가 변경되지 않기를 기대하기 때문입니다 (상수이기 때문에). 따라서 "This is some text"코드에서 문자열 상수를 여러 번 참조 하면 모든 메모리에 동일한 메모리를 할당 할 수 있습니다. 이제 하나를 수정하면 모두 수정합니다!


성스러운 연기! 누가 알았 을까 ... 이것은 최신 ArduinoIDE 컴파일러의 경우에도 마찬가지입니까? 방금 ESP32에서 시도했으며 GuruMeditation 오류 가 반복적으로 발생 합니다.
not2qubit

@ not2qubit 방금 Arduino 1.8.9에서 테스트했으며 사실입니다.
Nick Gammon

경고는 이유가 있습니다. 이번에 내가 얻은 : 경고 : ISO C ++는 문자열 상수를 'char '[-Wwrite-strings] 로 변환하는 것을 금지합니다 . char bar = "This is some text"; -FORBIDS는 강력한 단어입니다. 그렇게 할 수 없으므로 컴파일러는 두 변수를 통해 같은 문자열을 자유롭게 공유 할 수 있습니다. 금지 된 일을 하지 마십시오 ! 또한 경고를 읽고 제거하십시오. :)
Nick Gammon

따라서 이처럼 엉뚱한 코드를 발견하고 하루를 생존하고 싶을 때. 다른 문자열 "constants" 를 처음 선언 *foo하고 *bar사용 하면 이런 일이 발생하지 않습니까? 또한 다음과 같이 문자열을 전혀 넣지 않는 것과 어떻게 다른 가요? char *foo;
not2qubit

1
다른 상수가 도움이 될 수 있지만 개인적으로 나는 거기 에 아무것도 넣지 않고 나중에 일반적인 방법으로 데이터를 넣습니다 (예 : new, strcpydelete).
Nick Gammon


3

예:

void foo (char * s)
  {
  Serial.println (s);
  }

void setup ()
  {
  Serial.begin (115200);
  Serial.println ();
  foo ("bar");
  }  // end of setup

void loop ()
  {
  }  // end of loop

경고:

sketch_jul14b.ino: In function ‘void setup()’:
sketch_jul14b.ino:10: warning: deprecated conversion from string constant to ‘char*’

이 함수 foo는 char * (따라서 수정할 수 있음)를 기대하지만 수정해서는 안되는 문자열 리터럴을 전달합니다.

컴파일러는이 작업을 수행하지 말 것을 경고합니다. 더 이상 사용되지 않으므로 향후 컴파일러 버전에서 경고에서 오류로 바뀔 수 있습니다.


해결책 : foo가 const char *를 갖도록하십시오 .

void foo (const char * s)
  {
  Serial.println (s);
  }

나는 그것을 얻지 못한다. 당신은 수정할 수 없다는 것을 의미 합니까?

이전 버전의 C (및 C ++)를 사용하면 위의 예제와 같은 코드를 작성할 수 있습니다. 당신은 기능 (같은 만들 수있는 foo인쇄 뭔가 당신이 그것을 아래로 통과 한 다음 리터럴 문자열을 통과한다는)을 (예. foo ("Hi there!");)

그러나 char *인수로 사용되는 함수는 인수를 수정할 수 있습니다 (즉 Hi there!,이 경우 수정 ).

예를 들어 다음과 같이 작성했을 수 있습니다.

void foo (char * s)
  {
  Serial.println (s);
  strcpy (s, "Goodbye");
  }

불행하게도, 리터럴을 전달함으로써, "Hi there!"가되도록 리터럴을 잠재적으로 수정했습니다. 지금은 "안녕"입니다. 실제로 더 긴 문자열로 복사하면 다른 변수를 덮어 쓸 수 있습니다. 또는 일부 구현에서는 "Hi there!"때문에 액세스 위반이 발생합니다. 읽기 전용 (보호 된) RAM에 넣었을 수 있습니다.

따라서 컴파일러 작성자는 점차적 으로이 사용법을 더 이상 사용하지 않으므로 리터럴을 전달하는 함수는 해당 인수를로 선언해야합니다 const.


포인터를 사용하지 않으면 문제가 되나요?
Federico Corazza

어떤 종류의 문제? 이 특별한 경고는 문자열 상수를 char * 포인터로 변환하는 것입니다. 정교하게 할 수 있습니까?
Nick Gammon

@Nick : "(..) 문자열 리터럴을 전달하고 있습니다. 이는 수정해서는 안됩니다"는 무슨 뜻입니까? 나는 그것을 얻지 못한다. can not수정 을 의미 합니까?
Mads Skjern

나는 대답을 수정했다. Majenko는 대부분의 요점을 그의 답변에서 다루었습니다.
Nick Gammon

1

이 컴파일 오류가 있습니다.

TimeSerial.ino:68:29: warning: deprecated conversion from string constant to 'char*' [-Wwrite-strings]
   if(Serial.find(TIME_HEADER)) {

                         ^

이 줄을 바꾸십시오 :
#define TIME_HEADER "T" // Header tag for serial time sync message

이 줄로 :
#define TIME_HEADER 'T' // Header tag for serial time sync message

그리고 컴파일은 잘 진행됩니다.


3
이 변경은 문자 대문자 T에 대한 ASCII 코드 값을 가진 하나의 문자열 "T"에서 단일 문자로 정의를 변경합니다.
dlu
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.