2 boyutlu diziyi yumuşatırken strlenin beklenmedik optimizasyonu


28

İşte benim kod:

#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);
}

Gcc 8.3.0 veya 8.2.1'i dışında herhangi bir optimizasyon seviyesi ile kullanmak , beklediğim zaman -O0çıktı 0 2verir 2 2. Derleyici strlen, b[0]bunun bölündüğü değere bağlı olduğuna ve bu nedenle asla eşitlenemeyeceğine veya bu değeri aşmayacağına karar verdi .

Bu benim kodumda bir hata mı yoksa derleyicide bir hata mı?

Bu standartta açıkça belirtilmemiştir, ancak işaretçi provenansının ana akım yorumunun, herhangi bir nesne için X, kodun (char *)&Xtamamı üzerinde tekrarlayabilen bir işaretçi oluşturması gerektiğini düşündüm X- bu kavram X, iç yapı olarak alt diziler.

(Bonus soru, bu özel optimizasyonu kapatmak için bir gcc bayrağı var mı?)



4
Ref: gcc 7.4.0 raporlarım 2 2çeşitli seçenekler altında.
chux - Monica'yı eski durumuna getir

2
@Ale standart aynı adreste olduklarını garanti eder (yapı ilk dolguya sahip olamaz)
MM

3
@ DavidRankin-ReinstateMonica "char (*) [8] 'in sınırları b [0] ile sınırlı kalmasına neden oldu. çünkü s.bsınırlıdır b[0]bunun 8 karakterden ve dolayısıyla iki seçenek ile sınırlıdır: (1) UB 8 boş olmayan karakterler vardır out-of-ciltli durumda erişimi, (2) bir null karakteri olan, orada len 8'den küçüktür, bu nedenle 8'e bölmek sıfır verir. Bu yüzden (1) + (2) derleyici her iki durumda da aynı sonucu vermek için
UB'yi kullanabilir

3
& S == & s.b dikkate alındığında, sonucun farklı olabilmesinin bir yolu yoktur. @ User2162550'nin gösterdiği gibi, strlen () çağrılmaz ve derleyici, derleyicinin bilemediği durumlarda bile godbolt.org/z/dMcrdy sonucunun ne olabileceğini tahmin eder. Bu bir derleyici hatasıdır .
Ale

Yanıtlar:


-1

Görebildiğim bazı sorunlar var ve derleyicinin düzen belleğine nasıl karar verdiğinden etkilenebilirler.

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

Yukarıdaki kod s.b, 8 karakterlik bir dizinin 23 giriş dizisidir. Sadece atıfta s.bbulunduğunuzda, 23 baytlık dizideki (ve 8 karakterlik dizideki ilk bayt) ilk girişin adresini alırsınız. Kod söylendiğinde &s.b, dizinin adresinin adresini soruyor. Kapakların altında, derleyici muhtemelen yerel depolama üretiyor, dizinin adresini orada saklıyor ve yerel depolamanın adresinistrlen .

2 olası çözümünüz var. Onlar:

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

veya

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

Programınızı çalıştırmayı ve sorunu göstermeyi de denedim, ancak hem clang hem de gcc sürümünde, herhangi bir -Oseçenekle hala beklediğiniz gibi çalıştı. Değer için, x86_64-pc-linux-gnu üzerinde clang sürüm 9.0.0-2 ve gcc sürüm 9.2.1 kullanıyorum).


-2

Kodda hatalar var.

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

Örneğin, s, b ile başlasa bile risklidir:

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

İkinci strlen () de hatalar içeriyor

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

örneğin:

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

Sb dizgisi, doğru kopyalanırsa, 17 harf uzunluğunda olmalıdır. Hizalandıklarında yapıların bellekte nasıl saklandığından emin değilim. SB'nin aslında kopyalanan 17 karakteri içerdiğini kontrol ettiniz mi?

Yani bir strlen (sb) 17 göstermelidir

% D tamsayı olduğundan ve n değişkeni bir tamsayı olarak bildirildiği için printf yalnızca tamsayı sayılarını gösterir. sizeof (BUF), 8 olmalıdır

Dolayısıyla n, tamsayı olarak bildirildiğinden, 17'ye 8 (17/8) bölü 2 yazmalıdır. Memcpy, verileri sb'ye değil sb'ye kopyalamak için kullanıldığından, bunun bellek hizalamaları ile ilgili olduğunu tahmin ediyorum; 64 bit bir bilgisayar olduğunu varsayarsak, bir bellek adresinde 8 karakter olabilir.

Örneğin, birisinin bir sonraki "boş alan" a göre bir malloc (1) çağırdığını varsayalım ...

İkinci strlen çağrısı, dize kopyası sb yerine s yapısına yapıldığından doğru numarayı gösterir

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.