指针深入解析。。。。。。。。。。

来源:互联网 发布:显卡温度检测软件 编辑:程序博客网 时间:2024/06/05 08:44

第一章:指针相关概念

part1
指针的概念:
 指针是一个特殊的变量;它里面存储的值为内存的地址

part2
指针的类型与指针所指向的类型:
 指针的类型:你只要把指针声明语句里的指针名字去掉,剩下的部分就是这个指针的类型。
  例如:int* ptr;    //ptr的指针类型为 int*
  
 指针所指向的类型:你只须把指针声明语句中的指针名字和名字左边的指针声明符*去掉,剩下的就是指针所指向的类型。
 例如:int* ptr; //该指针所指向的数据类型为int型

  !!!提问:int a;
    char b;
    ptr = &a; //指针所指向的数据类型为?
    ptr = &b; //指针所指向的数据类型为?
 注意:当你通过指针来访问指针所指向的内存区时,指针所指向的类型决定了编译器将把那片内存区里的内容当做什么来看待。

part3
指针的值(指针所指向的内存区)和指针本身所占内存:
 指针的值:指针的值是指针本身存储的数值,这个值将被编译器当作一个地址,而不是一个一般的数值。
 指针所指向的内存区:指针所指向的内存区就是从指针的值所代表的那个内存地址开始,长度为si zeof(指针所指向的类型)的一片内存区。
 指针本身所占内存:在32位平台里,指针本身占据了4个字节的长度。   

 

练习题:

(1)int *ptr;
(2)char *ptr;
(3)int **ptr;
(4)int (*ptr)[3];
(5)int *(*ptr)[4];

 


第二章:指针相关运算

part1
 指针的算数运算:
  指针可以加上或减去一个整数。指针的这种运算的意义和通常的数值的加减运算的意义是不一样的。
 例如:int buf[10];
  int *ptr = buf;
  ptr += 5 ;

  再或者:
  char buf[10];
  int *ptr = buf;
  ptr += 5 ;
 
part2
 运算符&和*:
  char a;    &a (是一个表达式)表示一个指针:指针类型为 char *,所指为char
 运算符&:为取地址运算符;&a的运算结果是一个指针,指针的类型是a的类型加个*
 运算符*:为取值运算符;*p的结果是p所指向的东西。这个东西有这些特点:它的类型是p指向的类型,它所占用的地址是p所指向的地址。
 
 练习题:
  int dat = 10;
 char *ptrc = (char *)&dat;
 int *ptri = &dat;
 
 &dat运算结果是一个指针, 该指针的类型为char *
 *ptrc运算结果为指针所指向的东西, 该东西的类型为 char
 *ptri运算结果为指针所指向的东西, 该东西的类型为 int 


part3
 指针表达式:
  一个表达式的最后结果如果是一个指针,那么这个表达式就叫指针表式。
  
 例六:
int a,b;
int array[10];
int *pa;
pa = &a; //&a是一个指针表达式。
int **ptr=&pa; //&pa也是一个指针表达式。
*ptr = &b; //*ptr和&b都是指针表达式。
pa = array;
pa++;  //这也是指针表达式。
  例七:
/***********************************
char (*arr)[20];
char **ptr = arr ;
prt++;  //偏移4个字节
arr++ ;  //偏移20字节 
***********************************/


char *arr[20];
char **parr=arr; //如果把arr看作指针的话,arr也是指针表达式
parr+1; //
arr+1;//偏移一个地址(4字节)
char *str;
str= *parr;  //*parr是指针表达式
str= *(parr+1); //*(parr+1)是指针表达式
str= *(parr+2); //*(parr+2)是指针表达式
  由于指针表达式的结果是一个指针,所以指针表达式也具有指针所具有的四个要素:指针的类型,指针所指向的类型,指针指向的内存区,指针自身占据的内存。

第三章:指针的扩展

part1
数组和指针:
 数组的数组名其实可以看作一个指针。(以下通过几个例子来帮助我们学习)

 声明一个数组:
  TYPE array[n] ;

 array有两重含义:
 第一,它代表整个数组,它的类型是TYPE[n];
 第二 ,它是一个指针,该指针的类型是TYPE*,该指针指向的类型是TYPE,该指针指向的内存区就是数组第0号单元,该指针自己占有单独的内存区(注意它和数组第0号单元占据的内存区是不同的)。
 注意:该指针的值是不能修改的,即类似array++的表达式是错误的。

 NO.1 普通数组:
 例八:
int array[10]={0,1,2,3,4,5,6,7,8,9};
int value;
...
...
value=array[0]; //也可写成:value=*array;
value=array[3]; //也可写成:value=*(array+3);
value=array[4]; //也可写成:value=*(array+4);

&array  array

  上例中,如果数组名array代表数组本身,类型是   int[10]    ;如果把array看做指针的话,它指向   数组的第一个元素(首地址)     ,类型是   int*   .
array+3指向  第三个成员    ,*array等于   0   ,*(array+3)等于   3   。

 NO.2 指针数组:
 例九:
char *str[3]={
 "Hello,thisisasample!",
 "Hi,goodmorning.",
 "Helloworld"
};
sizeof(str);
char s[80];
strcpy(s,str[0]); //也可写成strcpy(s,*str);
strcpy(s,str[1]); //也可写成strcpy(s,*(str+1));
strcpy(s,str[2]); //也可写成strcpy(s,*(str+2));

   上例中,如果str代表数组本身,类型为   char* [3]    ;把指针数组名str当作一个指针,类型为  char **     ,它指向的类型是  char*   。那么,str+1呢?
 上例中,*str = str[0]也是一个指针,类型是   char*   ,它所指向的类型是  char   ,它指向的地址是     str[0]        。 那么*(str + 1)呢str[1]?
 

数组名在表达式中 :
   1. 在表达式sizeof(array)中,数组名array代表数组本身,此时测出的是整个数组的大小。
 2. 在表达式*array中,array扮演的是指针,因此这个表达式的结果就是数组第0号单元的值。
 3. 在表达式sizeof(*array)中,array扮演指针,此时测出的是数组具体元素的大小。
 4. 表达式array+n(其中n=0,1,2,....。)中,array扮演的是指针,故array+n的结果是一个指针。
 5.表达式sizeof(array+n)中,array扮演的是指针,测出的是指针类型的大小。

 NO.3 数组指针:
 例十:
int array[10];
int (*ptr)[10];
ptr = &array;

  上例中ptr是一个指针,它的类型是int(*)[10],他指向的类型是int[10] ,我们用整个数组的首地址来初始化它。在语句ptr=&array中,array代表数组本身。
 
 扩展深入:
 int array[3][10];
 int (*ptr)[10]; int(*)[]
 ptr = array;
  ptr数组指针指向二维数组。提问:ptr++地址偏移多少个字节

  本节中提到了函数sizeof(),问一问,sizeof(指针名称)测出的究竟是指针自身类型的大小呢还是指针所指向的类型的大小?
      例如:
int(*ptr)[10];

   则在32位程序中,有:
sizeof(int(*)[10])==4
sizeof(int[10])==40
sizeof(ptr)==4
   实际上,sizeof(对象)测出的都是对象自身的类型的大小,而不是别的什么类型的大小。

  二维数组的“滥用“方式:
        char aa[2][5] = {{1,2,3,4,5},
    {6,7,8,9,10}};  //定义一个二维数组
 
提问:
 aa是什么?
 **aa是什么? *(*(aa+0) + 0)  = aa[0][0] 
 *aa是什么?  =*(aa+1)  = aa[0]

 aa[1]是什么? *(aa+1)
 aa[1] + 1是什么?  *(aa+1) + 1

 aa[1][1] 是什么? *(*(aa+1) + 1)

 *(aa[1] + 1)是什么?
 *(*(aa+1) + 1)是什么?

 aa[1] 和 *(aa+1)是什么?
  &aa[1] 和 aa+1是什么?

练习题:
 测试一:
 char aa[2][3];
 char (*ptr)[3];
 char **ptr1;

 ptr = aa;  //可行吗
 ptr = &aa[0]; //可行吗
 ptr1 = aa;   //可行吗x


 测试二:
 char *aa[3];
 char **ptr;

 ptr = aa;  //可行吗,是否等价


part2
结构体和指针:
 例十一:
struct MyStruct {
 inta;
 intb;
 intc;
}
MyStruct ss={20,30,40}; //声明了结构对象ss,并把ss的三个成员初始化为20,30和40。
MyStruct *ptr=&ss;  //声明了一个指向结构对象ss的指针。它的类型是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。

 提问,第二种访问方式是否有问题?(字节对齐)

part3
指针和函数:
 函数指针:指向函数的指针
  可以把一个指针声明成为一个指向函数的指针。
int fun1(char*,int);
int (*pfun1)(char*,int);
pfun1=fun1;
....
....
int a=(*pfun1)("abcdefg",7);//通过函数指针调用函数。
  可以把指针作为函数的形参。在函数调用语句中,可以用指针表达式来作为实参。

 易混淆概念:(指针函数)
  指针函数为返回值为指针的函数,例如: int * fun1(char*, int);

 几种常见的函数指针:
  普通函数指针:
  int fun1(char*,int);   //函数返回值为int型
  int (*pfun1)(char*,int);
  pfun1=fun1;
  返回值为指针的函数指针:
  int* fun1(char*,int);   //函数返回值为int型
  int* (*pfun1)(char*,int);
  pfun1=fun1;
  返回值为函数指针的函数:
  int (*fun1(char*,int))(char *, int);   //函数返回值为int型

  **你会用typedef吗??

part4
指针类型转换:
 当我们初始化一个指针或给一个指针赋值时,赋值号的左边是一个指针,赋值号的右边是一个指针表达式。前面所举的例子中,绝大多数情况下,指针的类型和指针表达式的类型是一样的,指针所指向的类型和指针表达式所指向的类型是一样的。

 那有没有不同的情况呢?

 例十四:
  float f=12.3;
  float* fptr=&f;  //常规用法
  int* p;
 
 情况一:
  p=&f;
  不对。因为指针p的类型是int*,它指向的类型是int。表达式&f的结果是一个指针,指针的类型是float*,它指向的类型是float。两者不一致,直接赋值的方法是不行的。

情况二:
p=(int*)&f;
  如果有一个指针p,我们需要把它的类型和所指向的类型改为TYEP*TYPE, 语法格式是: (TYPE*)p;

  注意:这样强制类型转换的结果是一个新指针,该新指针的类型是TYPE*,它指向的类型是TYPE,它指向的地址就是原指针指向的地址。而原来的指针p的一切属性都没有被修改。

  知识点:一个函数如果使用了指针作为形参,那么在函数调用语句的实参和形参的结合过程中,也会发生指针类型的转换。
  例如:
           void fun(char*);
int a=125;
fun((char*)&a);
...
...
void fun(char*s)
{
...
}


 我们已经知道,指针的值就是指针指向的地址,在32位程序中,指针的值其实是一个32位整数。那可不可以把一个整数当作指针的值直接赋给指针呢?
例十五: 
unsigned int a;
TYPE *ptr;  //TYPE是int,char或结构类型等等类型。
...
...
a = 20345686;
ptr=20345686; //我们的目的是要使指针ptr指向地址20345686(十进制)
ptr=a;  //我们的目的是要使指针ptr指向地址20345686(十进制)

 大家想想编译能通过吗?正确的应当如何来写? 
 。。。
 
 思考:我们能不能将指针指向的地址即指针的值当作一个整数取出来?

 例十六:
inta=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;

 
 例十八:
char a;
int* ptr=&a;
...
...
ptr++;
*ptr=115;

 

在指针的强制类型转换:ptr1=(TYPE*)ptr2中,如果sizeof(ptr2的类型)大于sizeof(ptr1的类型),那么在使用指针ptr1来访问ptr2所指向的存储区时是安全的。如果sizeof(ptr2的类型)小于sizeof(ptr1的类型),那么在使用指针ptr1来访问ptr2所指向的存储区时是不安全的。

 

 

 

原创粉丝点击