數組名和函數名是什麼東西

數組名和函數名的本質都是一個地址

一、數組a[]中a和&a的區別

看個例子:

#include <stdio.h>
int main() {
	int a[5] = { 1,2,3,4,5 };
	int *ptr = (int *)(&a + 1);
	printf("a=0x%p\n", a);
	printf("&a=0x%p\n",  &a);
	printf("%d,%d,0x%x", *(a + 1), *(ptr - 1), (unsigned int)a + 1);
	return 0;
}

輸出結果:
在這裏插入圖片描述
代碼分析:

  1. a和&a的值是一樣的,都是0x1BFDEC,但意思不一樣,a是數組首元素的地址,即a[0]的地址,&a是一個指向這個數組的地址,所以a和&a所代表的地址是一樣的,但類型不一樣,a是一個數組首元素地址,&a是一個指向這個數組的指針
  2. 再看(&a + 1)代表什麼含義,&a表示指向這個數組的指針,所以(&a + 1)表示指向這個數組的指針+1,即&a+1 * 5 * sizeof(int),也就是下一個數組的首地址,顯然當前指針已經越過了數組的界限。
  3. 再看*(a + 1)代表什麼含義,a是一個數組首元素的地址,所以a+1,表示數組下一個元素的地址,即a[1]的地址,*(a + 1)表示的就是a[1]的值,即2.
  4. 同理,*(ptr - 1)的值就可以算了,ptr的值即爲(&a + 1)的值,指向的是下一個數組的首元素,那麼(ptr - 1)的值就是指向這個數組的最後一個元素, *(ptr - 1)的值就爲5.
  5. (unsigned int)a把a強制轉換成unsigned int型的數據,那麼(unsigned int)a + 1)就爲0x1BFDEC+1=0x1BFDED

二、函數void func(void)中func和&func的區別

看個例子:


#include <stdio.h>
void func(void)
{
	printf("hello.\n");
}
int main(void)
{
	printf("func=%x\n", func);
	printf("&func=%x\n", &func);
	return 0;
}

輸出結果:
在這裏插入圖片描述
代碼分析:

  1. func是函數的首地址,它的類型是void ()。
  2. &func表示一個指向函數的地址,它的類型是void (*)()。
  3. 因此func和&func所代表的地址值是一樣的,但類型不一樣。func是函數首地址,&func是一個指向函數的指針

再分析個例子:
想讓程序跳轉到絕對地址0x100000處執行,該如何做?
答案爲:

(*(void(*) ())0x100000)()
((void(*) ())0x100000)()//寫成這樣也對

代碼分析:

  1. void(*) (),可以明白這是一個函數指針類型。這個函數沒有參數,沒有返回值。
  2. (void(*) ())0x100000,這是將 0x100000 強制轉換爲函數指針類型, 0x100000 是一個地址,也就是說一個函數存在首地址爲 0x100000 的一段區域內.
  3. (* (void(* ) ())0x100000),這是取 0x100000 地址開始的一段內存裏面的內容,其內容就是保存在首地址爲 0x100000的一段區域內的函數
  4. (* (void(* ) ())0x100000)(),這是函數調用。實際上寫成((void(*) ())0x100000)()也能編譯通過,並正確執行。

三、總結

按照&運算符本來的意義,它要求其操作數是一個對象,但函數名不是對象(函數是一個對象),本來&func是非法的,但很久以前有些編譯器已經允許這樣做,c/c++標準的制定者出於對象的概念已經有所發展的緣故,也承認了&func的合法性。同理,數組也是一樣的道理。
所以,既然 & 不 & 都可以,那麼* 不 * 也都可以。
這樣也就解釋了爲什麼( * (void( * ) ())0x100000)()或者((void( * ) ())0x100000)()都對。
對於以函數名或者數組名作爲函數的參數來說,& 不& 都無所謂,
對於函數調用來說,函數名字前加不加 * 都無所謂。

但是對於數組,&a+1,和a+1作爲右值來說還是要加以區分,&a+1相當於指針+1(指向下個數組),a+1相當於地址加1(指向下個元素),(unsigned int)a+1相當於數值+1,結果是不一樣的

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章