四、指针的分类
按指针指向的数据的类型来分
字符型数据的地址
char *p;//定义了一个字符指针变量,只能存放字符型数据的地址编号
char ch;
p= &ch;
short int *p;//定义了一个短整型的指针变量p,只能存放短整型变量的地址
short int a;
p =&a;
int *p;//定义了一个整型的指针变量p,只能存放整型变量的地址
int a;
p =&a;
注:多字节变量,占多个存储单元,每个存储单元都有地址编号, c语言规定,存储单元编号最小的那个编号,是多字节变量的地址编号。
long int *p;//定义了一个长整型的指针变量p,只能存放长整型变量的地址
long int a;
p =&a;
float *p;//定义了一个float型的指针变量p,只能存放float型变量的地址
float a;
p =&a;
double *p;//定义了一个double型的指针变量p,只能存放double型变量的地址
double a;
p =&a;
7:函数指针
8、结构体指针
9、指针的指针
10、数组指针
总结:无论什么类型的指针变量,在32位系统下,都是4个字节,只能存放对应类型的变量的地址编号。
指针可以存放变量的地址编号
在程序中,引用变量的方法
int a;
a=100;
int *p;//在定义的时候,*不是取值的意思,而是修饰的意思,修饰p是个指针变量
p=&a;//取a的地址给p赋值,p保存了a的地址,也可以说p指向了a
*p= 100;//在调用的时候*是取值的意思,*指针变量 等价于指针指向的变量
注:指针变量在定义的时候可以初始化
int a;
int *p=&a;//用a的地址,给p赋值,因为p是指针变量
指针就是用来存放变量的地址的。
*+指针变量 就相当于指针指向的变量
指针变量只能保存开辟好空间的地址,不能随意保存地址
1 #include
2
3 int main(int argc, char *argv[])
4 {
5 int *p1,*p2,temp,a,b;
6 p1=&a;7 p2=&b;
8
9 printf("请输入:a b的值:\n");
10 scanf("%d %d", p1, p2);//给p1和p2指向的变量赋值
11
12 temp = *p1; //用p1指向的变量(a)给temp赋值
13 *p1 = *p2; //用p2指向的变量(b)给p1指向的变量(a)赋值
14 *p2 = temp;//temp给p2指向的变量(b)赋值
15
16 printf("a=%d b=%d\n",a,b);
17 printf("*p1=%d *p2=%d\n",*p1,*p2);
18
19 return 0;
20 }
扩展:
对应类型的指针,只能保存对应类型数据的地址,
如果想让不同类型的指针相互赋值的时候,需要强制类型转换
1 #include
2
3 int main(int argc, char *argv[])
4 {
5 int a=0x1234,b=0x5678;
6 char *p1,*p2;
7 printf("%#x %#x\n",a,b);
8 p1=(char *)&a;
9 p2=(char *)&b;
10 printf("%#x %#x\n",*p1,*p2);
11 p1++;
12 p2++;
13 printf("%#x %#x\n",*p1,*p2);
14
15 return 0;
16 }
注意:
1:*+指针 取值,取几个字节,由指针类型决定的指针为字符指针则取一个字节,
指针为整型指针则取4个字节,指针为double型指针则取8个字节。
2:指针++ 指向下个对应类型的数据
字符指针++ ,指向下个字符数据,指针存放的地址编号加1
整型指针++,指向下个整型数据,指针存放的地址编号加4
变量存放在内存中,有地址编号,咱们定义的数组,是多个相同类型的变量的集合,每个变量都占内存空间,都有地址编号。指针变量当然可以存放数组元素的地址。
1 int a[10];
2 //int *p =&a[0];
3 int *p;
4 p=&a[0]; //指针变量p保存了数组a中第0个元素的地址,即a[0]的地址
方法1: 数组名[下标]
int a[10];
a[2]=100;
方法2:指针名加下标
int a[10];
int *p;
p=a;
p[2]=100;//因为p和a等价
补充:c语言规定:数组的名字就是数组的首地址,即第0个元素的地址,是个常量。
注意:p和a的不同,p是指针变量,而a是个常量。所以可以用等号给p赋值,但不能给a赋值。
例如:int a[10]; a++就是错误的,因为a是数组名是一个地址常量
方法3:通过指针运算加取值的方法来引用数组的元素
int a[10];
int *p;
p=a;
*(p+2)=100;//也是可以的,相当于a[2]=100
解释:p是第0个元素的地址,p+2是 a[2]这个元素的地址。
对第二个元素的地址取值,即a[2]
1 #include
2
3 int main(int argc, char *argv[])
4 {
5 int a[5]={0,1,2,3,4};
6 int *p;
7 p=a;
8
9 //只要将数组名赋值给同类型的指针变量,则此时的指针变量与数组名可
10 //以用相同的方法操作数组
11 printf("a[2]=%d\n",a[2]);
12 printf("p[2]=%d\n",p[2]);
13
14 //*(a + n) <==> *(p + n) <==> a[n] <==> p[n]
15 printf("*(p+2) = %d\n",*(p+2));
16 printf("*(a+2) = %d\n",*(a+2));
17
18 printf("p=%p\n",p);
19 printf("p+2=%p\n",p+2);
20 printf("&a[0] = %p\n", &a[0]);
21 printf("&a[2] = %p\n", &a[2]);
22 return 0;
23 }
往下指几个它指向的变量,结果还是个地址
前提:指针指向数组的时候,加一个整数才有意义
1 //指针可以加一个整数,往下指几个它指向的变量,结果还是个地址
2 void test1()
3 {
4 int a[10];
5 int *p, *q;
6 //p和q间隔8个字节,意味着加一个整数最终移动的字节数与指针变量的类型也有关系
7 p = a;
8 q = p + 2;
9
10 printf("p = %p\n", p);
11 printf("q = %p\n", q);
12
13 return ;
14 }
前提:只有两个相同类型的指针指向同一个数组的元素的时候,比较大小才有意义
指向前面元素的指针 小于 指向后面 元素的指针
1 void test2()
2 {
3 int a[10];
4 int *p,*q;
5 p=&a[1];
6 q=&a[6];
7 if(p<q)
8 {
9 printf("p < q\n");
10 }
11 else if(p>q)
12 {
13 printf("p > q\n");
14 }
15 else
16 {
17 printf("p = q\n");
18 }
19 }
前提:必须是两个相同类型的指针指向同一个数组的元素的时候,做减法才有意义
做减法的结果是,两个指针指向的中间有多少个元素
1 void test3()
2 {
3 int a[10];
4 int *p,*q;
5 p=&a[0];
6 q=&a[3];
7 printf("%d\n",q‐p);
8 }
注意:只有相同类型的指针才可以相互赋值(void *类型的除外)
1 void test4()
2 {
3 int a = 100;
4 int *p, *q;
5 p = &a;
6
7 printf("a = %d %d\n", a, *p);
8
9 q = p;
10 printf("*q = %d\n", *q);
11
12 *q = 999;
13 printf("a = %d\n", a);
14 }
1:指针可以保存数组元素的地址
2:可以定义一个数组,数组中有若干个相同类型指针变量,这个数组被称为指针数组
指针数组的概念:
指针数组本身是个数组,是个指针数组,是若干个相同类型的指针变量构成的集合
注意:一般遇到这样的叠词,本质就是后者
类型说明符 * 数组名 [元素个数];
1 int * p[10];//定义了一个整型的指针数组p,有10个元素p[0]~p[9],每个元素都是int
*类型的变量
2 int a;
3 p[1]=&a;
4 int b[10];
5 p[2]=&b[3];
6 p[2]、*(p+2)是等价的,都是指针数组中的第2个元素。
字符指针数组char *p[10]、短整型指针数组、整型的指针数组、长整型的指针数组
float型的指针数组、double型的指针数组
结构体指针数组、函数指针数组
1 #include
2
3 int main(int argc, char *argv[])
4 {
5 //大多数情况下,指针数组都用来保存多个字符串
6 char *name[5] = {"Follw me","BASIC","Greatwall","FORTRAN","Computer"};
7 int i;
8 for(i=0;i<5;i++)
9 {
10 printf("%s\n",name[i]);
11 }
12
13 return 0;
14 }
15
指针的指针,即指针的地址,咱们定义一个指针变量本身指针变量占4个字节,指针变量也有地址编号。
int a;
int *p;
p=&a;
*p === a
int **q;
q=&p;
*q === p
**q === *p === a
int ***m;
m=&q;
*(*(*m)) === a
注意:
p q m都是指针变量,都占4个字节,都存放地址编号,只不过类型不一样而已
1 #include
2
3 int main(int argc, char *argv[])
4 {
5 int a = 100;
6
7 //定义一个一级指针
8 //一级指针用于保存普通变量的地址
9 int *p = &a;
10
11 //定义一个二级指针12 //二级指针用于保存一级指针的地址
13 int **q = &p;
14
15 printf("a = %d %d %d\n", a, *p, **q);
16 printf("&a = %p %p %p\n", &a, p, *q);
17 printf("&p = %p %p\n", &p, q);
18 printf("&q = %p\n", &q);
19
20 return 0;
字符串的概念:
字符串就是以’\0’结尾的若干的字符的集合
字符串的存储形式: 数组、字符串指针、堆
1、 char string[100] = “I love C!”
定义了一个字符数组string,用来存放多个字符,并且用”I love C!”给string数组初始化字符串“I love C!”存放在string中
2、 char *str = “I love C!”
定义了一个指针变量str,只能存放字符地址编号,
所以说I love C! 这个字符串中的字符不能存放在str指针变量中。
str只是存放了字符I的地址编号,“I love C!”存放在文字常量区
3、 char *str =(char*)malloc(10*sizeof(char));
动态申请了10个字节的存储空间,首地址给str赋值。
strcpy(str,"I love C");//将字符串“Ilove C!”拷贝到str指向的内存里
总结:
字符数组:
在内存(栈、静态全局区)中开辟了一段空间存放字符串
字符串指针:
在文字常量区开辟了一段空间存放字符串,将字符串的首地址付给str
堆:
使用malloc函数在堆区申请空间,将字符串拷贝到堆区
注意:
可修改性:
1. 栈和全局区内存中的内容是可修改的
char str[100]=”I love C!”;
str[0]=‘y’;//正确可以修改的
2. 文字常量区里的内容是不可修改的
char *str=”I love C!”;
*str =’y’;//错误,I存放在文字常量区,不可修改
3. 堆区的内容是可以修改的
char *str =(char*)malloc(10*sizeof(char));
strcpy(str,"I love C");
*str=’y’;//正确,可以,因为堆区内容是可修改的
注意:str指针指向的内存能不能被修改,要看str指向哪里。
str指向文字常量区的时候,内存里的内容不可修改
str指向栈、堆、静态全局区的时候,内存的内容是可以修改
初始化:
字符数组、指针指向的字符串:定义时直接初始化
char buf_aver[]="hello world";
char *buf_point="hello world";
堆中存放的字符串不能初始化、只能使用strcpy、scanf赋值
char *buf_heap;
buf_heap=(char *)malloc(15);
strcpy(buf_heap,"hello world");
scanf(“%s”,buf_heap);
使用时赋值
字符数组:使用scanf或者strcpy
char buf_aver[128];
buf_aver="hello kitty"; 错误,因为字符数组的名字是个常量
strcpy(buf_aver,"hello kitty"); 正确
scanf("%s",buf_aver); 正确
指向字符串的指针:
char *buf_point;
buf_point="hello kitty"; 正确,buf_point指向另一个字符串
strcpy(buf_point,"hello kitty"); 错误,只读,能不能复制字符串到buf_piont指向
的内存里
取决于buf_point指向哪里。