1、指针的定义与初始化

如何理解指针? 首先要在回答指针是什么时一定要说指针是变量,这样的话,指针就有了变量的特性。
(1) 系统为指针分配内存空间;
(2) 指针有自己的地址;
(3)指针能够存值,但这个值比较特殊–地址。
指针的字节长度: 任何类型指针的长度都是4个字节(32)系统,指针变量是一个地址,在操作系统中地址的长度是固定的。

2、* 与&运算符详解(用于取内容(*)和取地址(&)两种运算。)

  1. 指针的类型和指针所指向的类型
    (1)指针的类型
    从语法的角度看,你只要把指针声明语句里的指针名字去掉,剩下的部分就是这个指针的类型,这是指针本身所具有的类型。让我们看看例子中各个指针的类型。
    (1)int * ptr; //指针的类型是int*
    (2)char * ptr; //指针的类型是char*
    (3)int *ptr; //指针的类型是int *
    (4)int (* ptr)[3]; //指针的类型是int(*)[3]
    (5)int * (* ptr)[4]; //指针的类型是int*( *)[4]

    (2)指针所指向的类型
    当你通过指针来访问指针所指向的内存区时,指针所指向的类型决定了编译器将把那片内存区里的内容当作什么来看待。从语法上看,只须把指针声明语句中的指针名字和名字左边的指针声明符“*”去掉,剩下的就是指针所指向的类型。例如:
    (1)int*ptr; //指针所指向的类型是int
    (2)char*ptr; //指针所指向的的类型是char
    (3)int * * ptr; //指针所指向的的类型是int*
    (4)int (*ptr)[3]; //指针所指向的的类型是int()[3]
    (5)int* (* ptr)[4]; //指针所指向的的类型是int*()[4]

  2. 指针的值
    指针的值也叫作指针所指向的内存区或地址。指针的值是指针本身存储的数值,这个值将被编译器当作一个地址,而不是一个一般的数值。在32位程序里,所有类型的指针的值都是一个32位整数,因为32位程序里内存地址全都是32位长。指针所指向的内存区就是从指针的值所代表的那个内存地址开始,长度为sizeof(指针所指向的类型)的一片内存区。以后,我们说一个指针的值是XX,就相当于说该指针指向了以XX为首地址的一片内存区域;我们说一个指针指向了某块内存区域,就相当于说该指针的值是这块内存区域的首地址。

3、指针运算

  1. 指针的赋值运算

指针也是一个变量,它自己占据一个4个字节的地址空间(由于程序的寻址空间是2^32次方,即4GB,所以用4个字节表示指针就已经能指向任何程序能够寻址到的空间了,所以指针的大小为4字节),指针的值是另一个东西的地址,这个东西可以是普通变量,结构体,还可以是个函数等等。由于,指针的大小是4字节,所以,我们可以将指针强制转换成int型或者其他类型。同样,我们也可以将任何一个常数转换成int型再赋值给指针。所有的指针所占的空间大小都是4字节,他们只是声明的类型不同,他们的值都是地址指向某个东西,他们对于机器来说没有本质差别,他们之间可以进行强制类型转换。

C语言中,任何一个变量都必须占有一个地址,而这个地址空间内的0-1代码就是这个变量的值。不同的数据类型占有的空间大小不一,但是他们都必须有个地址,而这个地址就是硬件访问的依据,而名字只是提供给程序员的一种记住这个地址的方便一点的方法。但是,不同的变量在机器中都是0-1代码,所以,我们不能简单的通过检查一个值的位来判断它的类型。
不同类型指针的转换涉及到强制类型转换

指针 to 指针的强制类型转换是指将指针所指的内容的类型由原先的类型转换为后面的类型。
int a = 1;
int *p = & a;
float * p1 = (float*)p;
则p和p1的值都是&a,但是* p是将&a地址中的值按照int型变量进行解释,而*p1则是将&a地址中的值按照float型变量进行解释。

//指针类型强制转换:
int m;
int *pm = &m;
char *cp = (char *)&m;
//pm指向一个整型,cp指向整型数的第一个字节

下面的例子说明了指针的步长问题。

/*指针的移动有个步长,步长等于sizeof(指针指向的元素类型) */
#include<stdio.h>
int main(void)
{ 
    int a[5] = {1, 2, 3, 4, 5}; 
    int *ptr1 = (int*)(&a + 1); //&a(数组指针)指针指向的元素为整个数组,故加为sizeof(数组) 
    int *ptr2 = (int*)((int)a + 1); //a地址再加一个字节,直接地址值相加而不是指针 
    int *ptr3 = (int*)(a + 1); //a为数组首元素的地址,a+1为数组第二个元素的地址
/* 数组a在内存的存放形式为 *01000000 020000000 03000000 04000000 05000000 
*ptr2指向01000000的第二个字节,故*ptr2=00000002 * */ 
printf("%x %x %x\n", ptr1[-1], *ptr2, *ptr3); 
return 0;
}

这里写图片描述

赋值运算遇到的三个问题

  1. NULL(空指针)
  2. 野指针
  3. 指针变量的之间的赋值

(1)NULL指针:NULL 是一个标准规定的宏定义,用来表示空指针常量。

#define   NULL   (void *)0

空指针在计算机中就是0地址,不容许对0地址做任何操作。
int *p =NULL;更容易使读程序的人意识到这里是一个指针赋值。

(2)野指针
野指针问题及如何避免

野指针导致内存泄露

动态分配空间。

(3)指针变量的之间的赋值
不同指针类型变量赋值导致的问题:
长指针赋值给短指针,数据丢失。(长短指的是步长);
短指针赋值给长指针,数据多取。

《void * 的概念》
鉴于指针之间这种灵活的强制类型转换的需求和出于简化代码的考虑,ANSI C引入了空指针即void*。void指针又名万能指针,在现在的很多程序中,当参数不确定时就用万能指针代替,这一类的指针在线程\进程函数里特别常见。
ANSI C规定,void指针可以复制给其他任意类型的指针,其他任意类型的指针也可以复制给void指针,他们之间复制不需要强制类型转换。当然任何地址也可以复制给void型指针。

2、指针的算术运算

int a[10] = {1,2,3,4,5,6,7,8,9,10};
int *ptr = &a[0];
int *str = &a[5];

printf("%d\n",*(ptr + 1));
printf("%d\n",str - ptr);

结果:*(ptr + 1)= 2; str- ptr = 5;

这里写图片描述

指针之间只有减法,且同时必须同时指向同一个空间。

Logo

更多推荐