Argc ve argv 12 baytlarının adresleri neden birbirinden ayrılıyor?


40

Aşağıdaki programı bilgisayarımda çalıştırdım (Linux çalıştıran 64 bit Intel).

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

Program çıktısı

$ 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

İşaretçinin boyutu &argv8 bayttır. Ben adresini beklenen argcolmak address of (argv) + sizeof (argv) = 0x7ffed1a4c9f0 + 0x8 = 0x7ffed1a4c9f8ancak 4 bayt dolgu aralarına yoktur. Neden böyle?

Benim tahminime göre bellek hizalaması olabilir, ama emin değilim.

Aynı davranışı, çağırdığım işlevlerle de fark ediyorum.


15
Neden olmasın? 174 bayt arayla olabilirler. Yanıt, işletim sisteminize ve / veya kurulumunu yapan bir sarıcı kitaplığına bağlıdır main.
aschepler

2
@aschepler: Kurulum yapan herhangi bir sarıcıya bağlı olmamalıdır main. C mainolarak, normal bir işlev olarak adlandırılabilir, bu nedenle normal bir işlev gibi argümanlar alması ve ABI'ye uyması gerekir.
Eric Postpischil

@aschelper: Diğer işlevler için de aynı davranışı fark ettim.
letmutx

4
Bu ilginç bir 'düşünce deneyi', ama gerçekten, 'Nedenini merak ediyorum' dan daha fazlası olması gereken hiçbir şey yok. Bu adresler işletim sistemine, derleyiciye, derleyici sürümüne, işlemci mimarisine bağlı olarak değişebilir ve hiçbir şekilde 'gerçek hayata' güvenilmemelidir.
Neil

2
sizeof sonucu%zu
phuclv

Yanıtlar:


61

Sisteminizde, ilk birkaç tamsayı veya işaretçi argümanı kayıtlara geçirilir ve adresleri yoktur. Adreslerini &argcveya ile aldığınızda &argv, derleyici adresleri yığın konumlarına yazarak ve bu yığın konumlarının adreslerini vererek adresler üretmelidir. Bunu yaparken, derleyici, bir anlamda, hangi yığın konumlarının uygun olduğunu seçer.


6
Bunun yığına iletilse bile olabileceğini unutmayın ; derleyicinin, değerlerin girdiği yerel nesneler için depolama alanı olarak yığındaki gelen değer yuvasını kullanma zorunluluğu yoktur. Bunu yapmak mantıklı olabilir, fonksiyon sonunda kuyruk çağrısına girer ve kuyruk çağrısı için giden argümanları üretmek için bu nesnelerin mevcut değerlerine ihtiyaç duyar.
R .. GitHub DURDURMA BUZA DESTEK

10

Argc ve argv 12 baytlarının adresleri neden birbirinden ayrılıyor?

Dil standardı açısından, cevap "özel bir sebep değildir". C, fonksiyon parametrelerinin adresleri arasında herhangi bir ilişki belirtmez veya ima etmez. @EricPostpischil, özel uygulamanızda muhtemelen neler olduğunu açıklar, ancak bu ayrıntılar yığın üzerinde tüm argümanların iletildiği bir uygulama için farklı olacaktır ve bu tek alternatif değildir.

Dahası, bu bilgilerin bir programda yararlı olabileceği bir yol bulmakta zorlanıyorum. Örneğin, adresinin adresinden argvönce 12 bayt olduğunu "bilseniz bile" argc, bu işaretçilerden birini diğerinden hesaplamanın tanımlanmış bir yolu yoktur.


7
@ R.. GitHubSTOPHELPINGICE: Birinden diğerinin hesaplanması kısmen tanımlanmıştır, iyi tanımlanmamıştır. C standardı, dönüştürmenin nasıl yapılacağı konusunda katı değildir uintptr_tve kesinlikle parametrelerin adresleri veya argümanların iletildiği yerler arasındaki ilişkileri tanımlamaz.
Eric Postpischil

6
@ R.. GitHubSTOPHELPINGICE Gidiş-dönüş yapabilmeniz, g (f (x)) = x, burada x, bir işaretçi, f, dönüştürmek-pointer-to-uintptr_t ve g, convert-uintptr_t-to -Işaretçi. Matematiksel ve mantıksal olarak, g (f (x) +4) = x + 4 anlamına gelmez. Örneğin, f (x) x² ve g (y) sqrt (y) ise, g (f (x)) = x (gerçek negatif olmayan x için), ancak g (f (x) +4) ≠ x + 4, genel olarak. İşaretçiler söz konusu olduğunda, dönüştürme işlemi uintptr_tyüksek 24 bitte bir adres ve düşük 8 bitte bazı kimlik doğrulama bitleri verebilir. Sonra 4 eklenmesi sadece kimlik doğrulaması vidalar; güncellenmiyor…
Eric Postpischil

5
… Adres bitleri. Veya uintptr_t biçimine dönüştürme, yüksek 16 bitte bir temel adres ve düşük 16 bitte bir ofset verebilir ve düşük bitlere 4 eklemek yüksek bitlere taşıyabilir, ancak ölçekleme yanlıştır (temsil edilen adres olmadığı için) base • 65536 + ofset, ancak bazı sistemlerde olduğu gibi base • 64 + ofset). Oldukça basit bir şekilde, uintptr_tbir dönüşümden elde etmeniz basit bir adres olmak zorunda değildir.
Eric Postpischil

4
@ R.. GitHubSTOPHELPINGICE standardı okuduğumdan beri, sadece (void *)(uintptr_t)(void *)peşit bir karşılaştırma yapacak zayıf bir garanti var (void *)p. Ve komitenin neredeyse tam olarak bu konuda yorum yaptığını belirtmekte fayda var, "uygulamalar ... farklı kökenlere dayalı işaretçileri de bitsel olarak özdeş olsalar bile farklı olarak ele alabilirler ".
Ryan Avella

5
@ R..GitHubSTOPHELPINGICE Üzgünüm, uintptr_tfarklı bir işaretçi veya bayt cinsinden “bilinen” bir mesafe yerine iki adres dönüşümünün farklı olarak hesaplanan bir değer eklediğinizi özledim . Tabii, bu doğru, ama nasıl faydalı? Bu cevap devletler “diğer gelenler işaretçiler birini hesaplamak için hiçbir tanımlanmış bir yol hala var” olduğu kanıtlanmadı ama bu hesaplama hesaplamaz bdan aziyade hesaplar bhem ave bberi bmiktarını hesaplamak için çıkarma kullanılmalıdır eklemek. Birini diğerinden hesaplamak tanımlanmamıştır.
Eric Postpischil
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.