Tối ưu hóa bất ngờ của strlen khi răng cưa mảng 2-d


28

Đây là mã của tôi:

#include <string.h>
#include <stdio.h>

typedef char BUF[8];

typedef struct
{
    BUF b[23];
} S;

S s;

int main()
{
    int n;

    memcpy(&s, "1234567812345678", 17);

    n = strlen((char *)&s.b) / sizeof(BUF);
    printf("%d\n", n);

    n = strlen((char *)&s) / sizeof(BUF);
    printf("%d\n", n);
}

Sử dụng gcc 8.3.0 hoặc 8.2.1 với bất kỳ mức tối ưu hóa nào ngoại trừ -O0, điều này xuất ra 0 2khi tôi đang mong đợi2 2 . Trình biên dịch đã quyết định rằng strlengiới hạn b[0]và do đó không bao giờ có thể bằng hoặc vượt quá giá trị được chia cho.

Đây có phải là một lỗi trong mã của tôi hoặc một lỗi trong trình biên dịch?

Điều này không được nêu rõ trong tiêu chuẩn, nhưng tôi nghĩ cách giải thích chính của xuất xứ con trỏ là đối với bất kỳ đối tượng nào X, mã (char *)&Xsẽ tạo ra một con trỏ có thể lặp lại trên toàn bộ X- khái niệm này sẽ giữ ngay cả khi Xcó mảng phụ như cấu trúc bên trong.

(Câu hỏi thưởng, có cờ gcc để tắt tối ưu hóa cụ thể này không?)



4
Tham chiếu: Báo cáo gcc 7.4.0 của tôi 2 2dưới nhiều tùy chọn khác nhau.
chux - Phục hồi Monica

2
@Ale đảm bảo tiêu chuẩn chúng ở cùng một địa chỉ (struct không thể có phần đệm ban đầu)
MM

3
@ DavidRankin-ReinstateMonica "dẫn đến giới hạn của char (*) [8] bị giới hạn ở b [0]. Nhưng đó là theo như tôi nhận được" Tôi nghĩ rằng nó đóng đinh nó. vì s.bbị giới b[0]hạn ở 8 ký tự và do đó có hai tùy chọn: (1) truy cập ngoài giới hạn trong trường hợp có 8 ký tự không null, đó là UB, (2) có một ký tự null, trong đó len nhỏ hơn 8, do đó chia cho 8 cho không. Vì vậy, kết hợp trình biên dịch (1) + (2) có thể sử dụng UB để đưa ra kết quả giống nhau cho cả hai trường hợp
user2162550

3
Cho rằng & s == & s.b, không có cách nào mà kết quả có thể khác nhau. Như @ user2162550 đã chỉ ra, strlen () không được gọi và trình biên dịch đưa ra dự đoán về kết quả của nó, ngay cả trong trường hợp godbolt.org/z/dMcrdy nơi trình biên dịch không thể biết được. Đây là một lỗi biên dịch .
Ale

Câu trả lời:


-1

Có một số vấn đề mà tôi có thể thấy và chúng có thể bị ảnh hưởng bởi cách trình biên dịch quyết định bố trí bộ nhớ.

    n = strlen((char *)&s.b) / sizeof(BUF);
    printf("%d\n", n);

Trong đoạn mã trên s.b là một mảng 23 mục nhập của một mảng gồm 8 ký tự. Khi bạn chỉ tham khảo, s.bbạn sẽ nhận được địa chỉ của mục nhập đầu tiên trong mảng 23 byte (và byte đầu tiên trong mảng 8 ký tự). Khi mã nói &s.b, đây là yêu cầu địa chỉ của địa chỉ của mảng. Dưới vỏ bọc, trình biên dịch nhiều khả năng tạo ra một số lưu trữ cục bộ, lưu trữ địa chỉ của mảng trong đó và cung cấp địa chỉ của lưu trữ cục bộ strlen.

Bạn có 2 giải pháp khả thi. Họ đang:

    n = strlen((char *)s.b) / sizeof(BUF);
    printf("%d\n", n);

hoặc là

    n = strlen((char *)&s.b[0]) / sizeof(BUF);
    printf("%d\n", n);

Tôi cũng đã cố gắng chạy chương trình của bạn và chứng minh vấn đề, nhưng cả tiếng kêu và phiên bản gcc tôi có với bất kỳ -Otùy chọn nào vẫn hoạt động như bạn mong đợi. Để biết giá trị của nó, tôi đang chạy phiên bản clang 9.0.0-2 và gcc phiên bản 9.2.1 trên x86_64-pc-linux-gnu).


-2

Có lỗi trong mã.

 memcpy(&s, "1234567812345678", 17);

ví dụ, có rủi ro, mặc dù s bắt đầu bằng b nên:

 memcpy(&s.b, "1234567812345678", 17);

Strlen thứ hai () cũng có lỗi

n = strlen((char *)&s) / sizeof(BUF);

ví dụ: nên:

n = strlen((char *)&s.b) / sizeof(BUF);

Chuỗi sb, nếu được sao chép chính xác, phải dài 17 chữ cái. Không chắc chắn làm thế nào các cấu trúc được lưu trữ trong bộ nhớ, nếu chúng được căn chỉnh. Bạn đã kiểm tra sb thực sự có chứa 17 ký tự được sao chép chưa?

Vì vậy, một strlen (sb) sẽ hiển thị 17

Printf chỉ hiển thị số nguyên, vì% d là số nguyên và biến n được khai báo là số nguyên. sizeof (BUF), nên là 8

Vì vậy, số 17 chia cho 8 (17/8) nên in 2 vì n được khai báo là số nguyên. Vì memcpy đã được sử dụng để sao chép dữ liệu sang s chứ không phải sb, tôi đoán rằng điều này có liên quan đến việc sắp xếp bộ nhớ; giả sử nó là một máy tính 64 bit, có thể có 8 ký tự trên một địa chỉ bộ nhớ.

Chẳng hạn, giả sử rằng ai đó đã gọi một malloc (1), hơn là "không gian trống" tiếp theo không được căn chỉnh ...

Cuộc gọi strlen thứ hai, hiển thị số chính xác, vì bản sao chuỗi đã được thực hiện cho s struct thay vì sb

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.