Saya menjalankan program berikut di komputer saya (Intel 64-bit menjalankan Linux).
#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;
}
Output dari program ini adalah
$ 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
Ukuran pointer &argv
adalah 8 byte. Saya mengharapkan alamat argc
menjadi address of (argv) + sizeof (argv) = 0x7ffed1a4c9f0 + 0x8 = 0x7ffed1a4c9f8
tetapi ada padding 4 byte di antara mereka. Mengapa demikian?
Dugaan saya adalah bahwa hal itu bisa disebabkan oleh penyelarasan memori, tetapi saya tidak yakin.
Saya melihat perilaku yang sama dengan fungsi yang saya panggil juga.
c
memory-alignment
letmutx
sumber
sumber
main
.main
. Dalam C,main
dapat disebut sebagai fungsi reguler, sehingga perlu menerima argumen seperti fungsi biasa dan harus mematuhi ABI.%zu
Jawaban:
Pada sistem Anda, beberapa argumen integer atau pointer pertama dilewatkan dalam register dan tidak memiliki alamat. Ketika Anda mengambil alamatnya dengan
&argc
atau&argv
, kompiler harus membuat alamat dengan menulis konten register ke lokasi stack dan memberi Anda alamat lokasi stack tersebut. Dalam melakukan hal itu, kompiler memilih, dalam arti tertentu, lokasi tumpukan apa pun yang cocok untuknya.sumber
Dari perspektif standar bahasa, jawabannya adalah "tidak ada alasan khusus". C tidak menentukan atau menyiratkan hubungan apa pun antara alamat parameter fungsi. @EricPostpischil menjelaskan apa yang mungkin terjadi dalam implementasi khusus Anda, tetapi detail-detail itu akan berbeda untuk implementasi di mana semua argumen dilewatkan di stack, dan itu bukan satu-satunya alternatif.
Terlebih lagi, saya mengalami masalah dengan cara di mana informasi tersebut dapat berguna dalam suatu program. Misalnya, bahkan jika Anda "tahu" bahwa alamatnya
argv
adalah 12 byte sebelum alamatargc
, masih belum ada cara yang pasti untuk menghitung salah satu dari pointer tersebut dari yang lain.sumber
uintptr_t
dilakukan, dan itu tentu tidak mendefinisikan hubungan antara alamat parameter atau di mana argumen dilewatkan.uintptr_t
mungkin memberikan alamat dalam 24 bit tinggi dan beberapa bit otentikasi dalam 8 bit rendah. Kemudian menambahkan 4 hanya mengacaukan otentikasi; itu tidak memperbarui ...uintptr_t
Anda dapatkan dari konversi belum tentu alamat yang sederhana.(void *)(uintptr_t)(void *)p
akan dibandingkan dengan(void *)p
. Dan perlu dicatat bahwa panitia telah mengomentari hampir masalah yang tepat ini, menyimpulkan bahwa "implementasi ... juga dapat memperlakukan pointer berdasarkan asal yang berbeda meskipun mereka sedikit identik ."uintptr_t
konversi alamat daripada perbedaan pointer atau jarak "diketahui" dalam byte. Tentu, itu benar, tetapi bagaimana ini berguna? Tetap benar bahwa "masih belum ada cara yang pasti untuk menghitung salah satu petunjuk dari yang lain" sebagai jawaban menyatakan, tetapi perhitungan itu tidak menghitungb
daria
melainkan menghitungb
dari keduanyaa
danb
, karenab
harus digunakan dalam pengurangan untuk menghitung jumlah menambahkan. Menghitung satu dari yang lain tidak ditentukan.