2009-07-19 34 views
17

Trong C, các chuỗi được kết thúc bằng null (\ 0) gây ra vấn đề khi bạn muốn đặt một null vào một chuỗi. Tại sao không có một ký tự thoát đặc biệt như \ $ hay gì đó?Tại sao C không chấm dứt chuỗi với ký tự kết thúc chuỗi ký tự thoát đặc biệt?

Tôi hoàn toàn nhận thức được câu hỏi này câm thế nào, nhưng tôi đã tò mò.

+5

Điều gì xảy ra khi bạn muốn đặt \ $ vào chuỗi? –

+1

Sau đó, bạn thoát khỏi nhân vật trốn thoát, tất nhiên! –

+4

@Bryan: Bạn không thể thoát khỏi một nhân vật, bạn chỉ có thể thoát khỏi sự thể hiện mã nguồn của một nhân vật. Bạn không thể sử dụng ký tự nào mà bạn sử dụng làm chấm dứt trong chuỗi. – Guffa

Trả lời

39

Chấm dứt bằng 0 có nhiều hiệu ứng hoạt động tốt, có rất nhiều liên quan trở lại vào cuối những năm 60.

CPU có hướng dẫn nhảy có điều kiện về thử nghiệm cho 0. Trong thực tế, một số CPU thậm chí có hướng dẫn mà sẽ lặp/sao chép một chuỗi các byte đến 0.

Nếu bạn sử dụng một nhân vật thoát thay vào đó, bạn có hai thử nghiệm HAI byte khác nhau để khẳng định sự kết thúc của chuỗi. Không chỉ chậm hơn, nhưng bạn mất khả năng lặp lại một byte tại một thời điểm, vì bạn cần có khả năng xem trước hoặc khả năng quay lại.

Hiện tại, các ngôn ngữ khác (ho, Pascal, ho) sử dụng chuỗi theo kiểu đếm/giá trị. Đối với họ, mọi ký tự đều hợp lệ, nhưng chúng luôn giữ một bộ đếm với kích thước của chuỗi. Lợi thế là rõ ràng, nhưng cũng có những nhược điểm đối với kỹ thuật này.

Đối với một điều, kích thước chuỗi bị giới hạn bởi số byte tính. Một byte cung cấp cho bạn 255 ký tự, hai byte cung cấp cho bạn 65535, v.v. Có thể gần như không liên quan đến ngày hôm nay, nhưng thêm hai byte vào mỗi chuỗi một lần là khá tốn kém.

Edit:

Tôi không nghĩ rằng câu hỏi là câm.Trong những ngày này của các ngôn ngữ cấp cao với bộ nhớ quản lý, sức mạnh CPU đáng kinh ngạc và số tiền khiêu dâm của bộ nhớ, quyết định như vậy từ quá khứ cũng có vẻ có vẻ vô nghĩa. Và, quả thật, hôm nay họ không còn cảm giác vô nghĩa nữa, vì vậy đó là một điều tốt để hỏi họ.

+5

+1 để đề cập đến CPU. "Một số CPU" của bạn bao gồm tập lệnh x86 của Intel (mặc dù có thể những hướng dẫn này không được sử dụng nhiều nữa). – ChrisW

+2

Nếu bạn xác định cấu trúc chuỗi của riêng bạn, Bạn có thể làm cho giá trị 255 của byte kích thước, chỉ ra rằng một byte kích thước khác theo sau. –

+2

Các đặc điểm hiệu suất vẫn còn có liên quan ngày hôm nay trong nhiều tình huống. Nó rất quan trọng trong các hệ thống nhúng và trong quá trình phát triển hạt nhân/trình điều khiển mà bạn vẫn muốn loại bỏ và lưu mọi chu kỳ CPU mà bạn có thể. Đó là lý do tại sao C vẫn là vua ở những khu vực này. – Gerald

13

Bạn cần có một số giá trị byte thực tế để chấm dứt chuỗi - cách bạn thể hiện nó trong mã không thực sự có liên quan.

Nếu bạn sử dụng \$ để chấm dứt chuỗi, giá trị byte nào sẽ có trong bộ nhớ? Bạn sẽ bao gồm giá trị byte đó trong một chuỗi như thế nào?

Bạn sẽ nhấn vấn đề này bất cứ điều gì bạn làm, nếu bạn sử dụng một ký tự đặc biệt để chấm dứt chuỗi. Cách khác là sử dụng các chuỗi tính được tính, theo đó biểu diễn chuỗi bao gồm chiều dài của nó (ví dụ: BSTR).

+0

Được rồi, vì vậy \ $ sẽ trỏ đến một số giá trị hiện không được sử dụng. – akway

+4

Nhưng không có giá trị byte "không sử dụng". Bất kỳ byte nào cũng có thể xuất hiện trong chuỗi C - bạn cũng có thể nói rằng \ 0 đã được chọn vì nó không được sử dụng. – RichieHindle

+0

Như cái gì? Nếu bạn đang sử dụng UTF-8, thì toàn bộ phạm vi được sử dụng. –

2

Tôi đoán vì nó nhanh hơn để kiểm tra và hoàn toàn không thể xảy ra trong một chuỗi hợp lý. Ngoài ra, hãy nhớ rằng C không có khái niệm về chuỗi. Một chuỗi trong C không phải là một cái gì đó của chính nó. Nó chỉ là một mảng các ký tự. Thực tế là nó được gọi và được sử dụng như một chuỗi là hoàn toàn ngẫu nhiên và thông thường.

1

Nó gây ra vấn đề nhưng bạn có thể nhúng một \ 0 ...

const char* hello = "Hello\0World\0\0"; 

Nó gây ra một vấn đề nếu bạn chuyển thông tin này đến một chức năng thư viện chuẩn như strlen, nhưng không phải ngược lại.

Một giải pháp tốt hơn so với bất kỳ ký tự chuỗi chấm dứt có thể là để thêm vào trước theo chiều dài của chuỗi như ...

const char* hello = "\x0BHello World"; 

... đó là cách một số ngôn ngữ khác làm điều đó.

+1

Ví dụ hay, nhưng bạn có thể muốn độ dài chuỗi có tiền tố trong ví dụ của bạn để thực sự phản ánh độ dài của chuỗi? (Tôi nghĩ bạn quên đếm không gian) – jerryjvl

+0

Cảm ơn vì đã chú ý đến điều đó. Tôi đếm đến C, tính lại và quyết định rằng C là một quá nhiều, và sau đó sai lầm đã viết xuống A như thể C trừ 1 là A. Tôi đã sửa chữa nó ngay bây giờ. – ChrisW

+1

Nhắc tôi về những ngày cũ với hằng số Hollerith trong FORTRAN, vì vậy bạn sẽ có một chuỗi như 16HTHIS IS A STRING. Khốn nạn cho bạn nếu bạn bị đánh cắp! Các chuỗi được trích dẫn mới xuất hiện sau này đẹp hơn nhiều. –

-1

Không có lý do gì để một ký tự nul là một phần của chuỗi ngoại trừ một trình kết thúc; nó không có biểu diễn đồ họa, vì vậy bạn sẽ không nhìn thấy nó, cũng không hoạt động như một nhân vật điều khiển. Theo như văn bản có liên quan, nó là giá trị ngoài băng như bạn có thể nhận được mà không cần sử dụng một biểu diễn khác (ví dụ: một giá trị nhiều byte như 0xFFFF).

Để thay đổi một chút câu hỏi của Michael, bạn sẽ trông đợi "Hello \ 0World \ 0" như thế nào?

+0

Làm thế nào để bạn đại diện trong bộ nhớ một túi dữ liệu nhị phân, có thể chứa NUL? Câu trả lời C, về cơ bản, là "sử dụng mem * thói quen". Và nếu bạn cần lưu trữ độ dài, nó tiếp tục với "sau đó phát minh ra cách riêng của bạn để lưu trữ độ dài nếu bạn cần như vậy, và viết trình bao bọc cho các chức năng * mem bạn cần". – Blaisorblade

+0

Có rất nhiều lý do bạn có thể có một số không byte trong một mảng byte, hoặc - vì C sử dụng 'char' thay vì 'byte' - một mảng ký tự. Chỉ cần nhớ không coi đây là một chuỗi và bạn sẽ ổn thôi. Một "chuỗi C" là một mảng char kết thúc bằng null, mặc dù nó thực tế không phải là kiểu dữ liệu riêng của nó. Đó có thể là nguồn gốc của sự nhầm lẫn. –

0

Nếu chức năng thư viện chuẩn như strlen hoặc printf có thể (tùy chọn khôn ngoan) hãy tìm điểm đánh dấu chuỗi \ 777 (thay thế cho \ 000), bạn có thể có chuỗi ký tự không đổi chứa \ 0s:

const char* hello = "Hello\0World\0\0\777"; 
printf("%s\n", hello); 

Bằng cách này, nếu bạn muốn gửi một \ 0 đến stdout (aka print0), bạn có thể sử dụng:

putchar(0); 
0

Ditto trên những lý do lịch sử.

Những người sáng tạo của std :: string in C++ nhận ra lỗi này, vì vậy std :: string có thể bao gồm ký tự null. (Nhưng hãy cẩn thận constructing a std::string with a null character!)

Nếu bạn muốn có một chuỗi C (hoặc đúng hơn là một chuỗi C) với ký tự rỗng, bạn sẽ phải tạo ra cấu trúc của riêng mình.

typedef struct { 
    size_t length; 
    char[] data; //C99 introduced the flexible array member 
} my_string; 

Hoặc bạn sẽ phải theo dõi độ dài chuỗi theo cách nào đó và chuyển nó cho mọi chức năng chuỗi mà bạn viết.

0

Không cố ý đăng bài, nhưng điều này vẫn có liên quan cao đối với SQL nhúng.

Nếu bạn đang xử lý dữ liệu nhị phân trong C, bạn nên tạo một đối tượng nhị phân trong cấu trúc dữ liệu. Nếu bạn có thể đủ khả năng đó, một mảng của char sẽ đủ. Nó có lẽ không phải là một chuỗi anyway, phải không?

Đối với giá trị băm/tiêu hóa, thông thường "HEX" chúng thành các thành viên của {'0', .., 'F'}. Sau đó, chúng có thể được "UNHEXED" trong khi thao tác với cơ sở dữ liệu.

Đối với các thao tác tệp, hãy xem xét luồng nhị phân, có độ dài bản ghi logic.

Tự mình thoát ra chỉ thực sự an toàn nếu bạn có thể đảm bảo mã hóa. Trong thực tế, điều này có thể được nhìn thấy trong một tải trọng MYSQLDUMP (SQL) nơi các tệp nhị phân được thoát đúng cách cho UTF-8 nói, và lược đồ cài đặt được 'đẩy' cho tải và 'popped' sau đó.

Tôi không chủ trương sử dụng cuộc gọi dbms cho chức năng thư viện nào, nhưng tôi đã thấy nó hoàn thành. (chọn real_escape_string ($ string)).

Và có base64, một loại sâu khác. Google UUENCODE.

Vì vậy, đúng vậy, chức năng mem * nếu các ký tự của bạn có chiều rộng cố định.

Các vấn đề liên quan