目录
指针概述
概念:在C语言中,指针的实质就是地址,专门用来存放地址的变量,称为指针变量。
此外,还需要注意的是,指针的存储类型是指针变量本身的存储类型。
指针声明时,指定的数据类型不是指针变量本身的数据类型,而是指针目标的数据类型。简称为指针的数据类型。
指针变量的形式一般是:
//指针在声明的同时,也可以被赋予初值,称为指针的初始化
// 一般形式是:<存储类型> <数据类型> *<指针变量名> = <地址量> ;
int x, *px=&x;
//在上面语句中,把变量x的地址作为初值赋予了刚说明的int型指针px。
int x = 3; //int x; x = 3;
int *px = &x; //int * px; px = &x;
这里,指针px指向变量x,指针px中存放的是变量x的空间地址。
引入指针要注意程序中的px、*px 和 &px 三种表示方法的不同意义。设px为一个指针,则:
px — 指针变量, 它的内容是地址量
*px — 指针所指向的对象, 它的内容是数据
&px — 指针变量占用的存储区域的地址,是个常量
指针运算
在c语言中,对指针进行运算操作,要先明白几个关键点:
- 指针运算是以指针变量所存放的地址量作为运算量而进行的运算
- 指针运算的实质就是地址的计算
- 指针运算的种类是有限的,它只能进行赋值运算、算术运算和关系运算
指针的算术运算见下表:
注意:
- 不同数据类型的两个指针实行加减整数运算是无意义的
- px+n表示的实际位置的地址量是:
(px) + sizeof(px的类型) * n
- px-n表示的实际位置的地址量是:
(px) - sizeof(px的类型) * n
- 两指针相减运算
px-py 运算的结果是两指针指向的地址位置之间相隔数据的个数,是一个整数值,表示两 指针之间相隔数据的个数,而不是px-py的持有的地址值相减。
1 #include <stdio.h>
2
3 int main(int argc, char *argv[])
4 {
5 int a[5] = {4, 8, 1, 2, 7};
6 int *p, * q;
7
8 p = a; //&a[0];
9 q = &a[3];
10
11 printf("%d %d\n", *p, *q);
12 printf("%d\n", q-p); //a[0]到a[3]有三个元素
13
14 return 0;
15 }
16
结果:
4 2
3
指针与数组
在C语言中,指向数组的指针所指向的空间地址是数组在内存中的起始地址,一维数组的数组名为一维数组的指针(起始地址)
//例如:
double x[8];
double * px = x;
x为x数组的起始地址
设指针变量px的地址值等于数组指针x(即指针变量px指向数组的首元数),则:
x[i] 、*(px+i)、*(x+i) 和px[i]具有完全相同的功能:访问数组第i+1个数组元素
1 #include <stdio.h>
2
3 int main(int argc, char *argv[])
4 {
5 int x[5] = {4, 8, 1, 2, 7};
6 int *px;
7
8 px = x; //&x[0];
9
10 //依次打印x[i] 、*(px+i)、*(x+i) 和px[i]
11 printf("%d- %d- %d - %d\n", x[3], *(px+3), *(x+3), px[3]);
12 return 0;
13 }
14 结果:2- 2- 2- 2
~
~
注意:
- 指针变量和数组在访问数组中元素时,一定条件下其使用方法具有相同的形式,因为指针变量和数组名都是地址量
- 但指针变量和数组的指针(或叫数组名)在本质上不同,指针变量是地址变量,而数组的指针是地址常量:例如指针px做为地址变量,可以做自增自减操作px++、px--,但是x作为地址常量,则不能做自增自减操作,编译器会报错。
指针与二维数组
多维数组就是具有两个或两个以上下标的数组
在C语言中,二维数组的元素连续存储,按行优先存
我们先声明一个二维数组:
int a[3][2]={{1,2},{3,4},{5,6}}
二维数组a中包含了三个元素:a[0]、a[1]、a[2].其中每个元素又是一个含有两个元素的一维数组。例如:一维数组a[0],含有两个元素a[0][0]、a[0][1].
先明白二维数组中几个关键点:
在二维数组中:
int a[3][2] = {{1, 2}, {3, 4}, {5, 6}};
int (*p)[2]; //这里定义一个行指针,实际上就是一个指针数组,每个数组元素都是一个指针
p = a; //将行指针p指向二维数组a
a; //代表数组首行地址,一般用a[0][0]的地址表示
&a; //代表整个数组的地址,一般用a[0][0]地址表示
*p; //代表数组a首元素地址也就是a[0]或者&a[0][0]
*(p+i); //代表了第i行首元素的地址,*a是i=0的情况
*(p+i)+j; //代表了第i行j个元素的地址
**a; //代表a的首元素的值也就是a[0][0]
*(*(a+i)+j); //代表了第i行第j个元素
二维数组demo
2 #include <stdio.h>
3
4 int main(int argc, char *argv[])
5 {
6 int a[3][2] = {{1, 2}, {3, 4}, {5, 6}};
7
8 int (*p)[2]; //这里定义一个行指针
9 int i, j;
10
11 p = a; //将行指针p指向二维数组a
12
13 //分别对a、p做算术加运算,打印a+1、p+1、a[1][0]、p[1][0],地址量一致
14 printf("%p %p %p\n", a, a+1,&a[1][0]);
15 printf("%p %p %p\n", p, p+1,&p[1][0]);
16
17
18 printf("依次遍历: a[i][j], p[i][j], *(*(a + i)+j), *(*(p + i) + j)\n");
19 for (i = 0; i < 3; i++) {
20 for (j = 0; j < 2; j++)
21
22 printf("%d-- %d-- %d-- %d ", a[i][j], p[i][j], *(*(a + i)+j), *(*(p + i) + j));
23 puts("");
24 }
25
26
27 return 0;
28 }
29
打印结果:
0xbfb3f1d8 0xbfb3f1e0 0xbfb3f1e0
0xbfb3f1d8 0xbfb3f1e0 0xbfb3f1e0
依次遍历: a[i][j], p[i][j], *(*(a + i)+j), *(*(p + i) + j)
1-- 1-- 1-- 1 2-- 2-- 2-- 2
3-- 3-- 3-- 3 4-- 4-- 4-- 4
5-- 5-- 5-- 5 6-- 6-- 6-- 6
~
字符指针与字符串
初始化字符指针是把内存中字符串的首地址赋予指针,并不是把该字符串复制到指针中
1 //字符指针
2 #include<stdio.h>
3 int main()
4 {
5 char str[] = "Hello World";
6 char *p = str;
7 printf("%s\n",p);
8 return 0;
9 }
结果:Hello World
需要注意的是
在C编程中,当一个字符指针指向一个字符串常量时,不能修改指针指向的对象的值:
char * p = “Hello World”;
*p = ‘h’; // 错误, 字符串常量不能修改
//字符串常量
#include<stdio.h>
#include<ctype.h>
int main()
{
//这里声明两个指向字符串常量的字符指针p1、p2
char* p1 = "hello world";
char* p2 = "hello world";
printf("&p1= %p p1指向的地址: %p p1指向的值: %s\n",&p1,p1,p1);
printf("&p2= %p p2指向的地址: %p p2指向的值: %s\n",&p2,p2,p2);
return 0;
}
结果:
&p1= 0xbfed0438 p1指向的地址: 0x8048520 p1指向的值: hello world
&p2= 0xbfed043c p2指向的地址: 0x8048520 p2指向的值: hello world
不能修改的原因在于,不同于我们在程序中,声明一个字符串数组(例如:char* str[]),计算机会将声明的变量的存储空间放在了栈里面,进行动态管理;
不同于字符串数组,字符串常量,就像全局变量、static存储类型的声明一样,数据是存储在一个固定的静态存储区中的,静态存储区里面的数据一般是不能改变的,而且会在程序的执行结束后, 才会释放静态存储区里面的数据。例子中,两个字符指针p1、p2都是指向同一个地址。