답변:
내 말 그대로, 나는이 오류의 이유와 이유에 대한 약간의 배경 기술 정보를 제공 할 것입니다.
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";
PROGMEM
, PSTR()
또는 F()
. 따라서 const char text[]
보다 많은 RAM을 사용하지 마십시오 const char *text
.
(const char *)(...)
캐스팅 으로 정의하는 경향이 있습니다. 보드가 필요하지 않으면 실제 효과는 없지만 코드를 보드에 코드를 이식하면 크게 절약됩니다.
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"
코드에서 문자열 상수를 여러 번 참조 하면 모든 메모리에 동일한 메모리를 할당 할 수 있습니다. 이제 하나를 수정하면 모두 수정합니다!
*foo
하고 *bar
사용 하면 이런 일이 발생하지 않습니까? 또한 다음과 같이 문자열을 전혀 넣지 않는 것과 어떻게 다른 가요? char *foo;
new
, strcpy
및 delete
).
함수가 a를 사용하는 문자열 상수 전달을 중지 char*
하거나 const char*
대신 함수가 변경되도록 변경하십시오 .
"무작위 문자열"과 같은 문자열은 상수입니다.
예:
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
.
can not
수정 을 의미 합니까?
이 컴파일 오류가 있습니다.
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
그리고 컴파일은 잘 진행됩니다.