Con trỏ tới một trước phần tử đầu tiên của mảng


8

Người ta nói trong C rằng khi các con trỏ đề cập đến cùng một mảng hoặc một phần tử vượt quá cuối của mảng đó thì các số liệu và so sánh được xác định rõ. Sau đó, những gì về một trước phần tử đầu tiên của mảng? Có ổn không miễn là tôi không tham gia nó?

Được

int a[10], *p;
p = a;

(1) Nó có hợp pháp để viết --pkhông?

(2) Có hợp pháp để viết p-1trong một biểu thức không?

(3) Nếu (2) không sao, tôi có thể khẳng định điều đó p-1 < akhông?

Có một số mối quan tâm thực tế cho việc này. Hãy xem xét một reverse()hàm đảo ngược chuỗi C kết thúc bằng '\0'.

#include <stdio.h>

void reverse(char *p)
{
    char *b, t;

    b = p;
    while (*p != '\0')
        p++;
    if (p == b)      /* Do I really need */
        return;      /* these two lines? */
    for (p--; b < p; b++, p--)
        t = *b, *b = *p, *p = t;
}

int main(void)
{
    char a[] = "Hello";

    reverse(a);
    printf("%s\n", a);
    return 0;
}

Tôi có thực sự cần phải kiểm tra mã không?

Vui lòng chia sẻ ý tưởng của bạn từ ngôn ngữ-luật sư / quan điểm thực tế và cách bạn đối phó với các tình huống như vậy.


2
(1) Nó có thể là hợp pháp để viết nó, nhưng kết quả là hành vi không xác định nếu bạn thực hiện nó. Với các kiến ​​trúc được phân đoạn (Intel 80286, 80386, v.v.), kết quả có thể hoàn toàn gây hoang mang. (2) Ditto. (3) Không có, nhưng câu trả lời là không. Đối với phần cứng và o / s của bạn, bạn có thể an toàn, nhưng tiêu chuẩn C không đảm bảo điều đó.
Jonathan Leffler

1
Điều này có trả lời câu hỏi của bạn không? Tất cả các hành vi không xác định phổ biến mà một lập trình viên C ++ nên biết là gì? Nhìn cụ thể dưới phần cho con trỏ.
Ê-sai

Câu trả lời:


6

(1) Có hợp pháp để viết --p không?

Đó là "hợp pháp" như trong cú pháp C cho phép, nhưng nó gọi hành vi không xác định. Đối với mục đích tìm phần liên quan trong tiêu chuẩn, --ptương đương với p = p - 1(ngoại trừ pchỉ được đánh giá một lần). Sau đó:

C17 6.5.6 / 8

Nếu cả toán hạng con trỏ và kết quả trỏ đến các phần tử của cùng một đối tượng mảng hoặc vượt qua phần tử cuối cùng của đối tượng mảng, việc đánh giá sẽ không tạo ra tràn; mặt khác, hành vi là không xác định.

Việc đánh giá gọi hành vi không xác định, có nghĩa là không quan trọng nếu bạn hủy tham chiếu con trỏ hay không - bạn đã gọi hành vi không xác định.

Hơn nữa:

C17 6.5.6 / 9:

Khi hai con trỏ bị trừ, cả hai sẽ trỏ đến các phần tử của cùng một đối tượng mảng hoặc một phần tử qua phần tử cuối cùng của đối tượng mảng;

Nếu mã của bạn vi phạm "sẽ" trong tiêu chuẩn ISO, nó sẽ gọi hành vi không xác định.

(2) Có hợp pháp không khi viết p-1 trong một biểu thức?

Tương tự như (1), hành vi không xác định.


Đối với các ví dụ về cách điều này có thể gây ra vấn đề trong thực tế: hãy tưởng tượng rằng mảng được đặt ở đầu trang bộ nhớ hợp lệ. Khi bạn giảm xuống bên ngoài trang đó, có thể có ngoại lệ phần cứng hoặc biểu diễn bẫy con trỏ. Đây không phải là một kịch bản hoàn toàn khó xảy ra đối với các bộ vi điều khiển, đặc biệt khi chúng đang sử dụng bản đồ bộ nhớ được phân đoạn.


Bạn có thể vui lòng cung cấp một số ý kiến ​​cho mã ví dụ trong câu hỏi? Là kiểm tra cần thiết / thích hợp / đủ?
aafulei

Có thể là *p == '\0'lúc bắt đầu. Kiểm tra này có ý định ngăn chặn p--trong vòng lặp for.
aafulei

@aafulei Vâng tôi nhận ra. Bạn cần kiểm tra thêm vì vòng lặp được viết xấu. Cách khắc phục chính xác là viết lại vòng lặp, ví dụ: godbolt.org/z/R4TuwT
Lundin

Cảm ơn mã của bạn. Đó là một thông minh. Tôi sẽ đề nghị bạn đặt nó trong câu trả lời của bạn để nhiều người xem nó hơn. Điều duy nhất là khi có số lượng ký tự lẻ trong chuỗi (không tính '\0') sẽ có một tự hoán đổi (hoán đổi với chính nó) ở cuối. Nhưng điều đó tốt. Ngoài ra xin vui lòng chịu đựng tôi lâu hơn một chút để xác nhận chéo trước khi tôi có thể đánh dấu.
aafulei

Nếu p-1trong một biểu thức không hợp lệ, p=p-1sẽ không hợp lệ. Và p--p=p-1. Bạn có cho rằng việc giảm một con trỏ là không hợp lệ?
harper

-2

Việc sử dụng loại số học con trỏ đó là thực hành mã hóa xấu, vì nó có thể dẫn đến một loạt các vấn đề khó gỡ lỗi đáng kể.

Tôi chỉ phải sử dụng loại điều này một lần trong hơn 20 năm. Tôi đã viết một chức năng gọi lại, nhưng tôi không có quyền truy cập vào dữ liệu thích hợp. Hàm gọi cung cấp một con trỏ bên trong một mảng thích hợp và tôi cần byte ngay trước con trỏ đó.

Xem xét rằng tôi đã truy cập vào toàn bộ mã nguồn và tôi đã xác minh hành vi nhiều lần để chứng minh rằng tôi có được những gì tôi cần và tôi đã xem xét nó bởi các đồng nghiệp khác, tôi quyết định cho phép nó đi vào sản xuất.

Giải pháp thích hợp sẽ là thay đổi chức năng người gọi để trả về con trỏ thích hợp, nhưng điều đó không khả thi, thời gian và tiền bạc xem xét (phần đó của phần mềm đã được cấp phép từ bên thứ ba).

Vì vậy, a[-1]là có thể, nhưng nên được sử dụng CHỈ với sự chăm sóc rất tốt trong các tình huống rất đặc biệt. Nếu không, không có lý do chính đáng để làm Voodoo tự làm tổn thương mình bao giờ.


Lưu ý: tại một phân tích thích hợp, trong ví dụ của tôi, rõ ràng là tôi đã không truy cập một phần tử trước khi bắt đầu một mảng thích hợp, nhưng phần tử trước một con trỏ, được đảm bảo nằm trong cùng một mảng.


Đề cập đến mã được cung cấp:

  • sử dụng p [-1] với reverse(a);;
  • Bạn có thể sử dụng nó với reverse(a+1);, vì bạn vẫn ở trong mảng.
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.