Địa chỉ của hai con trỏ ký tự đến các ký tự chuỗi khác nhau giống nhau


80
#include<stdio.h>
#include<string.h>

int main()
{
    char * p = "abc";
    char * p1 = "abc";
    printf("%d %d", p, p1);
}

Khi tôi in giá trị của hai con trỏ, nó đang in cùng một địa chỉ. Tại sao?


66
Tại sao bạn nghĩ nó không nên? Cả hai con trỏ này đều trỏ đến cùng một thứ. Những gì bạn đang thấy có lẽ là hiệu quả của một kỹ thuật tối ưu hóa được gọi là tổng hợp chuỗi.
Daniel Kamil Kozar

2
Mặc dù dữ liệu giống nhau nhưng các biến khác nhau.
seereddi sekhar

2
Tất nhiên, các biến là khác nhau. Nếu bạn đã sử dụng địa chỉ của pp1, thì bạn sẽ nhận thấy rằng hai con trỏ này được lưu trữ dưới hai địa chỉ khác nhau. Thực tế là giá trị của chúng giống nhau - trong trường hợp này - không liên quan.
Daniel Kamil Kozar

Có, nếu tôi thay đổi các giá trị thì các địa chỉ sẽ khác.
seereddi sekhar

11
@JanHudec: Đọc lại câu hỏi. Trong trường hợp này (do tối ưu hóa trình biên dịch) p == p1(chúng không khác nhau) nhưng &p != &p1(chúng khác nhau).
MSalters

Câu trả lời:


87

Việc hai ký tự chuỗi khác nhau có cùng nội dung được đặt ở cùng một vị trí bộ nhớ hay các vị trí bộ nhớ khác nhau là tùy thuộc vào việc triển khai.

Bạn nên luôn đối xử pp1như hai con trỏ khác nhau (mặc dù họ có cùng một nội dung) khi họ có thể hoặc có thể không trỏ đến cùng một địa chỉ. Bạn không nên dựa vào tối ưu hóa trình biên dịch.

Tiêu chuẩn C11, 6.4.5, ký tự chuỗi, ngữ nghĩa

Không xác định được liệu các mảng này có khác biệt hay không miễn là các phần tử của chúng có giá trị thích hợp. Nếu chương trình cố gắng sửa đổi một mảng như vậy, hành vi không được xác định.


Định dạng để in phải là %p:

  printf("%p %p", (void*)p, (void*)p1);

Xem câu trả lời này để biết tại sao.


Tôi đã sử dụng dễ bay hơi để không có tối ưu hóa bộ nhớ, ngay cả khi chúng có cùng một địa chỉ. Một câu hỏi đặt ra là nếu tôi sửa đổi một trong những con trỏ, thì dữ liệu trong con trỏ kia cũng sẽ được sửa đổi.
Megharaj

8
@Megharaj i modify one of the pointer, will the data in the other pointed also be modifiedBạn có thể sửa đổi con trỏ nhưng không thể sửa đổi chuỗi ký tự. Ví dụ: char *p="abc"; p="xyz";hoàn toàn ổn trong khi char *p="abc"; p[0]='x';gọi hành vi không xác định . Điều này không có gì để làm với volatile. Bạn có sử dụng volatilehay không cũng không nên thay đổi bất kỳ hành vi nào mà chúng tôi quan tâm ở đây. volatilevề cơ bản buộc phải đọc dữ liệu từ bộ nhớ mọi lúc.
PP

2
@MSharathHegde Có. Bởi vì ptrỏ đến chuỗi ký tự "abc"p[0]='x'cố gắng sửa đổi ký tự đầu tiên của một ký tự chuỗi. Cố gắng sửa đổi một chữ chuỗi là hành vi không xác định trong C.
PP

2
@MSharathHegde Bởi vì tiêu chuẩn C nói rằng. Lý do chủ yếu là lịch sử vì ngôn ngữ C tiền tiêu chuẩn cho phép sửa đổi các ký tự chuỗi. Sau đó, tiêu chuẩn C (C89) đã biến nó thành không xác định để mã mới không làm điều đó và mã cũ (trước tiêu chuẩn) hoạt động như chúng vốn có. Về cơ bản, đó là một sự thỏa hiệp để không phá vỡ mã (pre-stanard) hiện có, tôi tin. Một lý do khác là loại chuỗi chữ là char []trong C. Vì vậy, làm cho nó chỉ đọc ( const char*như trường hợp trong C ++) sẽ đòi hỏi chaning các loại là tốt. [tiếp]
PP

7
Có một dòng trong K & R 2nd edition tại Phụ lục C: "Strings are no longer modifiable, and so may be placed in read-only memory", một bằng chứng lịch sử mà xâu sử dụng được sửa đổi ;-)
PP

28

Trình biên dịch của bạn có vẻ khá thông minh, phát hiện ra rằng cả hai chữ đều giống nhau. Và vì các ký tự là không đổi, trình biên dịch quyết định không lưu trữ chúng hai lần.

Có vẻ như điều đáng nói là điều này không nhất thiết phải như vậy. Hãy xem câu trả lời của Blue Moon về điều này .


Btw: Câu printf()lệnh sẽ trông như thế này

printf("%p %p", (void *) p, (void *) p1);

như "%p"sẽ được sử dụng để in các giá trị con trỏ, và nó chỉ được xác định cho loại con trỏ void *. * 1


Ngoài ra, tôi muốn nói rằng mã này thiếu một returncâu lệnh, nhưng tiêu chuẩn C dường như đang trong quá trình thay đổi. Những người khác có thể vui lòng làm rõ điều này.


* 1: Truyền tới void *đây là không cần thiết cho char *con trỏ, nhưng cho con trỏ đến tất cả các loại khác.


Cảm ơn. Vì vậy, kết luận là tối ưu hóa trình biên dịch phải không? trong hàm main C bởi lợi nhuận mặc định 0
seereddi sekhar

@seereddisekhar: Vâng, đó là một loại tối ưu hóa.
alk

2
@seereddisekhar Nhưng hãy cẩn thận, nó không có nghĩa là bạn nên so sánh hai chuỗi (con trỏ chẵn) bằng cách sử dụng ==mà bạn nên sử dụng strcmpy()hàm. Bởi vì trình biên dịch khác có thể không sử dụng tối ưu hóa (đó là trình biên dịch tối đa - thực hiện depentednt) như Alk đã trả lời PS: Blue Moon vừa thêm về nó.
Grijesh Chauhan

2
Kính gửi @Megharaj: Tôi có thể vui lòng đặt câu hỏi riêng về vấn đề này được không? Bạn có thể gửi một liên kết đến câu hỏi mới này ở đây dưới dạng bình luận.
alk

1
@Megharaj: Bạn không thể thay đổi giá trị của một chuỗi ký tự. Như tôi đã đề cập trong câu hỏi của mình, nó không đổi.
alk

18

Trình biên dịch của bạn đã thực hiện một cái gì đó được gọi là "chuỗi tổng hợp". Bạn chỉ định rằng bạn muốn có hai con trỏ, cả hai đều trỏ đến cùng một chuỗi ký tự - vì vậy nó chỉ tạo một bản sao của ký tự.

Về mặt kỹ thuật: Lẽ ra nó phải phàn nàn ở bạn vì đã không tạo ra các con trỏ "const"

const char* p = "abc";

Điều này có thể là do bạn đang sử dụng Visual Studio hoặc bạn đang sử dụng GCC mà không có -Wall.

Nếu bạn thực sự muốn chúng được lưu trữ hai lần trong bộ nhớ, hãy thử:

char s1[] = "abc";
char s2[] = "abc";

Ở đây, bạn tuyên bố rõ ràng rằng bạn muốn hai mảng ký tự c-string thay vì hai con trỏ đến ký tự.

Lưu ý: Tổng hợp chuỗi là một tính năng của trình biên dịch / tối ưu hóa và không phải là một khía cạnh của ngôn ngữ. Vì các trình biên dịch khác nhau như vậy trong các môi trường khác nhau sẽ tạo ra các hành vi khác nhau tùy thuộc vào những thứ như mức độ tối ưu hóa, cờ trình biên dịch và liệu các chuỗi có nằm trong các đơn vị biên dịch khác nhau hay không.


1
gcc (Debian 4.4.5-8) 4.4.5không phàn nàn (cảnh báo), mặc dù đang sử dụng -Wall -Wextra -pedantic.
alk

1
Có, kể từ V4.8.1 gcc theo mặc định không cảnh báo về việc không sử dụng constcho các ký tự chuỗi. Cảnh báo được bật theo tùy chọn -Wwrite-strings. Người ta dường như không được kích hoạt bởi bất kỳ lựa chọn nào khác (chẳng hạn như -Wall, -Wextrahoặc -pedantic).
sleske

1
Cả GCC 4.4.7 và 4.7.2 đều cung cấp cho tôi cảnh báo có hoặc không có -Wall. pastebin.com/1DtYEzUN
kfsone

15

Như những người khác đã nói, trình biên dịch nhận thấy rằng chúng có cùng giá trị và vì vậy quyết định để chúng chia sẻ dữ liệu trong tệp thực thi cuối cùng. Nhưng nó trở nên huyền ảo hơn: khi tôi biên dịch phần sau vớigcc -O

#include<stdio.h>
#include<string.h>

int main()
{
  char * p = "abcdef";
  char * p1 = "def";
  printf("%d %d", p, p1);
}

nó in 4195780 4195783cho tôi. Nghĩa là, p1bắt đầu 3 byte sau p, vì vậy GCC đã nhìn thấy hậu tố phổ biến của def(bao gồm cả dấu \0chấm hết) và thực hiện tối ưu hóa tương tự như bạn đã hiển thị.

(Đây là một câu trả lời vì quá dài để trở thành một bình luận.)


3

Các chuỗi ký tự trong mã được lưu trữ trong một phân đoạn dữ liệu chỉ đọc của mã. Khi bạn viết ra một chuỗi ký tự như "abc", nó thực sự trả về một 'const char *' và nếu bạn có tất cả các cảnh báo trình biên dịch trên đó, nó sẽ cho bạn biết rằng bạn đang truyền tại thời điểm đó. Bạn không được phép thay đổi các chuỗi đó vì lý do bạn đã chỉ ra trong câu hỏi này.


2

Khi bạn tạo một chuỗi ký tự ("abc"), nó sẽ được lưu vào một bộ nhớ, chứa các ký tự chuỗi và sau đó nó sẽ được sử dụng lại nếu bạn tham chiếu đến cùng một chuỗi ký tự, do đó cả hai con trỏ trỏ đến cùng một vị trí, nơi " abc "chuỗi ký tự được lưu trữ.

Tôi đã biết điều này một thời gian trước nên tôi có thể đã không giải thích nó thực sự rõ ràng, xin lỗi.


2

Điều này thực sự phụ thuộc vào trình biên dịch bạn đang sử dụng .

Trong hệ thống của tôi với TC ++ 3.5, nó in hai giá trị khác nhau cho hai con trỏ tức là hai địa chỉ khác nhau .

Trình biên dịch của bạn được thiết kế vì nó sẽ kiểm tra sự tồn tại của bất kỳ giá trị nào trong bộ nhớ và tùy thuộc vào sự tồn tại của nó, nó sẽ gán lại hoặc sử dụng cùng một tham chiếu của giá trị đã lưu trữ trước đó nếu cùng một giá trị được tham chiếu.

Vì vậy, đừng nghĩ về nó quá nhiều vì nó phụ thuộc vào cách trình biên dịch phân tích mã.

ĐÓ LÀ TẤT CẢ...


1

vì bản thân chuỗi "abc" là một địa chỉ trong bộ nhớ. khi bạn viết "abc" một lần nữa nó lưu cùng một địa chỉ


1

Đó là tối ưu hóa trình biên dịch nhưng quên tối ưu hóa cho tính di động. Đôi khi mã đã biên dịch dễ đọc hơn mã thực.


0

bạn đang sử dụng chuỗi ký tự,

khi trình biên dịch bắt hai chuỗi ký tự giống nhau,

nó cung cấp cùng một vị trí bộ nhớ, do đó nó hiển thị cùng một vị trí con trỏ. /

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.