Đọc một chuỗi với scanf


147

Tôi hơi bối rối về một cái gì đó. Tôi có ấn tượng rằng cách đọc đúng chuỗi C scanf()đã đi dọc theo dòng

(đừng bận tâm đến lỗi tràn bộ đệm có thể xảy ra, đây chỉ là một ví dụ đơn giản)

char string[256];
scanf( "%s" , string );

Tuy nhiên, những điều sau đây dường như cũng hoạt động,

scanf( "%s" , &string );

Đây chỉ là trình biên dịch của tôi (gcc), may mắn thuần túy, hay cái gì khác?


Trong trường hợp thứ hai, thực sự không có khả năng tràn bộ đệm, vì bạn hoàn toàn không sử dụng bộ đệm đó. Hoặc, hoặc bạn có thể nói rằng bất kỳ chuỗi nào lớn hơn 3 ký tự sẽ tràn "bộ đệm" của bạn.
TED

Tôi đã tham khảo ví dụ đầu tiên. Ngoài ra, những người khác đã chỉ ra những gì đang xảy ra ở đây.
hủy bỏ

Vâng Đã thử nó, và Gareth đã đúng. Kỳ dị.
TED

Vì câu hỏi này được trả về bằng cách tìm kiếm "cách đọc chuỗi bằng scanf", nên có thể chỉ ra rằng câu hỏi này là về cách chuyển con trỏ đến bộ đệm scanfvà cả câu hỏi và câu trả lời được chấp nhận tập trung vào rằng, và bỏ qua các hạn chế cực kỳ quan trọng đối với độ dài đầu vào tối đa nên được sử dụng trong mã thực (nhưng bên cạnh điểm cho câu hỏi này).
Arkku

Câu trả lời:


141

Một mảng "phân rã" thành một con trỏ tới phần tử đầu tiên của nó, do đó scanf("%s", string)tương đương với scanf("%s", &string[0]). Mặt khác, scanf("%s", &string)chuyển một con trỏ tới char[256], nhưng nó trỏ đến cùng một vị trí.

Sau đó scanf, khi xử lý phần đuôi của danh sách đối số của nó, sẽ cố gắng rút ra a char *. Đó là điều đúng khi bạn thông qua stringhoặc &string[0], nhưng khi bạn đã thông qua &stringtùy thuộc vào điều gì đó mà tiêu chuẩn ngôn ngữ không đảm bảo, đó là con trỏ &string&string[0]- con trỏ tới các đối tượng thuộc các loại và kích cỡ khác nhau bắt đầu tại cùng một nơi - được thể hiện theo cùng một cách.

Tôi không tin rằng tôi đã từng gặp một hệ thống không hoạt động và trên thực tế có lẽ bạn an toàn. Dù sao đi nữa, nó đã sai và nó có thể thất bại trên một số nền tảng. (Ví dụ giả thuyết: một triển khai "gỡ lỗi" bao gồm thông tin loại với mọi con trỏ. Tôi nghĩ rằng việc triển khai C trên Biểu tượng "Máy Lisp" đã làm một cái gì đó như thế này.)


5
+1. Cũng rất đơn giản để xác minh rằng phân rã mảng dẫn đến hoạt &stringđộng giống như string(thay vì dẫn đến hỏng bộ nhớ ngẫu nhiên, như các câu trả lời khác khẳng định không chính xác):printf("%x\n%x\n", string, &string);
Josh Kelley

@Josh Kelley thú vị, tôi sẽ hiểu rằng đây không phải là trường hợp con trỏ tới một chuỗi được phân bổ thông qua malloc ()?
hủy bỏ

4
@abeln: Đúng. Đối với một chuỗi được phân bổ thông qua malloc (), stringlà một con trỏ và &stringlà địa chỉ của con trỏ đó, vì vậy hai chuỗi này không thể thay thế cho nhau. Như Gareth giải thích, ngay cả đối với trường hợp phân rã mảng string&stringvề mặt kỹ thuật không phải là cùng loại (mặc dù chúng có thể hoán đổi cho hầu hết các kiến ​​trúc), và gcc sẽ đưa ra cảnh báo cho ví dụ thứ hai của bạn nếu bạn bật -Wall.
Josh Kelley

@Josh Kelley: cảm ơn, tôi rất vui vì tôi có thể giải tỏa tâm trí của mình về điều này.
hủy bỏ

1
@JCooper str, & str [0] đều đại diện cho cùng một thứ (bắt đầu của mảng) và mặc dù không nhất thiết & str cũng là cùng một địa chỉ bộ nhớ. Bây giờ strPtr trỏ đến str để địa chỉ bộ nhớ được lưu trữ bên trong strPtr giống với str và đó là 12fe60. Cuối cùng & strPtr là địa chỉ của biến strPtr, đây không phải là giá trị lưu trữ trong strptr mà là địa chỉ bộ nhớ thực của strPtr. Vì strptr là một biến khác với tất cả các biến khác, nó cũng có một địa chỉ bộ nhớ khác, trong trường hợp này là 12fe54
Juan Besa

-9

Tôi nghĩ rằng điều này dưới đây là chính xác và nó có thể giúp đỡ. Hãy sửa nó nếu bạn tìm thấy bất kỳ lỗi nào. Tôi mới đến C.

char str[]  
  1. mảng các giá trị của kiểu char, với địa chỉ riêng trong bộ nhớ
  2. mảng các giá trị của kiểu char, với địa chỉ riêng trong bộ nhớ có nhiều địa chỉ liên tiếp như các phần tử trong mảng
  3. bao gồm cả ký tự null chấm dứt '\0' &str, &str[0]str, cả ba đại diện cho cùng một vị trí trong bộ nhớ đó là địa chỉ của phần tử đầu tiên của mảngstr

    char * strPtr = & str [0]; // khai báo và khởi tạo

cách khác, bạn có thể chia cái này thành hai:

char *strPtr; strPtr = &str[0];
  1. strPtr là một con trỏ tới char
  2. strPtr điểm tại mảng str
  3. strPtr là một biến có địa chỉ riêng trong bộ nhớ
  4. strPtr là một biến lưu trữ giá trị của địa chỉ &str[0]
  5. strPtr địa chỉ riêng trong bộ nhớ khác với địa chỉ bộ nhớ mà nó lưu trữ (địa chỉ của mảng trong bộ nhớ aka & str [0])
  6. &strPtr đại diện cho địa chỉ của strPtr

Tôi nghĩ rằng bạn có thể khai báo một con trỏ tới một con trỏ là:

char **vPtr = &strPtr;  

khai báo và khởi tạo với địa chỉ của con trỏ strPtr

Ngoài ra, bạn có thể chia làm hai:

char **vPtr;
*vPtr = &strPtr
  1. *vPtr chỉ vào con trỏ strPtr
  2. *vPtr là một biến có địa chỉ riêng trong bộ nhớ
  3. *vPtr là một biến lưu trữ giá trị của địa chỉ & strPtr
  4. bình luận cuối cùng: bạn không thể làm str++, strđịa chỉ là một const, nhưng bạn có thể làmstrPtr++
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.