谨以此文送给初入编程坑的女票梁童鞋。
目录
基本概念
地址和指针的关系
地址:内存区对每个字节的编号。字符占1个字节,16位系统中int类型占2个字节,32位系统中int类型占4个字节。
指针:是指向另一变量在内存中位置的地址。在程序中定义一个变量,进行编译时就会给这个变量在内存中分配一个地址,通过访问这个地址即可以找到所需变量,这个变量的地址就称为该变量的指针。
指针变量: 专门用来存放另一变量地址的变量。
指针操作
指针的定义
类型声明 * 指针变量名
“ * ”表示是一个指针变量,“类型声明”表示本指针所指向的数据类型(基本类型、数组、函数、指针、对象)。
指针的类型是“类型声明 * ”(去掉指针变量名的剩余部分) ,指针指向的所指向的类型“类型声明”(去掉指针变量以及左侧的指针声明符*)。
指针赋值
指针变量同普通变量一样,使用之前要定义,而且必须赋具体的值,未经赋值的指针变量不能使用。给指针变量进行赋值只能赋予地址,而不能赋予其他任何数据。主要有以下两种赋值方式:
//定义指针变量的同时进行赋值
int a;
int *p=&a;
//先定义指针变量,然后再赋值
int a;
int *p;
p = &a; //注意p前不要加符号*
注意:定义完指针变量之后再赋值,不要加“ * ”。
指针的引用(*p)
引用指针变量是对变量进行间接访问的一种形式,其含义是指针变量所指向的变量的值。引用形式如下:
*指针变量
int *p,q=5;
p = &q; //&称为取地址运算符,&q即获取变量q的地址。
printf("%d",*p); // *p 是q的值,*p == q
指针运算
算术运算
指针只有加法减法两种算术运算,指针的运算都是相对其基本类型进行的。
int i;
int *p = &i;
p++; //地址加1,这里的1代表一个整型的长度。16位系统中int是2个字节,32位系统int是4个字节。
char j;
char *q = &j;
q++; //地址加1,这里的1代表一个字符的长度即一个字节。
编写程序时一定要注意,指针类型要与指针所指向的类型要一致。
比较运算
在关系表达式中允许指针的比较运算,本质上是地址的关系比较,是否是在同一位置、在某位置前后。
下标运算
C语言提供了指针变量的下标运算“ [ ] ”,其形式类似于一维数组元素的下标访问形式,假设指针p,p[ i ]的意思是p后第i个地址处的值,p[ i ]等价于 *(p+i)。
指针与数组
指针与一维数组
当定义一个一维数组,系统会内存中为该数组分配一个存储空间,其数组的名称就是数组在内存中的首地址。若再定义一个指针变量,将数组的首地址传给指针变量,则该指针就指向了这个一维数组。
int a[10],*p;
p = a;
这里a是数组名,也就是数组的首地址。
int a[10],*p;
p = &a[0];
a[0]是数组的首个元素,其地址也就是数组的首地址。
以上,将数组名或者将数组的首个元素的地址赋值给指针变量p的效果是完全相同的。
通过指针来引用一维数组中的元素:
*(p + n) 与 *(a + n)均可以表示数组a中的第n个元素。数组名虽然很多时候可以当作指针来操作,但不是指针,它所代表的就是数组这种数据结构。
int main()
{
int *p, a[10] = {1,2,3};
p = a;
printf("%d\n", sizeof(a)); //输出40 sizeof返回类型或者对象占用内存的字节数
printf("%d\n", sizeof(p)); //输出4
return 0;
}
注意:
1. p = &a会出现警告,间接(" * "又称间接寻址运算符)(指向)级别不同的警告
int a[10],*p;
p = &a; //会出现"int *"与"int (*)[10]"间接(指向)级别不同的警告。
指针p指向的类型是int型,不存在&a的操作(取地址的地址),int (*)[10]是二维数组的指针类型。
2. 尽量不要用数组名当作指针参数传入函数后,直接来进行加减法操作。可再定义一个指针指向该参数,再进行操作。
//假设主函数中定义
int a[3]={0,1,2}
int fun(int*p)
{
p = p + 1;
printf("%d", p[1]); //输出的结果是2。
return 0;
}
输出分析:前面下标运算中讲到,p[ i ]等价于 *(p+i),在经过p = p+1后,p[1]的值相当于(a+1)[1]处的值。
3. 使用指针对数组经过一循环操作后,在再利用该指针对该数组操作时一定要考虑指针是否要重新指向数组首地址。如,利用指针对数组进行改值或排序之后,再利用指针进行数组元素输出。
指针与二维数组
1. 二维数组的首地址
- a : 数组名,
- &a[0] :第0行的首地址
- &a[0][0]:第0行第0列的首地址
2. 二维数组的首个元素
- **a
- *(a[0] + 0) = *(*(a + 0)+ 0)
- a[0][0]
3. 二维数组第m行的地址
a[m] = &a[m] = *(a + m) = a + m
4. 二维数组第m行第n列的元素(*(a+i)等价于a[ i ])
a[m][n] = *(a[m] + n) = *(&a[m] + n) = *(*(a + m) + n) = *((a + m) + n)......利用第3条替换
5. 二维数组第m行第n列的元素的地址(a+i等价于&a[ i ])
&a[m][n] = a[m] + n = &a[m] + n = *(a + m) + n = (a+m) + n .......利用第3条替换
注意:
假设有,二维数组 a[3][5],首地址a为1000。
则a+1 = 1004 1020 (1000+4*5) a+1表示数组a第1行的地址。因此可以看出,此处+1是指在首地址加上二维数组中每一行所有元素占的字节数。
二维数组指针的定义(指向二维数组的指针)了解即可。
类型说明符 (* 指针变量名) [长度]
其中“类型说明符”为所指数组的数据类型。“ * ”表示其后的变量类型是指针类型。“长度”表示二维数组分解为多个一维数组时一维数组的长度,也就是二维数组的列数。应注意“ ( * 指针变量名 ) ”两边的括号不可少,如果缺少括号则表示是指针数组(如下),意义完全不同。
指针数组
指针数组也是数组,但它和一般的数组不同,其数组元素是指针,即内存单元地址。
数据类型 * 数组名[常量表达式];
未完待续。。。