題記:
關(guān)于指針,推薦看一下csdn飛天御劍流的《再再論指針》,相信對(duì)C語(yǔ)言指針會(huì)有一個(gè)更為清晰全面的理解。
指針是C語(yǔ)言的精華,它是一柄“雙刃劍”,用的好與壞就看使用者的功力了。下面就一道面試題,看一下指針與數(shù)組的區(qū)別。
char *p1, *p2;
char ch[12];
char **pp;
p1 = ch;
pp = &ch;
p2 = *pp;
問(wèn)p1和p2是否相同
|
題目如上,找出其中的不妥之處。
首先,數(shù)組ch是沒(méi)有初始化的。其次,一個(gè)比較隱含的地方是,數(shù)組名可以代表數(shù)組第一個(gè)元素的首地址,這個(gè)沒(méi)有問(wèn)題,但是,數(shù)組名并非一個(gè)變量,數(shù)組分配完成后,數(shù)組名就是固定的,地址也是固定的。這樣導(dǎo)致的結(jié)果就是絕對(duì)不能把數(shù)組名當(dāng)作變量來(lái)進(jìn)行處理。上述題目中,pp=&ch,顯然是把數(shù)組名當(dāng)作指針變量來(lái)使用了,這樣肯定出問(wèn)題。
這個(gè)題目存在的兩個(gè)問(wèn)題,第一個(gè)問(wèn)題比較簡(jiǎn)單,可以認(rèn)為是粗心大意。但是第二個(gè)問(wèn)題就是相當(dāng)復(fù)雜了,擴(kuò)展開(kāi)來(lái),那就是C語(yǔ)言中的精華中的指針和數(shù)組的聯(lián)系與區(qū)別問(wèn)題了。
下面分為兩步,首先看一下指針和數(shù)組的區(qū)別方法,然后提出對(duì)上述程序的修改方案。
1 指針和數(shù)組的區(qū)別
(1)指針和數(shù)組的分配
數(shù)組是開(kāi)辟一塊連續(xù)的內(nèi)存空間,數(shù)組本身的標(biāo)識(shí)符(也就是通常所說(shuō)的數(shù)組名)代表整個(gè)數(shù)組,可以使用sizeof來(lái)獲得數(shù)組所占據(jù)內(nèi)存空間的大小(注意,不是數(shù)組元素的個(gè)數(shù),而是數(shù)組占據(jù)內(nèi)存空間的大小,這是以字節(jié)為單位的)。舉例如下:
#include <stdio.h>
int main(void)
{
char a[] = "hello";
int b[] = {1, 2, 3, 4, 5};
printf("a:
%d\n", sizeof(a));
printf("b
memory size: %d bytes\n", sizeof(b));
printf("b
elements: %d\n", sizeof(b)/sizeof(int));
return 0;
}
|
數(shù)組a為字符型,后面的字符串實(shí)際上占據(jù)6個(gè)字節(jié)空間(注意最后有一個(gè)\0標(biāo)識(shí)字符串的結(jié)束)。從后面sizeof(b)就可以看出如何獲得數(shù)組占據(jù)的內(nèi)存空間,如何獲得數(shù)組的元素?cái)?shù)目。至于int數(shù)據(jù)類型分配內(nèi)存空間的多少,則是編譯器相關(guān)的。gcc默認(rèn)為int類型分配4個(gè)字節(jié)的內(nèi)存空間。
(2)空間的分配
這里又分為兩種情況。
第一,如果是全局的和靜態(tài)的
char *p = “hello”;
這是定義了一個(gè)指針,指向rodata section里面的“hello”,可以被編譯器放到字符串池。在匯編里面的關(guān)鍵字為.ltorg。意思就是在字符串池里的字符串是可以共享的,這也是編譯器優(yōu)化的一個(gè)措施。
char a[] = “hello”;
這是定義了一個(gè)數(shù)組,分配在可寫(xiě)數(shù)據(jù)塊,不會(huì)被放到字符串池。
第二,如果是局部的
char *p = “hello”;
這是定義了一個(gè)指針,指向rodata section里面的“hello”,可以被編譯器放到字符串池。在匯編里面的關(guān)鍵字為.ltorg。意思就是在字符串池里的字符串是可以共享的,這也是編譯器優(yōu)化的一個(gè)措施。另外,在函數(shù)中可以返回它的地址,也就是說(shuō),指針是局部變量,但是它指向的內(nèi)容是全局的。
char a[] = “hello”;
這是定義了一個(gè)數(shù)組,分配在堆棧上,初始化由編譯器進(jìn)行。(短的時(shí)候直接用指令填充,長(zhǎng)的時(shí)候就從全局字符串表拷貝),不會(huì)被放到字符串池(同樣如前,可能會(huì)從字符串池中拷貝過(guò)來(lái))。注意不應(yīng)該返回它的地址。
(3)使用方法
如果是全局指針,用于不需要修改內(nèi)容,但是可能會(huì)修改指針的情況。
如果是全局?jǐn)?shù)組,用于不需要修改地址,但是卻需要修改內(nèi)容的情況。
如果既需要修改指針,又需要修改內(nèi)容,那么就定義一個(gè)數(shù)組,再定義一個(gè)指針指向它就可以了。
2 我編寫(xiě)的修改方案
[armlinux@lqm pointer]$ cat pointer.c
/*
* Copyright 2007 (c), Shandong University
* All rights reserved.
*
* Filename : test.c
* Description: about pointer
* Author : Liu Qingmin
* Version : 1.0
* Date : 2007-08-27
*/
#include <stdio.h>
/*
* define a macro which is used to debug array mode and pointer
mode.
* if 1, debug array mode; else debug pointer mode.
* You can change it according to your decision.
*/
#define
ARRAY_OR_POINTER 0
int main(void)
{
char *p1;
char *p2;
char **pp;
//test1
#if ARRAY_OR_POINTER
char ch[] = "hello, world!\n";
printf("%d, %d, %d, %d\n", sizeof(p1), sizeof(p2),
sizeof(pp), sizeof(ch));
#else
char *ch = "hello, world!\n";
printf("%d, %d, %d, %d\n", sizeof(p1), sizeof(p2),
sizeof(pp), sizeof(ch));
#endif
//test2
p1 = ch;
#if ARRAY_OR_POINTER
pp = &p1;
#else
pp = &ch;
#endif
p2 = *pp;
if (p1 == p2) {
printf("p1 equals to p2\n");
} else {
printf("p1 doesn't equal to
p2\n");
}
return 0;
}
|
執(zhí)行結(jié)果如下:
// ARRAY_OR_POINTER為0時(shí)
[armlinux@lqm pointer]$ ./test
4, 4, 4, 4
p1 equals to p2
// ARRAY_OR_POINTER為1時(shí)
[armlinux@lqm pointer]$ ./test
4, 4, 4, 15
p1 equals to p2
|
如果使用了數(shù)組定義方式,而又使用pp=&ch,那么就會(huì)出現(xiàn)類似下面的錯(cuò)誤:
[armlinux@lqm pointer]$ make
gcc
-Wall
-g
-O2
-c
-o
pointer.o
pointer.c
pointer.c: In function
`main':
pointer.c:44: warning: assignment from incompatible pointer type
gcc -Wall -g -O2 pointer.o -o test
[armlinux@lqm pointer]$ ./test
4, 4, 4, 15
p1 doesn't equal to p2
|
|