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)