2013-02-05 25 views
24

Gần đây tôi đã bắt đầu lập trình bằng ngôn ngữ C, đến từ Java và Python. Bây giờ, trong cuốn sách của tôi, tôi đã nhận thấy rằng để thực hiện một chương trình "Hello World", cú pháp là một cái gì đó như thế này:C có loại chuỗi không?

char message[10] 
strcpy(message, "Hello, world!") 
printf("%s\n", message); 

Bây giờ, ví dụ này là sử dụng một mảng char và tôi tự hỏi - những gì đã xảy ra với chuỗi? Tại sao tôi không thể sử dụng một trong số đó? Có thể có một cách khác để làm điều này?

+7

C không có chuỗi. – sashoalm

+4

bạn cần thông báo char [14]; – acraig5075

+3

Strcpy của bạn sẽ tràn mảng char của bạn bằng cách này. bạn cần ít nhất một mảng char có chiều dài 14 (13 ký tự + đầu cuối nul) – wich

Trả lời

50

C không và chưa bao giờ có loại chuỗi gốc. Theo quy ước, ngôn ngữ sử dụng các mảng char chấm dứt bằng một ký tự null, nghĩa là, với '\0'. Các hàm và macro trong các thư viện chuẩn của ngôn ngữ cung cấp hỗ trợ cho các mảng ký tự null kết thúc, ví dụ: strlen lặp qua một mảng char cho đến khi nó gặp một ký tự '\0'strcpy bản sao từ chuỗi nguồn cho đến khi gặp số '\0'.

Việc sử dụng các chuỗi bị hủy trong C phản ánh thực tế rằng C được dự định chỉ cao hơn một chút so với ngôn ngữ lắp ráp. Các chuỗi không được chấm dứt đã được hỗ trợ trực tiếp tại thời điểm đó trong assembly language for the PDP-10 and PDP-11.

Điều đáng lưu ý là thuộc tính này của chuỗi C dẫn đến khá nhiều lỗi tràn bộ đệm khó chịu, bao gồm cả lỗi bảo mật nghiêm trọng. Ví dụ, nếu bạn quên null-chấm dứt chuỗi ký tự được chuyển như đối số nguồn thành strcpy, hàm sẽ tiếp tục sao chép các byte tuần tự từ bất kỳ điều gì xảy ra trong bộ nhớ trong quá trình kết thúc chuỗi nguồn cho đến khi nó xảy ra khi gặp phải 0, có khả năng ghi đè bất kỳ thông tin có giá trị nào sau vị trí của chuỗi đích trong bộ nhớ.

Trong ví dụ mã của bạn, chuỗi chữ "Hello, world!" sẽ được biên dịch thành mảng dài 14 byte của char. 13 byte đầu tiên sẽ giữ các chữ cái, dấu phẩy, dấu cách và dấu chấm than và byte cuối cùng sẽ giữ ký tự null-terminator '\0', được trình biên dịch thêm tự động cho bạn. Nếu bạn truy cập phần tử cuối cùng của mảng, bạn sẽ tìm thấy nó bằng 0. Ví dụ:

const char foo[] = "Hello, world!"; 
assert(foo[12] == '!'); 
assert(foo[13] == '\0'); 

Tuy nhiên, trong ví dụ của bạn, message chỉ dài 10 byte. strcpy sẽ viết tất cả 14 byte, bao gồm null-terminator, vào bộ nhớ bắt đầu tại địa chỉ của message. 10 byte đầu tiên sẽ được ghi vào bộ nhớ được cấp phát trên ngăn xếp cho message và bốn byte còn lại sẽ được ghi vào cuối ngăn xếp. Hệ quả của việc viết bốn byte thừa đó lên stack là khó dự đoán trong trường hợp này (trong ví dụ đơn giản này, nó có thể không làm hại gì), nhưng trong mã thực tế, nó thường dẫn đến lỗi dữ liệu hoặc lỗi truy cập bộ nhớ.

+1

Đây là câu trả lời hữu ích nhất, Cảm ơn bạn :) – arielschon12

3

C không hỗ trợ loại chuỗi loại đầu tiên.

C++ đã std :: string

11

Không có stringC. Bạn phải sử dụng mảng char.

Bằng cách mã của bạn sẽ không hoạt động, vì kích thước của mảng nên cho phép toàn bộ mảng phù hợp với cộng thêm một ký tự kết thúc bằng không.

7

Trong C, một chuỗi đơn giản là một mảng các ký tự, kết thúc bằng một byte rỗng. Vì vậy, một char* thường được phát âm là "chuỗi", khi bạn đang đọc mã C.

1

C không có kiểu dữ liệu chuỗi riêng của nó như Java.

Chỉ chúng ta có thể tuyên bố Chuỗi datatype trong C sử dụng mảng ký tự hoặc ký tự con trỏ Ví dụ:

char message[10]; 
or 
char *message; 

Nhưng bạn cần phải khai báo ít nhất:

char message[14]; 

để sao chép "Hello, world ! " vào biến tin nhắn.

  • 13: độ dài của "Hello, world!"
  • 1: đối với ký tự rỗng '\ 0' xác định phần cuối của chuỗi
0

Trước tiên, bạn không cần phải làm tất cả. Cụ thể, strcpy là không cần thiết - bạn không cần sao chép chuỗi chỉ đến printf. Bạn có thể xác định message của mình với chuỗi đó tại chỗ.

Thứ hai, bạn đã không cho phép đủ không gian cho "Hello, World!" chuỗi (message cần phải có ít nhất 14 ký tự, cho phép thêm một ký tự cho null terminator).

Vì sao, đó là lịch sử. Trong lắp ráp, không có chuỗi, chỉ byte, từ, vv Pascal có dây, nhưng có vấn đề với gõ tĩnh vì điều đó - string[20] là một loại khác nhau mà string[40]. Có những ngôn ngữ ngay cả trong những ngày đầu đã tránh được vấn đề này, nhưng điều đó đã gây ra chi phí phân bổ độc lập và năng động, vốn đã trở thành vấn đề hiệu quả hơn nhiều.

C chỉ cần chọn để tránh chi phí vượt quá và ở mức rất thấp. Chuỗi là mảng ký tự. Mảng có liên quan rất chặt chẽ với con trỏ trỏ tới mục đầu tiên của chúng. Khi các kiểu mảng "phân rã" thành kiểu con trỏ, thông tin kích thước bộ đệm bị mất từ ​​kiểu tĩnh, do đó bạn không gặp các vấn đề chuỗi Pascal cũ.

Trong C++, có lớp std::string tránh được nhiều vấn đề này - và có chi phí phân bổ động, nhưng những ngày này, chúng tôi thường không quan tâm đến điều đó. Và trong mọi trường hợp, std::string là một lớp thư viện - có xử lý mảng ký tự kiểu C bên dưới.

4

Để lưu ý nó trong ngôn ngữ mà bạn đề cập:

Java:

String str = new String("Hello"); 

Python:

str = "Hello" 

Cả Java và Python có khái niệm về một "chuỗi", C không không có khái niệm về "chuỗi". C có các mảng ký tự có thể có trong "chỉ đọc" hoặc có thể thao tác.

C:

char * str = "Hello"; // the string "Hello\0" is pointed to by the character pointer 
         // str. This "string" can not be modified (read only) 

hoặc

char str[] = "Hello"; // the characters: 'H''e''l''l''o''\0' have been copied to the 
         // array str. You can change them via: str[x] = 't' 

Một mảng nhân vật là một chuỗi các ký tự tiếp giáp với một nhân vật trọng điểm độc đáo ở phần cuối (thường là một terminator NULL '\0'). Lưu ý rằng ký tự sentinel được tự động thêm vào cho bạn trong các trường hợp trên.