문자열 상수에서 '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, strcpy및 delete).
— 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.