Có thể sửa đổi một chuỗi ký tự trong C không?


81

Tôi đã vật lộn trong vài giờ với tất cả các loại hướng dẫn C và sách liên quan đến con trỏ nhưng điều tôi thực sự muốn biết là liệu có thể thay đổi con trỏ char sau khi nó được tạo hay không.

Đây là những gì tôi đã thử:

Vậy có cách nào để thay đổi các giá trị bên trong chuỗi thay vì địa chỉ con trỏ không?

Câu trả lời:


158

Khi bạn viết một "chuỗi" trong mã nguồn của mình, nó sẽ được ghi trực tiếp vào tệp thực thi vì giá trị đó cần được biết tại thời điểm biên dịch (có sẵn các công cụ để tách phần mềm ra và tìm tất cả các chuỗi văn bản thuần túy trong đó). Khi bạn viết char *a = "This is a string", vị trí của "Đây là một chuỗi" nằm trong tệp thực thi và vị trí atrỏ đến, nằm trong tệp thực thi. Dữ liệu trong hình ảnh thực thi ở chế độ chỉ đọc.

Những gì bạn cần làm (như các câu trả lời khác đã chỉ ra) là tạo bộ nhớ đó ở một vị trí không chỉ được đọc - trên heap hoặc trong khung ngăn xếp. Nếu bạn khai báo một mảng cục bộ, thì không gian được tạo trên ngăn xếp cho mỗi phần tử của mảng đó và chuỗi ký tự (được lưu trữ trong tệp thực thi) được sao chép vào không gian đó trong ngăn xếp.

bạn cũng có thể sao chép dữ liệu đó theo cách thủ công bằng cách phân bổ một số bộ nhớ trên heap, sau đó sử dụng strcpy()để sao chép một chuỗi ký tự vào không gian đó.

Bất cứ khi nào bạn phân bổ không gian bằng cách sử dụng malloc()nhớ gọi free()khi bạn hoàn thành nó (đọc: rò rỉ bộ nhớ).

Về cơ bản, bạn phải theo dõi xem dữ liệu của mình đang ở đâu. Bất cứ khi nào bạn viết một chuỗi trong nguồn của mình, chuỗi đó chỉ được đọc (nếu không, bạn sẽ có khả năng thay đổi hành vi của tệp thực thi - hãy tưởng tượng nếu bạn đã viết char *a = "hello";và sau đó đổi a[0]thành 'c'. Sau đó, ở một nơi khác đã viết printf("hello");. Nếu bạn được phép thay đổi ký tự của "hello"và trình biên dịch của bạn chỉ lưu trữ nó một lần (nó nên), sau đó printf("hello");sẽ xuất ra cello!)


12
Phần cuối cùng đã giải thích cho tôi rất nhiều về lý do tại sao cần phải đọc. Cảm ơn bạn.
CDR

1
-1: không yêu cầu sử dụng const char * và không có gì đảm bảo rằng các chuỗi ký tự được lưu trữ trong bộ nhớ thực thi.
Bastien Léonard

Tôi không cần bạn không cần const cho hai giải pháp tôi đã đưa ra - ngoài ra, nếu chuỗi được biết tại thời điểm biên dịch và được biên dịch thành tệp thực thi - nó sẽ được lưu trữ ở đâu khác? Trong gcc, nếu tôi viết char * a = "hallo."; hoặc char b [] = "hello." ;, thì hợp ngữ xuất ra "LC0: .ascii" Hallo. \ 0 "LC1: .ascii" Hello. \ 0 "" cả hai đều nằm trong bộ nhớ thực thi ... Khi nào thì không ?
Carson Myers

1
Vừa thử với GCC 4.4, nó đặt các chuỗi theo nghĩa đen trong .rodata (dữ liệu chỉ đọc). Tôi đã kiểm tra bằng objdump và danh sách lắp ráp. Tôi không nghĩ rằng tiêu chuẩn yêu cầu các chuỗi chữ phải ở chế độ chỉ đọc, vì vậy tôi nghĩ chúng thậm chí có thể được đặt trong .data.
Bastien Léonard

Ngoài ra, tôi không thấy bất kỳ lợi thế nào trong việc không định tính con trỏ là const. Nó có thể ẩn lỗi nếu sau này bạn quyết định thay đổi chuỗi.
Bastien Léonard

29

Không, bạn không thể sửa đổi nó, vì chuỗi có thể được lưu trữ trong bộ nhớ chỉ đọc. Nếu bạn muốn sửa đổi nó, bạn có thể sử dụng một mảng để thay thế, ví dụ:

Hoặc cách khác, bạn có thể cấp phát bộ nhớ bằng cách sử dụng malloc, ví dụ:


5
Để hoàn thành mã, sẽ rất tốt nếu bạn cũng có thể thêm cuộc gọi miễn phí ().
Naveen

15

Rất nhiều người nhầm lẫn về sự khác biệt giữa char * và char [] kết hợp với chuỗi ký tự trong C. Khi bạn viết:

... bạn thực sự đang trỏ foo tới một khối bộ nhớ không đổi (trên thực tế, những gì trình biên dịch làm với "hello world" trong trường hợp này là phụ thuộc vào việc triển khai).

Thay vào đó, sử dụng char [] cho trình biên dịch biết rằng bạn muốn tạo một mảng và điền vào nó với nội dung, "hello world". foo là một con trỏ đến chỉ mục đầu tiên của mảng char. Cả hai đều là con trỏ char, nhưng chỉ char [] sẽ trỏ đến một khối bộ nhớ được cấp phát cục bộ và có thể thay đổi.


7

Bộ nhớ cho a & b không do bạn cấp phát. Trình biên dịch có thể tự do chọn vị trí bộ nhớ chỉ đọc để lưu các ký tự. Vì vậy, nếu bạn cố gắng thay đổi nó có thể dẫn đến lỗi seg. Vì vậy, tôi khuyên bạn nên tự tạo một mảng ký tự. Cái gì đó như:char a[10]; strcpy(a, "Hello");


1
Vấn đề với mảng ký tự là tôi đang chuyển một con trỏ của mảng char tới một hàm để tôi có thể thao tác một chuỗi ở đó và sau đó gửi lại. Có vẻ như tôi phải sử dụng malloc không may.
Matthew Stopa

1
Không, bạn vẫn có thể sử dụng đối tượng được phân bổ trên ngăn xếp. Ví dụ nếu bạn có một hàm void f (char * p); thì từ main () bạn có thể truyền f (a). Điều này sẽ chuyển địa chỉ của ký tự đầu tiên cho hàm. Ngoài ra, nếu bạn quyết định sử dụng malloc () thì đừng quên giải phóng bộ nhớ bằng cách sử dụng free ().
Naveen

5

Có vẻ như câu hỏi của bạn đã được trả lời nhưng bây giờ bạn có thể thắc mắc tại sao char * a = "String" được lưu trữ trong bộ nhớ chỉ đọc. Chà, nó thực sự không được xác định bởi tiêu chuẩn c99 nhưng hầu hết các trình biên dịch chọn nó theo cách này cho các trường hợp như:

tiêu chuẩn c99 (pdf) [trang 130, mục 6.7.8]:

Khai báo:

định nghĩa các đối tượng mảng char "đơn giản" s và t có các phần tử được khởi tạo bằng các ký tự chuỗi ký tự. Khai báo này giống với char

Nội dung của các mảng có thể thay đổi được. Mặt khác, tuyên bố

định nghĩa p với kiểu "con trỏ tới char" và khởi tạo nó để trỏ đến một đối tượng có kiểu "mảng char" với độ dài 4 có các phần tử được khởi tạo bằng một chuỗi ký tự. Nếu cố gắng sử dụng p để sửa đổi nội dung của mảng, thì hành vi đó là không xác định.


4

Bạn cũng có thể sử dụng strdup:

Đối với bạn ví dụ:


Không phải là câu trả lời cho câu hỏi, nhưng vẫn là một chức năng rất tiện dụng, cảm ơn!
mknaf

1
+1 vì đã dạy tôi về strdup. Tôi không chắc khi nào tôi muốn sử dụng nó.
Z boson

Khi bạn làm một cái gì đó chẳng hạn var = malloc(strlen(str) + 1); strcpy(var, str);, thì bạn có thể nên sử dụng strdupthay thế.
Maxime Chéramy

3

Tất cả đều là những câu trả lời hay giải thích lý do tại sao bạn không thể sửa đổi các ký tự chuỗi vì chúng được đặt trong bộ nhớ chỉ đọc. Tuy nhiên, khi đẩy đến xô đẩy, có một cách để làm điều này. Kiểm tra ví dụ này:

Tôi đã viết điều này như một phần của những suy nghĩ có phần sâu sắc hơn của tôi về tính đúng đắn , mà bạn có thể thấy thú vị (tôi hy vọng :)).

Hy vọng nó giúp. Chúc may mắn!


Lưu ý rằng thay đổi một ký tự chuỗi là hành vi không xác định.
Steohan

0

Bạn cần sao chép chuỗi vào một vùng đệm bộ nhớ khác, không chỉ đọc và sửa đổi nó ở đó. Sử dụng hàm strncpy () để sao chép chuỗi, strlen () để phát hiện độ dài chuỗi, malloc () và free () để cấp phát động bộ đệm cho chuỗi mới.

Ví dụ (C ++ giống như mã giả):


0

6
Malloc cần thêm 1 byte. Đừng quên ký tự kết thúc NULL, mà strcpy mong đợi và cũng sẽ sao chép. Đây là một sai lầm quá thường xuyên.
xcramps
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.