Lỗi này có nghĩa là gì? Tôi không thể giải quyết nó bằng mọi cách.
cảnh báo: chuyển đổi không dùng từ hằng chuỗi sang 'char *' [-Wwrite-chuỗi]
Lỗi này có nghĩa là gì? Tôi không thể giải quyết nó bằng mọi cách.
cảnh báo: chuyển đổi không dùng từ hằng chuỗi sang 'char *' [-Wwrite-chuỗi]
Câu trả lời:
Như tôi sẽ không làm, tôi sẽ cung cấp một chút thông tin kỹ thuật nền tảng cho các vấn đề và lỗi này.
Tôi sẽ kiểm tra bốn cách khác nhau để khởi tạo chuỗi C và xem sự khác biệt giữa chúng là gì. Đây là bốn cách trong câu hỏi:
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";
Bây giờ để làm điều này, tôi sẽ muốn thay đổi chữ cái thứ ba "i" thành "o" để biến nó thành "Thos là một số văn bản". Điều đó có thể, trong mọi trường hợp (bạn sẽ nghĩ), có thể đạt được bằng cách:
text[2] = 'o';
Bây giờ chúng ta hãy xem mỗi cách khai báo chuỗi làm gì và text[2] = 'o';
câu lệnh đó sẽ ảnh hưởng đến mọi thứ như thế nào .
Cách đầu tiên thường thấy nhất : char *text = "This is some text";
. Điều này có nghĩa là gì? Chà, trong C, nó có nghĩa đen là "Tạo một biến được gọi text
là một con trỏ đọc ghi vào chuỗi ký tự này được giữ trong không gian chỉ đọc (mã).". Nếu bạn có tùy chọn -Wwrite-strings
bật thì bạn sẽ nhận được cảnh báo như đã thấy trong câu hỏi trên.
Về cơ bản điều đó có nghĩa là "Cảnh báo: Bạn đã cố gắng tạo một biến là điểm đọc-ghi đến một khu vực bạn không thể ghi vào". Nếu bạn cố gắng và sau đó đặt ký tự thứ ba thành "o" thì thực tế bạn sẽ cố gắng viết vào một khu vực chỉ đọc và mọi thứ sẽ không tốt đẹp. Trên PC truyền thống có Linux dẫn đến:
Phân đoạn lỗi
Bây giờ là cái thứ hai : char text[] = "This is some text";
. Theo nghĩa đen, trong C, có nghĩa là "Tạo một mảng kiểu" char "và khởi tạo nó với dữ liệu" Đây là một số văn bản \ 0 ". Kích thước của mảng sẽ đủ lớn để lưu trữ dữ liệu". Vì vậy, thực sự phân bổ RAM và sao chép giá trị "Đây là một số văn bản \ 0" vào nó khi chạy. Không có cảnh báo, không có lỗi, hoàn toàn hợp lệ. Và đúng cách để làm điều đó nếu bạn muốn có thể chỉnh sửa dữ liệu . Hãy thử chạy lệnh text[2] = 'o'
:
Thos là một số văn bản
Nó đã làm việc, hoàn hảo. Tốt
Bây giờ cách thứ ba : const char *text = "This is some text";
. Một lần nữa nghĩa đen: "Tạo một biến gọi là" văn bản "là con trỏ chỉ đọc dữ liệu này trong bộ nhớ chỉ đọc.". Lưu ý rằng cả con trỏ và dữ liệu hiện chỉ đọc. Không có lỗi, không có cảnh báo. Điều gì xảy ra nếu chúng ta thử và chạy lệnh thử nghiệm của chúng tôi? Vâng, chúng tôi không thể. Trình biên dịch bây giờ thông minh và biết rằng chúng tôi đang cố gắng làm điều gì đó xấu:
lỗi: gán vị trí chỉ đọc '* (văn bản + 2u)'
Nó thậm chí sẽ không được biên dịch. Cố gắng ghi vào bộ nhớ chỉ đọc hiện được bảo vệ bởi vì chúng tôi đã nói với trình biên dịch rằng con trỏ của chúng tôi là bộ nhớ chỉ đọc. Tất nhiên, nó không có được trỏ đến bộ nhớ chỉ đọc, nhưng nếu bạn trỏ nó để đọc-ghi bộ nhớ (RAM) bộ nhớ sẽ vẫn được bảo vệ khỏi được ghi vào bởi trình biên dịch.
Cuối cùng là hình thức cuối cùng : const char text[] = "This is some text";
. Một lần nữa, giống như trước đây, []
nó phân bổ một mảng trong RAM và sao chép dữ liệu vào nó. Tuy nhiên, bây giờ đây là một mảng chỉ đọc. Bạn không thể viết thư cho nó vì con trỏ tới nó được gắn thẻ là const
. Cố gắng viết cho nó kết quả là:
lỗi: gán vị trí chỉ đọc '* (văn bản + 2u)'
Vì vậy, một bản tóm tắt nhanh về nơi chúng ta đang ở:
Hình thức này là hoàn toàn không hợp lệ và nên tránh bằng mọi giá. Nó mở ra cánh cửa cho tất cả những điều tồi tệ xảy ra:
char *text = "This is some text";
Biểu mẫu này là biểu mẫu phù hợp nếu bạn muốn chỉnh sửa dữ liệu:
char text[] = "This is some text";
Biểu mẫu này là biểu mẫu phù hợp nếu bạn muốn các chuỗi không được chỉnh sửa:
const char *text = "This is some text";
Hình thức này có vẻ lãng phí RAM nhưng nó có công dụng của nó. Tốt nhất hãy quên nó đi ngay bây giờ.
const char text[] = "This is some text";
PROGMEM
, PSTR()
hoặc F()
. Như vậy, const char text[]
không sử dụng nhiều RAM hơn const char *text
.
(const char *)(...)
vai diễn đơn giản . Không có tác dụng thực sự nếu bảng không cần nó, nhưng sẽ tiết kiệm rất nhiều nếu sau đó bạn chuyển mã của mình sang một bảng.
Để giải thích về câu trả lời xuất sắc của Makenko, có một lý do chính đáng tại sao trình biên dịch cảnh báo bạn về điều này. Hãy làm một bản phác thảo thử nghiệm:
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
Chúng tôi có hai biến ở đây, foo và bar. Tôi sửa đổi một trong những cái trong setup (), nhưng xem kết quả là gì:
Thos is some text
Thos is some text
Cả hai đã thay đổi!
Trong thực tế nếu chúng ta nhìn vào các cảnh báo chúng ta thấy:
sketch_jul14b.ino:1: warning: deprecated conversion from string constant to ‘char*’
sketch_jul14b.ino:2: warning: deprecated conversion from string constant to ‘char*’
Trình biên dịch biết điều này là tinh ranh, và nó là đúng! Lý do cho điều này là, trình biên dịch (một cách hợp lý) hy vọng rằng các hằng chuỗi không thay đổi (vì chúng là hằng số). Do đó, nếu bạn tham chiếu chuỗi liên tục "This is some text"
nhiều lần trong mã của mình, nó được phép phân bổ cùng một bộ nhớ cho tất cả chúng. Bây giờ nếu bạn sửa đổi một, bạn sửa đổi tất cả chúng!
*foo
và *bar
sử dụng các "hằng" chuỗi khác nhau , ngăn điều này xảy ra? Ngoài ra, điều này khác với việc không đặt bất kỳ chuỗi nào, như : char *foo;
?
new
, strcpy
và delete
).
Hoặc ngừng cố gắng truyền một hằng chuỗi trong đó một hàm lấy một char*
hoặc thay đổi hàm để nó const char*
thay thế.
Chuỗi như "chuỗi ngẫu nhiên" là hằng số.
Thí dụ:
void foo (char * s)
{
Serial.println (s);
}
void setup ()
{
Serial.begin (115200);
Serial.println ();
foo ("bar");
} // end of setup
void loop ()
{
} // end of loop
Cảnh báo:
sketch_jul14b.ino: In function ‘void setup()’:
sketch_jul14b.ino:10: warning: deprecated conversion from string constant to ‘char*’
Hàm foo
mong đợi một char * (do đó nó có thể sửa đổi) nhưng bạn đang truyền một chuỗi ký tự, không nên sửa đổi.
Trình biên dịch đang cảnh báo bạn không làm điều này. Bị phản đối, nó có thể chuyển từ cảnh báo thành lỗi trong phiên bản trình biên dịch trong tương lai.
Giải pháp: Làm cho foo mất một const char *:
void foo (const char * s)
{
Serial.println (s);
}
Tôi không hiểu Bạn có nghĩa là không thể sửa đổi?
Các phiên bản cũ hơn của C (và C ++) cho phép bạn viết mã như ví dụ của tôi ở trên. Bạn có thể tạo một hàm (như foo
) in một cái gì đó mà bạn truyền lại cho nó, và sau đó truyền xuống một chuỗi bằng chữ (ví dụ. foo ("Hi there!");
)
Tuy nhiên, một hàm lấy char *
làm đối số được phép sửa đổi đối số của nó (nghĩa là sửa đổi Hi there!
trong trường hợp này).
Bạn có thể đã viết, ví dụ:
void foo (char * s)
{
Serial.println (s);
strcpy (s, "Goodbye");
}
Thật không may, bằng cách truyền lại một nghĩa đen, bây giờ bạn đã có khả năng sửa đổi nghĩa đen đó thành "Xin chào!" bây giờ là "Tạm biệt" không tốt. Trong thực tế nếu bạn sao chép trong một chuỗi dài hơn, bạn có thể ghi đè lên các biến khác. Hoặc, trên một số triển khai, bạn sẽ bị vi phạm quyền truy cập vì "Xin chào!" có thể đã được đặt trong RAM chỉ đọc (được bảo vệ).
Vì vậy, các trình biên dịch-nhà văn đang dần phản đối việc sử dụng này, do đó, các hàm mà bạn truyền lại theo nghĩa đen, phải khai báo đối số đó là const
.
can not
được sửa đổi?
Tôi có lỗi biên dịch này:
TimeSerial.ino:68:29: warning: deprecated conversion from string constant to 'char*' [-Wwrite-strings]
if(Serial.find(TIME_HEADER)) {
^
Vui lòng thay thế dòng này:
#define TIME_HEADER "T" // Header tag for serial time sync message
với dòng này:
#define TIME_HEADER 'T' // Header tag for serial time sync message
và biên dịch diễn ra tốt đẹp.