2012-04-03 12 views
13

Tuyên bố từ chối trách nhiệm: Câu hỏi này nghiêm túc về mặt học thuật. Ví dụ tôi sắp đưa ra có lẽ là kiểu xấu.Dereferencing con trỏ đến dữ liệu tĩnh ngoài phạm vi trong C

Giả sử trong CI viết một chương trình con của mẫu này:

char *foo(int x) 
{ 
    static char bar[9]; 

    if(x == 0) 
     strcpy(bar, "zero"); 
    else 
     strcpy(bar, "not zero"), 

    return bar; 
} 

Sau đó, ở đâu đó, tôi sử dụng foo như sau:

printf("%i is %s\n", 5, foo(5)); 

mô hình tinh thần của tôi về con trỏ và các biến tĩnh dự đoán rằng, trong thực hành, đầu ra của printf này sẽ là

5 không phải là số không

... nhưng điều này thực sự được yêu cầu theo tiêu chuẩn C hay tôi ở lãnh thổ quỷ mũi?

Để làm cho mọi việc thậm chí tệ hơn, những gì về một cái gì đó giống như

strcpy(foo(5), "five"); 

mô hình tinh thần của tôi nói rằng nên "tác phẩm" này, trừ khi đó là một cách rõ ràng bất hợp pháp, mặc dù nó hơi vô nghĩa vì nó không ảnh hưởng đến đầu ra của foo. Nhưng một lần nữa, điều này thực sự được định nghĩa theo tiêu chuẩn?

+2

Ví dụ cuối cùng của bạn là "hợp pháp", nhưng tôi không chắc đó là "đạo đức". Cũng có thể có một đồng bằng cũ toàn cầu nếu mọi người có thể sửa đổi nó. (Và hãy thận trọng với chủ đề.) – Mat

Trả lời

12

Nội dung bạn đã viết là OK; không có quỷ mũi đang chờ bạn. Ngay cả ví dụ strcpy() là 'OK' vì bạn không bị chà đạp ngoài giới hạn của mảng. Đó là 'OK' là trong dấu ngoặc kép bởi vì nó không phải là một ý tưởng tốt, nhưng như được viết, không có ngoài giới hạn truy cập bộ nhớ và do đó không có hành vi không xác định. Dữ liệu static trong hàm có trong suốt thời gian tồn tại của chương trình, chứa giá trị cuối cùng được ghi vào đó.

Có thể có vấn đề nếu bạn cố gắng:

printf("%i is %s but %i is %s\n", 5, foo(5), 0, foo(0)); 

Bạn sẽ nhận được câu trả lời sai cho một trong hai con số, nhưng nó không được định nghĩa đó sẽ là câu trả lời sai.

+0

Tại sao nó lại khác? Tôi không thấy nó ... –

+2

Biến trong hàm chỉ có thể giữ 'không 0 hoặc' không' tại thời điểm 'printf()' được gọi (sau khi hai lệnh gọi tới 'foo()' là hoàn thành). Vì vậy, cùng một chuỗi sẽ được in cho cả 0 và 5, và vì một trong số chúng là số không và số còn lại là không, không quan trọng chuỗi trong hàm có chứa 'zero' hay' không zero'; nó là sai cho một trong hai số. Nếu bạn muốn được ưa thích và có cả hai chính xác, bạn sẽ sửa lại chức năng: 'static const char bar [9] =" not zero "; if (x == 0) trả về & thanh [4]; else return & bar [0]; 'và đột nhiên cả hai sẽ ổn. –

+1

@ 0A0D Vì hàm đó có tác dụng phụ. – cnicutar

1

Tôi thấy không có gì sai với mã này.

Con trỏ foo() trả về là một con trỏ hợp lệ và bạn được phép bỏ qua nó.

Chỉnh sửa: Bởi "không có gì sai", tôi đồng ý với các câu trả lời khác rằng mã này không tốt trong bất kỳ ý nghĩa nào của từ vì nhiều lý do. Nó chỉ đơn thuần là đúng theo tiêu chuẩn C.

5

Vâng, kể từ khi bạn muốn có một trích dẫn từ tiêu chuẩn:

Thời gian tồn tại của một đối tượng là một phần của chương trình thực hiện trong đó lưu trữ được đảm bảo để dành cho nó.

Một đối tượng có identi fi er được khai báo mà không có sự lưu trữ hạng Speci fi er _Thread_local, và một trong hai với bên ngoài hoặc nội bộ liên kết hoặc với việc lưu trữ hạng Speci fi er tĩnh, có thời gian lưu trữ tĩnh.Thời gian của nó là toàn bộ quá trình thực thi của chương trình và giá trị được lưu trữ chỉ được khởi tạo một lần, trước khi khởi động chương trình.

2

Trong điều kiện bất kỳ, foo() trả về một con trỏ đến một biến tĩnh, mà đời là vĩnh viễn (tức là nó sống từ khi kiểm soát đi qua nó lần đầu tiên cho đến khi kết thúc chương trình). Điều này là hoàn toàn tốt đẹp.

Logic của mã của bạn kém hiệu quả; ví dụ, hàm không được tái nhập và không xa thread-safe, và dĩ nhiên hoạt động chuỗi không được bảo vệ là hoàn toàn tự tử.

+0

Vâng, hoạt động chuỗi không có bảo vệ là hơi ác vì nó làm cho nó dễ dàng để phá vỡ những điều sau này, nhưng không nên nó được okay kể từ khi tôi chỉ bao giờ sử dụng nó cho chuỗi chữ? Ngay cả từ bên ngoài hàm, nếu tôi đã biết rằng 'foo' trả về một con trỏ tới một chuỗi mà tôi không phải trả tự do, và chuỗi đó đôi khi không phải là" ', thì tôi có thể suy ra rằng chuỗi tĩnh phân bổ và tôi có thể phù hợp với "năm" vào nó. – Ian

+0

Bạn phải chắc chắn rằng bạn không bao giờ viết một chuỗi dài hơn bảy ký tự vào chuỗi. Có lẽ không phải "tự tử" (tôi nghĩ rằng tôi đã hiểu sai mã của bạn là 'strcat' thay vì' strcpy'), nhưng dù sao thì nó cũng không nguy hiểm. Bạn có thể đặt 'strncpy' vào đó với' sizeof bar'. –

0

Không có gì sai với nó. phong cách tốt hơn sẽ

const char *foo(int x); 

Sau đó, bạn không thể strcpy vào nó mà không đúc đi những const. Nếu bạn thực sự muốn phá vỡ nó không có gì có thể ngăn cản bạn.

nếu bạn muốn đặt lại.

const char *foo(int x) 
{ 
return (x? "not zero" : "zero"); 
}