Argc और argv 12 बाइट्स के पते अलग क्यों हैं?


40

मैंने अपने कंप्यूटर पर निम्न प्रोग्राम चलाया (64-बिट इंटेल पर चलने वाला लिनक्स)।

#include <stdio.h>

void test(int argc, char **argv) {
    printf("[test] Argc Pointer: %p\n", &argc);
    printf("[test] Argv Pointer: %p\n", &argv);
}

int main(int argc, char **argv) {
    printf("Argc Pointer: %p\n", &argc);
    printf("Argv Pointer: %p\n", &argv);
    printf("Size of &argc: %lu\n", sizeof (&argc));
    printf("Size of &argv: %lu\n", sizeof (&argv));
    test(argc, argv);
    return 0;
}

कार्यक्रम का आउटपुट था

$ gcc size.c -o size
$ ./size
Argc Pointer: 0x7fffd7000e4c
Argv Pointer: 0x7fffd7000e40
Size of &argc: 8
Size of &argv: 8
[test] Argc Pointer: 0x7fffd7000e2c
[test] Argv Pointer: 0x7fffd7000e20

सूचक &argvका आकार 8 बाइट्स है। मुझे उम्मीद थी कि संबोधन argcहोगा address of (argv) + sizeof (argv) = 0x7ffed1a4c9f0 + 0x8 = 0x7ffed1a4c9f8लेकिन उनके बीच 4 बाइट की पैडिंग है। यह एक केस क्यों है?

मेरा अनुमान है कि यह मेमोरी संरेखण के कारण हो सकता है, लेकिन मुझे यकीन नहीं है।

मैं उन कार्यों के साथ समान व्यवहार देखता हूं जिन्हें मैं कॉल करता हूं।


15
क्यों नहीं? वे 174 बाइट के अलावा हो सकते हैं। एक उत्तर आपके ऑपरेटिंग सिस्टम और / या रैपर लाइब्रेरी पर निर्भर करेगा जो सेटअप करता है main
aschepler 15

2
@aschepler: यह किसी भी रैपर पर निर्भर नहीं होना चाहिए जो इसके लिए सेटअप करता है main। सी में, mainएक नियमित फ़ंक्शन के रूप में कहा जा सकता है, इसलिए इसे एक नियमित फ़ंक्शन की तरह तर्क प्राप्त करना होगा और एबीआई का पालन करना होगा।
एरिक पोस्टपिसिल

@ शुभ: मैं अन्य कार्यों के लिए भी यही व्यवहार नोटिस करता हूं।
letmutx

4
यह एक दिलचस्प 'विचार प्रयोग' है, लेकिन वास्तव में, ऐसा कुछ भी नहीं है जो 'मुझे आश्चर्य है कि' से अधिक होना चाहिए। ये पते ओएस, कंपाइलर, कंपाइलर वर्जन, प्रोसेसर आर्किटेक्चर के आधार पर बदल सकते हैं और किसी भी तरह से 'वास्तविक जीवन' पर निर्भर नहीं होने चाहिए।
नील

जवाबों:


61

आपके सिस्टम पर, पहले कुछ पूर्णांक या सूचक तर्क रजिस्टरों में पारित किए जाते हैं और उनका कोई पता नहीं होता है। जब आप के साथ अपने पतों, &argcया &argv, संकलक ढेर स्थानों के लिए रजिस्टर सामग्री लिखने और आप उन ढेर स्थानों के पते देकर निर्माण करना पतों पर है। ऐसा करने में, कंपाइलर एक मायने में, जो भी स्टैक स्थान होता है, उसके लिए सुविधाजनक होता है।


6
ध्यान दें कि स्टैक पर पास होने पर भी यह हो सकता है ; कंपाइलर को स्टैक पर इनकमिंग-वैल्यू स्लॉट का उपयोग करने की कोई बाध्यता नहीं है क्योंकि स्थानीय वस्तुओं के लिए स्टोरेज को मानों में जाता है। यह करने के लिए समझ में आ सकता है कि यह फ़ंक्शन अंततः पूंछ-कॉल करने वाला है और पूंछ-कॉल के लिए आउटगोइंग तर्कों का उत्पादन करने के लिए इन वस्तुओं के वर्तमान मूल्यों की आवश्यकता है।
आर .. गिटहब स्टॉप हेल्पिंग आईसीई

10

Argc और argv 12 बाइट्स के पते अलग क्यों हैं?

भाषा मानक के दृष्टिकोण से, उत्तर "कोई विशेष कारण नहीं" है। C फंक्शन पैरामीटर्स के पतों के बीच किसी संबंध को निर्दिष्ट या अधिरोहित नहीं करता है। @EricPostpischil वर्णन करता है कि आपके विशेष कार्यान्वयन में क्या हो रहा है, लेकिन उन विवरणों को एक कार्यान्वयन के लिए अलग होगा जिसमें सभी तर्क स्टैक पर पारित किए जाते हैं, और यह एकमात्र विकल्प नहीं है।

इसके अलावा, मुझे उस तरीके से आने में परेशानी हो रही है जिसमें ऐसी जानकारी किसी कार्यक्रम के भीतर उपयोगी हो सकती है। उदाहरण के लिए, भले ही आप "जानते" हों कि पते का पता argv12 बाइट्स से पहले है argc, फिर भी उन बिंदुओं में से एक की गणना दूसरे से करने के लिए कोई परिभाषित तरीका नहीं है।


7
@ R..GitHubSTOPHELPINGICE: एक को दूसरे से कम्प्यूट करना आंशिक रूप से परिभाषित है, अच्छी तरह से परिभाषित नहीं है। रूपांतरण कैसे uintptr_tकिया जाता है, इस पर सी मानक सख्त नहीं है, और यह निश्चित रूप से मापदंडों के पते या जहां तर्कों को पारित किया जाता है, के बीच संबंधों को परिभाषित नहीं करता है।
एरिक पोस्टपिसिल

6
@ R..GitHubSTOPHELPINGICE: यह तथ्य कि आप राउंड-ट्रिप का मतलब यह कर सकते हैं कि g (f (x)) = x, जहां x एक पॉइंटर है, f, Convert-pointer-to-uintptr_t है, और g Convert-uintptr_t-to-to है -pointer। गणितीय और तार्किक रूप से, इसका मतलब यह नहीं है कि g (f (x) +4) = x + 4। उदाहरण के लिए, यदि f (x) x² और g (y) sqrt (y) थे, तो g (f (x)) = x (वास्तविक गैर-ऋणात्मक x के लिए), लेकिन g (f (x) +4) , X + 4, सामान्य तौर पर। संकेत के मामले में, रूपांतरण uintptr_tउच्च 24 बिट्स और निम्न 8 बिट्स में कुछ प्रमाणीकरण बिट्स में पता दे सकता है। फिर प्रमाणीकरण में सिर्फ 4 स्क्रू जोड़ना; यह अद्यतन नहीं करता है ...
एरिक पोस्टपिसिल

5
… पता बिट्स। या uintptr_t में रूपांतरण उच्च 16 बिट्स में एक आधार पता और निम्न 16 बिट्स में एक ऑफसेट दे सकता है, और 4 को कम बिट्स में जोड़कर उच्च बिट्स में ले जा सकता है, लेकिन स्केलिंग गलत है (क्योंकि प्रतिनिधित्व किया गया पता नहीं है आधार • 65536 + ऑफसेट लेकिन बल्कि आधार है • 64 + ऑफसेट, जैसा कि कुछ प्रणालियों में था)। काफी बस, uintptr_tआप एक रूपांतरण से मिलता है जरूरी नहीं कि एक सरल पता है।
एरिक पोस्टपिसिल

4
@ R..GitHubSTOPHELPINGICE मानक के मेरे पढ़ने से, केवल एक कमजोर गारंटी है जो (void *)(uintptr_t)(void *)pइसके बराबर होगी (void *)p। और यह ध्यान देने योग्य है कि समिति ने लगभग इस सटीक मुद्दे पर टिप्पणी की है, यह निष्कर्ष निकाला है कि "कार्यान्वयन ... अलग-अलग मूल के आधार पर संकेत का भी इलाज कर सकते हैं, भले ही वे समरूप हों ।"
रयान एवेला

5
@ R..GitHubSTOPHELPINGICE: क्षमा करें, मैंने याद किया कि आप uintptr_tबाइट्स में "अलग-अलग" या "ज्ञात" दूरी के बजाय पते के दो रूपांतरणों के अलग-अलग मूल्य के रूप में गणना की गई थी। यकीन है, यह सच है, लेकिन यह कैसे उपयोगी है? यह सच है कि उत्तर के रूप में "दूसरे से उन बिंदुओं में से एक की गणना करने के लिए अभी भी कोई परिभाषित तरीका नहीं है", लेकिन वह गणना गणना bसे नहीं करता है a, बल्कि bदोनों से गणना करता है , aऔर राशि की गणना करने के लिए घटाव में उपयोग किया जाना चाहिए। जोड़ने के लिए। एक से दूसरे की गणना करना परिभाषित नहीं है। bb
एरिक पोस्टपिसिल
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.