Làm cách nào để loại bỏ chuyển đổi `không dùng nữa từ các chuỗi cảnh báo sang 'char *'` cảnh báo trong GCC?


409

Vì vậy, tôi đang làm việc trên một cơ sở mã cực lớn và gần đây đã nâng cấp lên gcc 4.3, hiện đang kích hoạt cảnh báo này:

cảnh báo: chuyển đổi không dùng từ hằng chuỗi sang 'char *'

Rõ ràng, cách chính xác để khắc phục điều này là tìm mọi tuyên bố như

char *s = "constant string";

hoặc gọi hàm như:

void foo(char *s);
foo("constant string");

và làm cho chúng const charcon trỏ. Tuy nhiên, điều đó có nghĩa là chạm 564 tệp, tối thiểu, đây không phải là nhiệm vụ tôi muốn thực hiện tại thời điểm này. Vấn đề hiện tại là tôi đang chạy theo -werror, vì vậy tôi cần một số cách để dập tắt những cảnh báo này. Làm thế nào tôi có thể làm điều đó?


Khi bạn đến để giải quyết thay thế 554 dòng, sed là một người bạn tốt. Hãy chắc chắn rằng bạn sao lưu đầu tiên mặc dù.
Matt

2
Tôi đã xem xét các cuộc thảo luận về cách loại bỏ các thông báo lỗi và những gì cần thay thế chính xác. Tôi không có ý kiến ​​gì về điều đó. Tuy nhiên, tôi nghĩ rằng Matt đang đi đúng hướng. Xác định những gì bạn muốn thay thế bằng những gì. Bạn chỉ cần đúng (các) biểu thức chính quy. Thực hiện các thay đổi trong một bản sao. Sử dụng "diff" để so sánh chúng với bản gốc. Thực hiện các thay đổi bằng cách sử dụng sed là nhanh chóng, dễ dàng và miễn phí, và diff cũng nhanh chóng, dễ dàng và miễn phí. Hãy thử nó và xem có bao nhiêu thay đổi bạn phải xem xét. Đăng những gì bạn muốn thay thế bằng những gì và cho phép người dùng đề xuất thay thế regex.
Thomas Hedden

Toàn bộ cuộc thảo luận là thiếu lý do tại sao đây là một vấn đề cần khắc phục tất cả theo cảnh báo gcc. Lý do là trong câu trả lời stackoverflow.com/questions/56522654/ của David Schwartz .
andig

Câu trả lời:


227

Tôi tin rằng việc chuyển qua -Wno-write-stringsgcc sẽ ngăn chặn cảnh báo này.


6
Là nó có thể được vô hiệu hóa trên mỗi tập tin cơ bản bằng cách sử dụng pragmas.
Priyank Bolia

18
@PriyankBolia bdonlan đã nhận xét về câu trả lời của Rob Walker rằng nó có thể sử dụng #pragma GCC diagnostic ignored "-Wwrite-strings".
MasterMastic

9
Ngoại trừ nếu bạn kiểm soát API, trong trường hợp câu trả lời của @ John bên dưới, về việc thay đổi chữ ký để chấp nhận const char *, thì đúng hơn.
jcwenger

215
ĐÂY LÀ THỰC TIỄN BAD HORRIBLY, và tôi buồn vì nó đã nhận được tất cả những phiếu bầu. Cảnh báo không có để bạn bỏ qua chúng. Cảnh báo ở đó nói với bạn rằng "anh bạn, bạn đang làm điều gì đó có thể sai, hãy cẩn thận" và bạn chỉ nên kìm nén chúng khi bạn muốn trả lời như "im lặng, tôi biết tôi đang làm gì", rất có thể không phải là trường hợp với lập trình viên trẻ sơ sinh.
Nhà vật lý lượng tử

9
Tôi đồng ý, bạn không nên loại bỏ cảnh báo và thay vào đó hãy sử dụng giải pháp do John cung cấp. Quá tệ, đây là câu trả lời được chấp nhận!
Jérôme

564

Bất kỳ chức năng nào mà bạn chuyển qua chuỗi ký tự "I am a string literal"nên sử dụng char const *làm loại thay vì char*.

Nếu bạn định sửa một cái gì đó, hãy sửa nó ngay.

Giải trình:

Bạn không thể sử dụng chuỗi ký tự để khởi tạo các chuỗi sẽ được sửa đổi, vì chúng thuộc loại const char*. Bỏ đi các hằng số để sau đó sửa đổi chúng là hành vi không xác định , do đó bạn phải sao chép các const char*chuỗi của mình charbằng charcác char*chuỗi được phân bổ động để sửa đổi chúng.

Thí dụ:

#include <iostream>

void print(char* ch);

void print(const char* ch) {
    std::cout<<ch;
}

int main() {
    print("Hello");
    return 0;
}

25
Mặc dù điều này là đúng, nhưng bạn không luôn có quyền kiểm soát API của bên thứ 3 mà có thể không sử dụng chính xác char */ const char *, vì vậy trong trường hợp đó tôi thường sử dụng.
ideaman42

15
@ppumkin Thật không may, nhiều hàm chuỗi thư viện chuẩn C lấy các đối số là char*ngay cả đối với các chuỗi sẽ không được sửa đổi. Nếu bạn lấy tham số là một char const*và chuyển nó đến một hàm tiêu chuẩn, char*bạn sẽ đạt được điều đó. Nếu chức năng thư viện sẽ không thao tác chuỗi, bạn có thể bỏ đi const.
Giăng

Chỉ vì không phải lúc nào cũng có thể không có nghĩa là nó không phải là tùy chọn ưa thích trong nhiều lần cảnh báo này xuất hiện trong mã sản xuất chung.
LovesTha

1
Bây giờ tôi hoàn toàn hiểu giải pháp và chức năng của chuỗi ký tự. Nhưng có lẽ những người khác thì không, vì vậy tôi 'giữ' sự cần thiết phải giải thích
NicoBerrog lỗi

1
Tôi không hiểu cách áp dụng giải pháp của bạn :(
desmond13

69

Tôi đã có một vấn đề tương tự, tôi đã giải quyết nó như thế này:

#include <string.h>

extern void foo(char* m);

int main() {
    // warning: deprecated conversion from string constant to ‘char*’
    //foo("Hello");

    // no more warning
    char msg[] = "Hello";
    foo(msg);
}

Đây có phải là một cách thích hợp để giải quyết điều này? Tôi không có quyền truy cập để foođiều chỉnh nó để chấp nhận const char*, mặc dù đó sẽ là một giải pháp tốt hơn (vì fookhông thay đổi m).


8
@elcuco, bạn sẽ đề xuất gì? Tôi không thể chỉnh sửa foo và cố gắng tìm một giải pháp không yêu cầu ngăn chặn cảnh báo. Trong trường hợp của tôi, cái sau là vấn đề tập thể dục, nhưng đối với poster ban đầu thì nó có vẻ quan trọng. Theo như tôi có thể nói, câu trả lời của tôi là câu trả lời duy nhất giải quyết cả hai điều kiện của tôi và OP cùng một lúc để nó có thể là một câu trả lời có giá trị cho ai đó. Nếu bạn nghĩ rằng giải pháp của tôi không đủ tốt, bạn có thể vui lòng cung cấp một giải pháp thay thế không? (Điều đó không bao gồm chỉnh sửa foo hoặc bỏ qua cảnh báo.)
BlackShift

nếu chúng ta cho rằng foo được mã hóa chính xác (điều không may là dường như không phải là trường hợp của mã 'Josh Matthews' đang nói đến) thì đây là giải pháp tốt nhất. đó là bởi vì nếu hàm thực sự cần thay đổi chuỗi 'thông điệp', thì chuỗi không đổi sẽ phá vỡ mã, phải không? nhưng dù sao thì điều này dường như không trả lời được câu hỏi vì các lỗi đã có trong mã cũ không phải ở mã mới, vì vậy dù sao anh ta cũng cần phải thay đổi mã cũ.
João Portela

Đó là cách tiếp cận tôi đã thực hiện quá. Và nếu ai đó đang tìm kiếm điều này cho các trường hợp char **trong PyArg_ParseTupleAndKeywordstôi sẽ làm một cái gì đó như thế này:static char kw[][16] = {"mode", "name", "ip", "port"}; static char * kwlist[] = {kw[0], kw[1], kw[2], kw[3], NULL};
bảnh bao

@elcuco: Tôi không chắc mảng hoạt động của C ++ như thế nào. Điều này sẽ thực sự sao chép bất kỳ dữ liệu, và không chỉ con trỏ?
Alexander Malakhov

Mặc dù cách tiếp cận này có thể có giá trị trong một số trường hợp áp dụng một cách mù quáng là IMO có khả năng gây hại nhiều hơn là có lợi. Áp dụng điều này một cách mù quáng có thể dễ dàng dẫn đến con trỏ lơ lửng. Nó cũng sẽ làm phồng mã với các bản sao chuỗi vô nghĩa.
cắm vào


30

Nếu đó là một cơ sở mã hoạt động, bạn vẫn có thể muốn nâng cấp cơ sở mã. Tất nhiên, thực hiện các thay đổi theo cách thủ công là không khả thi nhưng tôi tin rằng vấn đề này có thể được giải quyết một lần và mãi mãi bằng một sedlệnh duy nhất . Mặc dù vậy, tôi đã không thử nó, vì vậy hãy dùng những hạt muối sau đây.

find . -exec sed -E -i .backup -n \
    -e 's/char\s*\*\s*(\w+)\s*= "/char const* \1 = "/g' {} \;

Điều này có thể không tìm thấy tất cả các địa điểm (thậm chí không xem xét các cuộc gọi chức năng) nhưng nó sẽ làm giảm bớt vấn đề và làm cho nó có thể thực hiện một vài thay đổi còn lại bằng tay.


7
điều đó chỉ giải quyết các cảnh báo khai báo và không gọi hàm +1 cho sed fu: p
João Portela

25

Tôi không thể sử dụng chuyển đổi trình biên dịch. Vì vậy, tôi đã biến điều này:

char *setf = tigetstr("setf");

đến đây:

char *setf = tigetstr((char *)"setf");

1
+1 - bạn không thể thay đổi giá trị của các ứng dụng, chỉ có giá trị. Điều này đã được chứng minh để khắc phục vấn đề thực sự. khác chỉ làm việc xung quanh một số vấn đề với trình biên dịch.
elcuco

1
Điều thực sự khó chịu là tigetstr () nên được tạo nguyên mẫu bằng một (const char *), chứ không phải (char *)
vy32

2
Khi tôi làm điều này, tôi nhận được "cảnh báo: chuyển từ loại 'const char *' sang loại 'char *' bỏ đi constness". Tôi đã phải sử dụng const_cast để loại bỏ tất cả các cảnh báo: const_cast <char *> ("setf")
CrouZ

2
Tôi nghĩ rằng const cast là giải pháp được chấp nhận đầu tiên trên trang này (ngoại trừ thay đổi API).
rwst

25

Đây là cách thực hiện nội tuyến trong một tệp, vì vậy bạn không phải sửa đổi Makefile của mình.

// gets rid of annoying "deprecated conversion from string constant blah blah" warning
#pragma GCC diagnostic ignored "-Wwrite-strings"

Bạn có thể sau đó ...

#pragma GCC diagnostic pop

25

Thay thế

char *str = "hello";

với

char *str = (char*)"hello";

hoặc nếu bạn đang gọi chức năng:

foo("hello");

thay thế nó bằng

foo((char*) "hello");

15

Thay vì:

void foo(char *s);
foo("constant string");

Những công việc này:

void foo(const char s[]);
foo("constant string");

Đây là cách chính xác để làm điều đó vì bạn không nên chuyển một chuỗi (hằng) cho một hàm mong đợi một chuỗi không cố định nào!
jfla

15

Trong C ++, sử dụng const_castnhư dưới đây

char* str = const_cast<char*>("Test string");

7

Test stringlà chuỗi const. Vì vậy, bạn có thể giải quyết như thế này:

char str[] = "Test string";

hoặc là:

const char* str = "Test string";
printf(str);

4

Tại sao không chỉ sử dụng kiểu đúc?

(char*) "test"

2

Thực hiện typecasting từ chuỗi không đổi đến con trỏ char tức là

char *s = (char *) "constant string";

1

Trong C ++, Thay thế:

char *str = "hello";

với:

std::string str ("hello");

Và nếu bạn muốn so sánh nó:

str.compare("HALLO");

1

Tôi không hiểu cách áp dụng giải pháp của bạn :( - kalmanIsAGameChanger

Làm việc với Arduino Sketch, tôi có một chức năng gây ra các cảnh báo của mình.

Hàm gốc: char StrContains (char * str, char * sfind)

Để ngăn chặn các cảnh báo, tôi đã thêm const ở phía trước char * str và char * sfind.

Đã sửa đổi: char StrContains (const char * str, const char * sfind).

Tất cả các cảnh báo đã biến mất.


Đây là câu trả lời đúng theo cảnh báo đã nói: "cảnh báo: chuyển đổi không dùng nữa từ hằng chuỗi thành 'char *'".
Norbert Boros

0

xem tình huống này:

typedef struct tagPyTypeObject
{
    PyObject_HEAD;
    char *name;
    PrintFun print;
    AddFun add;
    HashFun hash;
} PyTypeObject;

PyTypeObject PyDict_Type=
{
    PyObject_HEAD_INIT(&PyType_Type),
    "dict",
    dict_print,
    0,
    0
};

xem trường tên, trong gcc nó biên dịch mà không có cảnh báo, nhưng trong g ++, tôi sẽ không biết tại sao.


gcc ngụ ý coi tệp là tệp nguồn C, g ++ coi nó là tệp nguồn c ++, trừ khi ghi đè bằng -x ?? Lựa chọn. Vì vậy, ngôn ngữ khác nhau, c và c ++ có sự khác biệt tinh tế về những gì cần cảnh báo.
zhaorufei

0

Bạn cũng có thể tạo một chuỗi có thể ghi từ hằng chuỗi bằng cách gọi strdup().

Chẳng hạn, mã này tạo cảnh báo:

putenv("DEBUG=1");

Tuy nhiên, đoạn mã sau không (nó tạo một bản sao của chuỗi trên heap trước khi chuyển nó tới putenv):

putenv(strdup("DEBUG=1"));

Trong trường hợp này (và có lẽ trong hầu hết những người khác) tắt cảnh báo là một ý tưởng tồi - đó là lý do. Sự thay thế khác (làm cho tất cả các chuỗi có thể ghi theo mặc định) có khả năng không hiệu quả.

Hãy lắng nghe những gì trình biên dịch đang nói với bạn!


6
Và nó cũng rò rỉ bộ nhớ được phân bổ cho chuỗi có thể ghi đó.
RBerteig

1
Có nó - đó là mục đích. Không phải là vấn đề với mã một lần (ví dụ: khởi tạo), như trên. Hoặc, bạn có thể tự quản lý bộ nhớ và giải phóng nó khi bạn hoàn thành nó.
BillAtHRST

1
Trường hợp cụ thể putenv()là đầy đủ - đó không phải là một lựa chọn tốt về ví dụ (ít nhất, không phải không có nhiều thảo luận về những gì putenv()không có trong câu trả lời này). Đó là một cuộc thảo luận hoàn toàn riêng biệt. (Lưu ý rằng đặc tả POSIX cho hành vi của putenv()có vấn đề, dựa trên các triển khai kế thừa từ trước khi POSIX được xác định.) IIRC, có một lỗi trong bản phát hành gần đây (thiên niên kỷ) của Thư viện GNU C có liên quan đến putenv()thay đổi hành vi, và được đổi lại.)
Jonathan Leffler

0

chỉ sử dụng tùy chọn -w cho g ++

thí dụ:

g ++ -w -o đơn giản.o đơn giản.cpp -lpthread

Hãy nhớ điều này không tránh sự phản đối thay vì nó ngăn hiển thị thông báo cảnh báo trên thiết bị đầu cuối.

Bây giờ nếu bạn thực sự muốn tránh sự phản đối, hãy sử dụng từ khóa const như thế này:

const char* s="constant string";  

0

Tại sao bạn không sử dụng -Wno-deprecatedtùy chọn để bỏ qua các thông báo cảnh báo không dùng nữa?


0

Vấn đề bây giờ là tôi đang chạy với -Werror

Đây là vấn đề thực sự của bạn, IMO. Bạn có thể thử một số cách tự động để chuyển từ (char *) sang (const char *) nhưng tôi sẽ đặt tiền cho chúng không chỉ hoạt động. Bạn sẽ phải có một con người tham gia ít nhất một số công việc. Trong thời gian ngắn, chỉ cần bỏ qua cảnh báo (nhưng IMO vẫn tiếp tục hoặc sẽ không bao giờ được sửa) và chỉ cần xóa -Werror.


9
Những người sử dụng lý do -Werror là để cảnh báo làm được cố định. Nếu không họ không bao giờ được sửa chữa.
Zan Lynx

2
Lý do mọi người sử dụng -Werror là vì họ chỉ làm việc trong các dự án đồ chơi, hoặc họ là bạo dâm. Có mã của bạn không được xây dựng vì bản cập nhật GCC là một vấn đề thực sự khi bạn có 100k + LỘC. Dito. ai đó thêm rác như "-Wno-write-String" vào bản dựng để loại bỏ các cảnh báo gây phiền nhiễu (như bình luận được đánh giá cao nhất trong bài đăng này gợi ý).
James Antill

2
có sự bất đồng rõ ràng trong chủ đề đó, ví dụ lập trình
viên.97things.oreilly.com / wiki / index.php / Kẻ

3
@James: Bạn đưa ra một quan điểm thú vị, nhưng phải có một cách tốt hơn. Dường như vô nghĩa khi không sửa các cảnh báo ngay lập tức - làm thế nào để bạn nhận ra khi mã mới đã đưa ra một cảnh báo mới khi bạn chưa xóa tất cả các cảnh báo cũ? Theo kinh nghiệm của tôi, điều đó chỉ dẫn đến việc mọi người bỏ qua các cảnh báo rằng họ không nên bỏ qua.
tộc

2
@James: dự án đồ chơi của chúng tôi là 1,5 + M LỘC (đa ngôn ngữ). Như nobar đã nói, -Werror tránh bỏ qua các cảnh báo không nên và có, mỗi khi một phiên bản mới của trình biên dịch tăng lên, chúng ta phải kiểm tra lại tất cả. -Wno-write-String chỉ được sử dụng khi sử dụng Boost cho trình bao bọc python trong một tệp theo cách tệp, bởi vì chúng tôi sẽ không viết lại Boost (và hiện tại, 2017, chúng tôi không muốn sử dụng Boost nữa nhưng C ++ 11 / con trăn). Mỗi cảnh báo bị bỏ qua sau đó phải được xem xét định kỳ bằng kiểm tra chất lượng để xem liệu bây giờ chúng có thể tránh được bằng mã hay không.
msn

0

Cảm ơn tất cả vì sự giúp đỡ. Chọn từ đây và có giải pháp này. Điều này biên dịch sạch. Chưa kiểm tra mã. Ngày mai ... có lẽ ...

const char * timeServer[] = { "pool.ntp.org" }; // 0 - Worldwide 
#define WHICH_NTP            0 // Which NTP server name to use.
...
sendNTPpacket(const_cast<char*>(timeServer[WHICH_NTP])); // send an NTP packet to a server
...
void sendNTPpacket(char* address) { code }

Tôi biết, chỉ có 1 mục trong mảng timeServer. Nhưng có thể có nhiều hơn. Phần còn lại đã được bình luận ra bây giờ để tiết kiệm bộ nhớ.


-1
PyTypeObject PyDict_Type=
{ ...

PyTypeObject PyDict_Type=
{
  PyObject_HEAD_INIT(&PyType_Type),
                     "dict",
                     dict_print,
                     0,
                     0
}; 

xem trường tên, trong gcc nó biên dịch mà không có cảnh báo, nhưng trong g ++, tôi sẽ không biết tại sao.

trong gcc (Compiling C) , -Wno-write-String được kích hoạt theo mặc định.

trong g++ (Compiling C++) -Wwrite-String được kích hoạt theo mặc định

Đây là lý do tại sao có một hành vi khác nhau. Đối với chúng tôi bằng cách sử dụng macro Boost_pythontạo ra các cảnh báo như vậy. Vì vậy, chúng tôi sử dụng -Wno-write-stringskhi biên dịch C ++ vì chúng tôi luôn sử dụng-Werror


-1

Khai báo chuỗi như constsẽ giải quyết vấn đề:

char const*s = "constant string";
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.