2-डी सरणी को एलियास करते समय स्ट्रलेन का अप्रत्याशित अनुकूलन


28

यहाँ मेरा कोड है:

#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 या 8.2.1 का उपयोग करना -O0, 0 2जब मैं उम्मीद कर रहा था तो यह आउटपुट करता है 2 2। संकलक ने तय किया कि यह करने के strlenलिए बाध्य है b[0]और इसलिए विभाजित होने वाले मूल्य के बराबर या उससे अधिक कभी नहीं हो सकता है।

क्या यह मेरे कोड में बग है या कंपाइलर में बग है?

यह मानक में स्पष्ट रूप से नहीं लिखा गया है, लेकिन मुझे लगा कि सूचक सिद्धता की मुख्य धारा की व्याख्या यह थी कि किसी भी वस्तु के लिए X, कोड (char *)&Xको एक सूचक उत्पन्न करना चाहिए जो पूरे पर पुनरावृति कर सकता है X- इस अवधारणा को धारण करना चाहिए भले ही Xऐसा हो आंतरिक संरचना के रूप में उप-सरणियाँ।

(बोनस प्रश्न, क्या इस विशिष्ट अनुकूलन को बंद करने के लिए एक gcc ध्वज है?)



4
रेफरी: 2 2विभिन्न विकल्पों के तहत मेरी जीसीसी 7.4.0 रिपोर्ट ।
chux -

2
@ वे एक ही पते पर मानक गारंटी देते हैं (संरचना में प्रारंभिक गद्दी नहीं हो सकती है)
MM

3
@ DavidRankin-ReinstateMonica "जिसके परिणामस्वरूप चार (*) [8] की सीमा को b [0] तक सीमित किया जा रहा है। लेकिन जहां तक ​​मुझे मिलता है" मुझे लगता है कि यह नाखून है। के बाद से s.bतक ही सीमित है b[0]यह 8 वर्ण, और इसलिए दो विकल्प तक ही सीमित है: (1) के बाहर के लिए बाध्य मामले में पहुँच वहाँ 8 में गैर-शून्य वर्ण, जो यूबी है कर रहे हैं, (2) वहाँ एक अशक्त चरित्र जो में है, लेन 8 से कम है, इसलिए 8 से भाग देना शून्य देता है। इसलिए एक साथ (1) + (2) संकलक दोनों मामलों में समान परिणाम देने के लिए UB का उपयोग कर सकते हैं
user2162550

3
यह देखते हुए कि s == & s.b, कोई ऐसा तरीका नहीं है जिसके परिणाम भिन्न हो सकते हैं। जैसा कि @ user2162550 ने दिखाया, strlen () नहीं कहा जाता है और कंपाइलर एक अनुमान लगाता है कि इसका परिणाम क्या हो सकता है, यहां तक ​​कि मामले में godbolt.org/z/dMcrdy जहां कंपाइलर इसे नहीं जान सकता। यह एक कंपाइलर बग है
अले

जवाबों:


-1

कुछ मुद्दे हैं जो मैं देख सकता हूं और वे प्रभावित हो सकते हैं कि कंपाइलर लेआउट मेमोरी को कैसे तय करता है।

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

उपरोक्त कोड s.bमें 8 वर्णों के एक सरणी का 23 प्रविष्टि सरणी है। जब आप सिर्फ उल्लेख करते हैं, s.bतो आपको 23 बाइट सरणी में पहली प्रविष्टि का पता मिल रहा है (और 8 वर्ण सरणी में पहला बाइट)। जब कोड कहता है &s.b, यह सरणी के पते का पता पूछ रहा है। कवर्स के तहत, कंपाइलर कुछ स्थानीय स्टोरेज को जेनरेट करने की संभावना से अधिक है, इसमें एरे के पते को स्टोर करके स्थानीय स्टोरेज के एड्रेस को सप्लाई करता है strlen

आपके पास 2 संभावित उपाय हैं। वो हैं:

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

या

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

मैंने आपके कार्यक्रम को चलाने और इस मुद्दे को प्रदर्शित करने की भी कोशिश की, लेकिन दोनों के क्लैग और संस्करण के साथ मेरे पास कोई भी -Oविकल्प है, जो अभी भी आपकी उम्मीद के मुताबिक काम कर रहा है। इसके लायक क्या है, मैं क्लेर संस्करण 9.0.0-2 और gcc संस्करण 9.2.1 x86_64-pc-linux-gnu पर चला रहा हूं)।


-2

कोड में त्रुटियां हैं।

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

उदाहरण के लिए, जोखिम भरा है, भले ही बी के साथ शुरू होना चाहिए:

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

दूसरी strlen () में भी त्रुटियाँ हैं

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

उदाहरण के लिए, होना चाहिए:

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

स्ट्रिंग एसबी, यदि सही तरीके से कॉपी किया गया है, तो 17 अक्षर लंबा होना चाहिए। यह निश्चित नहीं है कि स्मृति में संरचनाएं कैसे संग्रहीत की जाती हैं, यदि वे गठबंधन की जाती हैं। क्या आपने जाँच की है कि वास्तव में sb में 17 अक्षर कॉपी किए गए हैं?

तो एक स्ट्रलेन (sb) को 17 दिखाना चाहिए

Printf केवल पूर्णांक संख्या दिखाता है, क्योंकि% d पूर्णांक है, और चर n को पूर्णांक घोषित किया जाता है। sizeof (BUF), 8 होना चाहिए

अतः 8 (17/8) से विभाजित 17 को 2 को प्रिंट करना चाहिए क्योंकि n को पूर्णांक घोषित किया गया है। जैसा कि मेम्पी का उपयोग डेटा को कॉपी करने और एसबी करने के लिए किया गया था, मुझे लगता है कि यह मेमोरी संरेखण के साथ करना है; यह मानते हुए कि यह 64 बिट कंप्यूटर है, एक मेमोरी एड्रेस पर 8 अक्षर हो सकते हैं।

उदाहरण के लिए, मान लेते हैं कि किसी व्यक्ति ने एक मॉलोक (1) कहा है, अगले "मुक्त स्थान" से संरेखित नहीं है ...

दूसरी स्ट्रैलेन कॉल, सही संख्या दर्शाती है, क्योंकि स्ट्रिंग कॉपी sb के बजाय s संरचना में की गई थी

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.