2012-04-06 40 views
9

Tôi là một người mới bắt đầu bằng C, và tôi đã chơi với C. Tôi gõ một mã C như thế này:Tại sao chương trình C này biên dịch mà không có lỗi?

#include <stdio.h> 
int main() 
{ 
    printf("hello world\n"); 
    \ 
    return 0; 
} 

Mặc dù tôi đã sử dụng \ cố ý, trình biên dịch C không ném bất kỳ lỗi. Biểu tượng này được sử dụng cho ngôn ngữ C là gì?

Edit:

Ngay cả công trình này:

"\n"; 
+0

"\ n"; đó là một tuyên bố mà không có bất kỳ ảnh hưởng nào. Nó hoàn toàn bị bỏ qua bởi trình biên dịch và tạo ra một cảnh báo. Hãy thử biên dịch với gcc -Wall flag cho phép cảnh báo biên dịch. – dAm2K

+3

Câu hỏi chính của bạn đã được trả lời nhiều lần. Trong tham chiếu đến phần về '" \ n ";', một chương trình c là (nhiều hơn hoặc ít hơn) một danh sách các câu lệnh. Giá trị bằng chữ (như '3', hoặc' "\ n" ', hoặc' "hamburger" ') là một câu lệnh hoàn toàn hợp lệ, mặc dù nó không _do_ bất cứ điều gì. – jpm

Trả lời

0

Các dấu chéo ngược \ được giải thích bằng vi xử lý C. Nó bảo vệ nhân vật sau của nó (nhân vật dòng mới trong trường hợp của bạn).

+1

Thực ra, nó trái ngược với 'bảo vệ'; nó đảm bảo rằng cả dấu gạch chéo ngược và dòng mới sẽ bị xóa. –

0

Dấu gạch chéo ngược đơn giản là thoát ký tự tiếp theo. Trong trường hợp này, có thể là ký tự kết thúc dòng (CR). Hoàn toàn hợp lý.

+0

Dấu thoát trong C chỉ được bật bên trong chuỗi. –

+0

Thật vậy, bộ tiền xử lý chỉ có thể thoát khỏi ký tự cuối dòng. Xin lỗi vì gây hiểu nhầm. Câu trả lời của dAm2k ở trên là chính xác hơn. – rainecc

+0

@MatteoItalia: Giai đoạn 2 của bản dịch là: "Mỗi thể hiện của một dấu chéo ngược (\\) ngay lập tức theo sau là một ký tự dòng mới bị xóa, nối các dòng nguồn vật lý để tạo thành các dòng nguồn logic." (§5.1.2.2/1.1). –

11

Chuỗi dấu gạch ngang ngược dòng mới được xóa khỏi mã trong giai đoạn rất sớm (giai đoạn 2) của quá trình dịch. Nó từng là cách bạn tạo các chuỗi ký tự chuỗi dài trước khi có chuỗi nối và là cách bạn vẫn mở rộng các macro trên nhiều dòng.

Xem §5.1.1.2 giai đoạn dịch của tiêu chuẩn C99:

Các ưu tiên giữa các quy tắc cú pháp của bản dịch được xác định bởi các giai đoạn sau. 5)

  1. Physical ký tự file nguồn multibyte được ánh xạ, trong một thực hiện được xác định cách, để các bộ ký tự nguồn (giới thiệu nhân vật mới-line cho chỉ số end-of-line) nếu cần thiết. Các chuỗi Trigraph được thay thế bằng các biểu diễn bên trong một ký tự tương ứng.
  2. Mỗi trường hợp của một ký tự dấu chéo ngược (\) ngay lập tức theo sau là một dòng mới ký tự bị xóa, nối các dòng nguồn vật lý để tạo thành các dòng nguồn logic. Chỉ có dấu gạch chéo ngược cuối cùng trên bất kỳ dòng nguồn vật lý nào đủ điều kiện để là một phần của mối nối đó. Tệp nguồn không trống sẽ kết thúc bằng một ký tự dòng mới, không được đặt trước ngay trước ký tự dấu gạch chéo ngược trước khi bất kỳ sự ghép nối nào xảy ra như vậy.
  3. Tệp nguồn được phân tách thành mã thông báo tiền xử lý 6) và trình tự của ký tự khoảng trắng (bao gồm cả nhận xét). Tệp nguồn không được kết thúc bằng mã thông báo tiền xử lý một phần hoặc trong một nhận xét từng phần. Mỗi nhận xét được thay thế bằng một ký tự khoảng trắng. Các ký tự dòng mới được giữ lại. Cho dù mỗi chuỗi không rỗng của các ký tự trắng không phải là dòng mới được giữ lại hoặc thay thế bằng một ký tự khoảng trắng được xác định thực hiện.
  4. Chỉ thị tiền xử lý được thực thi, các lời gọi macro được mở rộng và _Pragma biểu thức toán tử đơn nhất được thực hiện. Nếu chuỗi ký tự khớp với cú pháp của tên ký tự toàn cục được tạo bởi mã thông báo nối (6.10.3.3), hành vi không xác định.Chỉ thị tiền xử lý #include tiền xử lý làm cho tiêu đề hoặc tệp nguồn được đặt tên được xử lý từ giai đoạn 1 đến giai đoạn 4, đệ quy. Tất cả các chỉ thị tiền xử lý sau đó sẽ bị xóa.
  5. Mỗi bộ ký tự nguồn thành viên và chuỗi thoát trong các hằng số ký tự và các chuỗi ký tự được chuyển đổi thành thành viên tương ứng của ký tự thực hiện được đặt; nếu không có thành viên tương ứng, nó được chuyển thành một thành viên được xác định khác với ký tự rỗng (rộng). 7)
  6. Mã thông báo bằng chuỗi liền kề được ghép nối.
  7. Ký tự khoảng trắng tách các mã thông báo không còn đáng kể. Mỗi mã thông báo tiền xử lý được chuyển đổi thành mã thông báo. Các mã thông báo kết quả là được phân tích cú pháp và ngữ nghĩa và được dịch như một đơn vị dịch thuật.
  8. Tất cả các tham chiếu đối tượng và chức năng bên ngoài được giải quyết. Các thành phần của thư viện là được liên kết để đáp ứng các tham chiếu bên ngoài đối với các hàm và đối tượng không được xác định trong bản dịch hiện tại . Tất cả các đầu ra dịch giả như vậy được thu thập vào một hình ảnh chương trình chứa thông tin cần thiết để thực hiện trong môi trường thực thi của nó.

5) Triển khai sẽ hoạt động như thể những giai đoạn riêng biệt này xảy ra, mặc dù nhiều giai đoạn thường được gấp lại trong thực tế.

6) Như được mô tả trong 6.4, quá trình chia các ký tự của tệp nguồn thành mã thông báo tiền xử lý là phụ thuộc vào ngữ cảnh. Ví dụ: xem việc xử lý < trong chỉ thị tiền xử lý #include.

7) Yêu cầu triển khai không chuyển đổi tất cả các ký tự nguồn không tương ứng thành cùng một ký tự thực hiện .

Nếu bạn có một ký tự trống hoặc bất kỳ ký tự nào khác sau dấu gạch chéo ngược, bạn sẽ gặp phải lỗi biên dịch. Chúng tôi có thể nói rằng bạn không có bất cứ điều gì sau đó bởi vì bạn không có lỗi biên dịch.


Các phần khác của câu hỏi của bạn, về:

"\n"; 

là khá khác nhau. Nó là một biểu thức đơn giản mà không có tác dụng phụ và do đó không có hiệu lực trên chương trình. Trình tối ưu hóa sẽ loại bỏ hoàn toàn nó. Khi bạn viết:

i = 1; 

bạn có biểu thức có giá trị bị hủy; nó được đánh giá về tác dụng phụ của việc sửa đổi i.

Đôi khi, bạn sẽ thấy mã như:

*ptr++; 

Trình biên dịch sẽ cảnh báo bạn rằng kết quả của biểu thức bị loại bỏ; biểu thức có thể được đơn giản hóa thành:

ptr++; 

và sẽ đạt được hiệu quả tương tự trong chương trình.

+0

Trong giai đoạn một, "chỉ báo kết thúc dòng" được thực hiện xác định. Việc chuyển vùng trắng có thể được coi là một phần của chỉ báo cuối dòng, vì vậy trong giai đoạn 2, dấu gạch chéo ngược sẽ được theo sau ngay lập tức là một dòng mới, mặc dù có khoảng trắng trong đầu vào gốc. –

+0

@JerryCoffin: Về lý thuyết, bạn đã đúng. Trong thực tế, tuyên bố đơn giản của tôi áp dụng cho tất cả các môi trường hiện đại mà tôi từng nghe đến. Bạn có một ví dụ truy cập rõ ràng trong đó trình biên dịch C loại bỏ các khoảng trống cuối trước khi thực hiện nối tiếp dấu gạch chéo ngược hay không? (Các quy tắc chính xác từ tiêu chuẩn C99 được trích dẫn; bất kỳ nhận xét nào tôi đưa ra là tiêu chuẩn thứ hai và các lỗi trong quá trình triển khai đang được sử dụng.) –

+0

Không, không phải hiện tại, mặc dù dường như tôi nhớ lại một số quy tắc cũ một phần của thời gian. –

0

Dấu gạch chéo ngược cộng với nội dung sau đây là escape sequence; "\ n" cùng với nhau là ký tự dòng mới (in dòng mới). Một điều quan trọng khác là "\ t", cho tab.

+0

Chuỗi thoát chỉ xuất hiện trong chuỗi ký tự và ký tự. Dấu gạch chéo ngược trong câu hỏi không nằm trong chuỗi hoặc ký tự theo nghĩa đen. –

4

\, khi ngay lập tức theo sau dòng mới, được tiêu thụ bằng cách xử lý trước và làm cho dòng "vật lý" tiếp theo được nối với dòng lôgic hiện tại. Điều này rất quan trọng đối với việc viết các chỉ thị tiền xử lý lâu dài, mà phải là tất cả trên một dòng logic:

#define SHORT very log macro \ 
    consisting of lots and \ 
    lots of preprocessor \ 
    tokens 

Nếu bạn loại bỏ các chuỗi dấu gạch chéo ngược dòng mới, nó không còn chính xác nữa. Một số ngôn ngữ khác từ nền văn hóa Unix có cú pháp tiếp tục đường gạch chéo ngược tương tự: ngôn ngữ shell POSIX có nguồn gốc từ trình bao Bourne và cũng là các tệp makefiles.

$ this is \ 
one shell command 

Giới thiệu "\n";, là biểu thức chính được sử dụng để tạo biểu thức tuyên bố. Trong C, các biểu thức có thể được sử dụng như các câu lệnh, và điều này được khai thác mọi lúc. Ví dụ: cuộc gọi printf của bạn là một câu lệnh biểu thức. printf("hello world\n") là biểu thức postfix gọi hàm, nhận giá trị trả về. Bởi vì bạn đã sử dụng biểu thức này như một câu lệnh, giá trị trả lại được bỏ đi. Giá trị trả về của printf cho biết số lượng ký tự được in hoặc liệu nó có thành công hay không, do đó, bằng cách vứt nó đi, chương trình của bạn tự không biết liệu cuộc gọi printf có thực sự hoạt động hay không.

Vì giá trị của biểu thức-câu lệnh bị hủy bỏ, nếu tuyên bố đó cũng không có tác dụng phụ, đó là tuyên bố vô dụng không làm gì (như "\n") của bạn. Nhưng những lời tuyên bố vô dụng như vậy không sai lầm. Nếu bạn thêm các tùy chọn cảnh báo vào dòng lệnh trình biên dịch của mình, bạn có thể nhận được một cảnh báo như "tuyên bố không có hiệu lực" hoặc một cái gì đó tương tự.

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