⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 我眼中的指针.txt

📁 集中探讨C指针的文章--我眼中的指针,可以参考。
💻 TXT
📖 第 1 页 / 共 2 页
字号:

在不同的表达式中数组名array可以扮演不同的角色。  

在表达式sizeof(array)中,数组名array代表数组本身,故这时sizeof函数  

测出的是整个数组的大小。  

在表达式*array中,array扮演的是指针,因此这个表达式的结果就是数组第  

0号单元的值。sizeof(*array)测出的是数组单元(GCR注:数组元素)的大小。  

表达式array+n(其中n=0,1,2,....。)中,array扮演的是指针,故arr  

ay+n的结果是一个指针,它的类型是TYPE*,它指向的类型是TYPE,它指向数组第  

n号单元。故sizeof(array+n)测出的是指针类型的大小。  

例十:  

int array[10];  

int (*ptr)[10];  

ptr=&array;
        (GCR注:数组名并不完全是指针,不能用ptr=array的方法赋值,\
                因为ptr是指向数组的指针,array是指向数组第一个元素的指针\
                所以他们的“指针”类型不同,不能对等赋值,只有对array取指运算后才可以)  

上例中ptr是一个指针,它的类型是int (*)[10],他指向的类型是int [10]  

,我们用整个数组的首地址来初始化它。在语句ptr=&array中,array代表数组本  

身。 (GCR注:&array代表数组的地址,而不是数组第一个元素的地址) 


本节中提到了函数sizeof(),那么我来问一问,sizeof(指针名称)测出的究  

竟是指针自身类型的大小呢还是指针所指向的类型的大小?答案是前者。例如:  


int (*ptr)[10];  

则在32位程序中,有:  

sizeof(int(*)[10])==4  

sizeof(int [10])==40  

sizeof(ptr)==4  

实际上,sizeof(对象)测出的都是对象自身的类型的大小,而不是别的什么  

类型的大小。 


第六章。指针和结构类型的关系  


可以声明一个指向结构类型对象的指针。  

例十一:  

struct MyStruct  

{  

int a;  

int b;  

int c;  

}  

MyStruct ss={20,30,40};//声明了结构对象ss,并把ss的三个成员初始  

化为20,30和40。  

MyStruct *ptr=&ss;//声明了一个指向结构对象ss的指针ptr。它的类型是  

MyStruct*,它指向的类型是MyStruct。  

int *pstr=(int*)&ss;//声明了一个指向结构对象ss的指针。但是它的  

类型和它指向的类型和ptr是不同的。  


请问怎样通过指针ptr来访问ss的三个成员变量?  

答案:  

ptr->a;  

ptr->b;  

ptr->c;  

又请问怎样通过指针pstr来访问ss的三个成员变量?  

答案:  

*pstr;//访问了ss的成员a。  

*(pstr+1);//访问了ss的成员b。  

*(pstr+2)//访问了ss的成员c。  

呵呵,虽然我在我的MSVC++6.0上调式过上述代码,但是要知道,这样使用p  

str来访问结构成员是不正规的,为了说明为什么不正规,让我们看看怎样通过指  

针来访问数组的各个单元:  

例十二:  

int array[3]={35,56,37};  

int *pa=array;(GCR注:由此可见array是指向数组的第一个元素,而不是指向数组本身)  

通过指针pa访问数组array的三个单元的方法是:  

*pa;//访问了第0号单元  

*(pa+1);//访问了第1号单元  

*(pa+2);//访问了第2号单元  

从格式上看倒是与通过指针访问结构成员的不正规方法的格式一样。  

所有的C/C++编译器在排列数组的单元时,总是把各个数组单元存放在连续的  

存储区里,单元和单元之间没有空隙。但在存放结构对象的各个成员时,在某种  

编译环境下,可能会需要字对齐或双字对齐或者是别的什么对齐,需要在相邻两  

个成员之间加若干?quot;填充字节",这就导致各个成员之间可能会有若干个字节  

的空隙。  

所以,在例十二中,即使*pstr访问到了结构对象ss的第一个成员变量a,也  

不能保证*(pstr+1)就一定能访问到结构成员b。因为成员a和成员b之间可能会有  

若干填充字节,说不定*(pstr+1)就正好访问到了这些填充字节呢。这也证明了指  

针的灵活性。要是你的目的就是想看看各个结构成员之间到底有没有填充字节,  

嘿,这倒是个不错的方法。  

通过指针访问结构成员的正确方法应该是象例十二中使用指针ptr的方法。  


第七章。指针和函数的关系  


可以把一个指针声明成为一个指向函数的指针。  

int fun1(char*,int);  

int (*pfun1)(char*,int);  

pfun1=fun1;  (GCR注:呵呵函数的指针就没有数组的指针那么麻烦,函数名就是函数的指针
                      可以直接赋值)

....  

....  

int a=(*pfun1)("abcdefg",7);//通过函数指针调用函数  

可以把指针作为函数的形参。在函数调用语句中,可以用指针表达式来作为实参。  

例十三:  

int fun(char*);  

int a;  

char str[]="abcdefghijklmn";  

a=fun(str);  

...  

...  

int fun(char*s)  

{  

int num=0;  

for(int i=0;i 

{  

num+=*s;s++;  

}  

return num;  

)  

这个例子中的函数fun统计一个字符串中各个字符的ASCII码值之和。前面说  

了,数组的名字也是一个指针。在函数调用中,当把str作为实参传递给形参s后  

,实际是把str的值传递给了s,s所指向的地址就和str所指向的地址一致,但是  

str和s各自占用各自的存储空间。在函数体内对s进行自加1运算,并不意味着同  

时对str进行了自加1运算。(GCR注:经常采用对形参用const修饰的方法) 


第八章。指针类型转换  


当我们初始化一个指针或给一个指针赋值时,赋值号的左边是一个指针,赋  

值号的右边是一个指针表达式。在我们前面所举的例子中,绝大多数情况下,指  

针的类型和指针表达式的类型是一样的,指针所指向的类型和指针表达式所指向  

的类型是一样的。  

例十四:  

1。 float f=12.3;  

2。 float *fptr=&f;  

3。 int *p;  

在上面的例子中,假如我们想让指针p指向实数f,应该怎么搞?是用下面的  

语句吗?  

p=&f;  

不对。因为指针p的类型是int*,它指向的类型是int。表达式&f的结果是一  

个指针,指针的类型是float*,它指向的类型是float。两者不一致,直接赋值的  

方法是不行的。至少在我的MSVC++6.0上,对指针的赋值语句要求赋值号两边的类  

型一致,所指向的类型也一致,其它的编译器上我没试过,大家可以试试。为了  

实现我们的目的,需要进行"强制类型转换":  

p=(int*)&f;  

如果有一个指针p,我们需要把它的类型和所指向的类型改为TYEP*和TYPE,  

那么语法格式是:  

(TYPE*)p;  

这样强制类型转换的结果是一个新指针,(GCR注在:注意是一个新指针)该新指针的类型是TYPE*,它指向的  

类型是TYPE,它指向的地址就是原指针指向的地址。而原来的指针p的一切属性都  

没有被修改。  


一个函数如果使用了指针作为形参,那么在函数调用语句的实参和形参的结  

合过程中,也会发生指针类型的转换。  

例十五:  

void fun(char*);  

int a=125,b;  

fun((char*)&a);  

...  

...  

void fun(char*s)  

{  

char c;  

c=*(s+3);*(s+3)=*(s+0);*(s+0)=c; (GCR注:) 

c=*(s+2);*(s+2)=*(s+1);*(s+1)=c;  

}  

}  

注意这是一个32位程序,故int类型占了四个字节,char类型占一个字节。函  

数fun的作用是把一个整数的四个字节的顺序来个颠倒。注意到了吗?在函数调用  

语句中,实参&a的结果是一个指针,它的类型是int *,它指向的类型是int。形  

参这个指针的类型是char*,它指向的类型是char。这样,在实参和形参的结合过  

程中,我们必须进行一次从int*类型到char*类型的转换。结合这个例子,我们可  

以这样来想象编译器进行转换的过程:编译器先构造一个临时指针 char*temp,  

然后执行temp=(char*)&a,最后再把temp的值传递给s。所以最后的结果是:s的  

类型是char*,它指向的类型是char,它指向的地址就是a的首地址。  


我们已经知道,指针的值就是指针指向的地址,在32位程序中,指针的值其  

实是一个32位整数。那可不可以把一个整数当作指针的值直接赋给指针呢?就象  

下面的语句:  

unsigned int a;  

TYPE *ptr;//TYPE是int,char或结构类型等等类型。  

...  

...  

a=20345686;  

ptr=20345686;//我们的目的是要使指针ptr指向地址20345686(十进制  


ptr=a;//我们的目的是要使指针ptr指向地址20345686(十进制)  

编译一下吧。结果发现后面两条语句全是错的。那么我们的目的就不能达到  

了吗?不,还有办法:  

unsigned int a;  

TYPE *ptr;//TYPE是int,char或结构类型等等类型。  

...  

...  

a=某个数,这个数必须代表一个合法的地址;  

ptr=(TYPE*)a;//呵呵,这就可以了。(GCR注:嵌入式系统程序中经常将一个无符号整数
                                 强制为地址,但一般要加volatile。)  

严格说来这里的(TYPE*)和指针类型转换中的(TYPE*)还不一样。这里的(TYP  

E*)的意思是把无符号整数a的值当作一个地址来看待。  

上面强调了a的值必须代表一个合法的地址,否则的话,在你使用ptr的时候  

,就会出现非法操作错误。  


想想能不能反过来,把指针指向的地址即指针的值当作一个整数取出来。完  

全可以。下面的例子演示了把一个指针的值当作一个整数取出来,然后再把这个  

整数当作一个地址赋给一个指针:  

例十六:  

int a=123,b;  

int *ptr=&a;  

char *str;  

b=(int)ptr;//把指针ptr的值当作一个整数取出来。  

str=(char*)b;//把这个整数的值当作一个地址赋给指针str。  


好了,现在我们已经知道了,可以把指针的值当作一个整数取出来,也可以  

把一个整数值当作地址赋给一个指针。  


第九章。指针的安全问题  


看下面的例子:  

例十七:  

char s='a';  

int *ptr;  

ptr=(int*)&s;  

*ptr=1298;  

指针ptr是一个int*类型的指针,它指向的类型是int。它指向的地址就是s的  

首地址。在32位程序中,s占一个字节,int类型占四个字节。最后一条语句不但  

改变了s所占的一个字节,还把和s相临的高地址方向的三个字节也改变了。这三  

个字节是干什么的?只有编译程序知道,而写程序的人是不太可能知道的。也许  

这三个字节里存储了非常重要的数据,也许这三个字节里正好是程序的一条代码  

,而由于你对指针的马虎应用,这三个字节的值被改变了!这会造成崩溃性的错误。  

让我们再来看一例:  

例十八:  

1。 char a;  

2。 int *ptr=&a;  

...  

...  

3。 ptr++;  

4。 *ptr=115;  

该例子完全可以通过编译,并能执行。但是看到没有?第3句对指针ptr进行  

自加1运算后,ptr指向了和整形变量a相邻的高地址方向的一块存储区。这块存储  

区里是什么?我们不知道。有可能它是一个非常重要的数据,甚至可能是一条代  

码。而第4句竟然往这片存储区里写入一个数据!这是严重的错误。所以在使用指  

针时,程序员心里必须非常清楚:我的指针究竟指向了哪里。  

在用指针访问数组的时候,也要注意不要超出数组的低端和高端界限,否则  

也会造成类似的错误。  

在指针的强制类型转换:ptr1=(TYPE*)ptr2中,如果sizeof(ptr2的类型)大  

于sizeof(ptr1的类型),那么在使用指针ptr1来访问ptr2所指向的存储区时是安  

全的。如果sizeof(ptr2的类型)小于sizeof(ptr1的类型),那么在使用指针ptr1  

来访问ptr2所指向的存储区时是不安全的。至于为什么,读者结合例十七来想一  

想,应该会明白的。 



 

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -