1,问题的引入 int a; a = 100;//把数值100存放到变量a对应的存储单元中去 b = a;//把a的数值存放到变量b对应的存储单元中去 ==> 在C语言中,任何一个变量名,都有两层含义 (1)代表该变量的存储单元,左值 lvalue (2)代表该变量的值 右值 rvalue 而且,我们对变量的访问有两种情况 (1)写操作,把一个值写入变量对应的存储单元中 (2)读操作,读出变量对应存储单元中存放的值 我们知道系统已经把变量名与变量的地址相关联,系统实质上通过地址 来访问变量对应的存储空间。那么是不是我们也可以直接通过地址来访问 变量呢? ==> 指针 2,指针的概念 地址:系统把内存以一个字节为单位划分成许多份进行编号,这个编号就是内存单元的地址。 在C语言中,指针的概念与地址差不多,你可以指针就是有类型的地址。 一个变量的首地址,称为该变量的"指针"。它标志着该变量的内容从哪里开始。 3,指针变量 ★指针变量也是一个变量,它是用来保存一个对象的地址的。 指针变量的定义: 类型 *指针变量名; "类型":指针变量指向的类型 "指向":如果我保存了你的地址,那么就说我指向你。 eg: int *p; int a; p = &a; p是一个指针变量名,它指向的类型是int p的类型是: int* 注意:在32位的处理器里,地址都是32位的,即指针变量分配的空间都是4个字节。 所以把指针变量的类型强制转化为其它指针类型也不会失真。 因此 void* 也叫通用指针。 ★指针变量的类型只决定指针变量的变化(与整形加减)与实际地址变化间的倍率 eg: int a; char b; int *p1=&a; char *p2=&b; printf("%p\n",p1);//%p是打印地址的格式控制符 printf("%p\n",++p1); printf("%p\n",p2); printf("%p\n",++p2); 4,如何获取地址 (1)通过取地址符 & &对象名: 表示取该对象的地址 对象: 变量,数组元素。。。 (2)有些对象的名字本身就表示地址 如:数组名,函数名.... 注:这两种方式获取到的地址都有类型的。 5,如何访问指针指向的空间。 *(指向运算符) *(地址) <==> 地址对应的那个变量 即*(地址) 可作左值也可作右值,还可以取地址。 *(&a) <==> a; *& 可直接约掉 注意: 与定义时的*区分 6,数组与指针 数组元素与普通变量,也有自己的地址。 并且数组元素间地址是连续的。(数组名为首元素的地址,是个常量) eg: int a[10]; int *p; p = &a[0];
那么把100赋值给a[0],有几种方法: a[0] = 100; *p = 100; *a = 100;//数组名为首元素的地址,是个常量 p[0] = 100; 那么 *(a+1) ==> a[1] ★得到公式 *(p+i) <=> p[i],when i>=0 7,字符串与指针 在C语言中,并没有内置字符串的类型,C语言的字符串是通过char*指针 来实现的。 char *str = "ABCDEF"; str:保存字符串的首地址,即'A'的地址 str+1:指向字符'B'的地址 *(str+1) = '2';//error(段错误),因为这里没有定义字符数组,没有分配空间 //常量保存在rodata段内,不能被改变 eg: char *str1 = "ABCDEF";//字符串保存在rodata段内 char str2[]= "ABCDEF";//字符串保存在数组里 *(str1+1) = '2';//error(段错误),指向的是只读空间,不能进行写操作 *(str2+1) = '2';//right,指向的是数组空间,可以进行写操作 str1++;//right,str1是指针变量,其值可以改变 str2++;//error(段错误),str2是数组名,其值不能改变 注意: (1)NULL在C语言中表示不指向任何空间,若指向了NULL并进行了读或写操作, 就会出现段错误,NULL的值是0 (2)出现段错误的两种情况 a.指向了NULL,并进行了读或写操作 b.对只读空间进行了写操作 8,二维数组与指针 int a[3][4]; 二维数组a可以看成元素为int[4]的一维数组。 所以,*(a+i)<=>a[i]指向的是该一维数组的第i个元素,该元素为int[4]类型, 因此,这里需要再指向一下才能指向二维数组的元素。 即*((*(a+i))+j) <=> a[i][j],这时元素的类型为int型。 表达式(i>=0,j>=0) 类型 含义 值 a+i int[4]*/int[3][4] 指向第i行的首地址/整个二维数组 &a[i] 做指针运算/sizeof(a),typeof(a) *(a+i)+j<=>a[i]+j int*/int[4] 第i行第j列的元素的地址/i行的一维数组 &a[i][j] 做指针运算/sizeof(a[i]),typeof(a[i]) *(*(a+i)+j)<=>a[i][j] int 第i行第j列的元素 a[i][j]
9,数组指针与指针数组 int a[10]; 如果我们需要定义一个指针变量p,来保存a的地址,该如何定义? typeof(a) *p; => int[10] *p; => int(*p)[10]; p就是一个指针变量,指向一个数组(int[10]),那么我们把p叫做数组指针。 因为*的结合性较低,所以这里要把*p括起来,表示定义的是指针变量。 int *p[10]; 这里的p是一个数组,里面有10个元素,每个元素都是int*类型, 那么我们把p叫做指针数组。 eg: int(*p1)[10]; int *p2[10]; printf("%d %d\n",sizeof(p1),sizeof(p2)); ★二维数组名的类型就是数组指针 10,main的参数 在unix/liunx下面,main函数的原型,应该是如下的 int main(int argc,char *argv[]) { } 在操作系统调用你的可执行程序时(调用你的main函数),允许带参数, 只不过参数都是字符串类型(char *) argc:argument count,表示允许你的程序时,参数的个数(程序名是第一个参数) argv:argument vector 参数字符串指针的数组 eg: ./sum 1 2 命令的本质就是程序,只不过它存放在命令搜索路径里
11,二级指针与多级指针 其实main函数原型也可写为如下模式 int main(int argc,char **argv) {} 可见指针数组就是二级指针,二级指针就是能指向两次的指针。 一般有以下两种理解方式 (1)指针变量的地址 eg: int a; int *p = &a; int **p2 = &p; printf("%d %d %d\n",a,*p,**p2); (2)指针数组 如果一个数组的元素是指针,则该数组名就是二级指针 同理,如果一个数组的元素是二级指针,则该数组名就是三级指针 ... eg: int a=1,b=2,c=3; int *p[3]={&a,&b,&c}; *(p+1) ==> p[1] ==> &b **(p+1) ==> b 在C语言中,char *可以表示字符串,其实指针也可以表示一个已定义好的数组。 它们区别是:字符串有终止符'\0'来确定长度,而数组必须要我们自己给定长度。 int array_max(int *a,int n) { } 12,函数指针 函数指针变量就是用来保存函数地址的变量 函数指针变量的定义格式: 指向的函数返回值类型 (*函数指针变量名)(指向的函数形参列表) 通过函数指针调用指向的函数: (*函数指针名)(实参列表); 或 (函数指针名)(实参列表);
☆函数指针数组 void (*a[3])(int,int);//定义了一个函数指针数组a,该数组有3个元素,每个元素都是 //函数指针类型,指向的函数返回类型是void,参数类型是int,int。 13,指针作为函数参数 由于形参不能改变实参,被调函数想传数据给主调函数一般用return返回。 除此之外还有两种方法 (1)访问全局变量 (2)带指针作为函数参数 14,动态内存分配(malloc.c)
