一、指针和数组
1、数组的指针
(1)一个变量有一个地址,一个数组则包含若干的元素,而每个数组元素都在内存中占用了内存单元,它们都有相应的地址,所谓数组的指针是指数组的起始地址。
(2)数组名表示数组的首地址,因此数组名也是一种指针
(3)通过数组名访问数组中的元素
int ch[] = {1,2,3,4};
//访问ch中的第3个元素:
ch[3] == 4;
//也可通过指针法引用数组中的元素:
*(ch + 3);
//如果想访问第n个元素
*(ch + n)
//注意:n<=sizeof(ch)/sizeof(ch[0])-1
/*
1、假如有数组 int a[4]
2、通过键盘上输入数字对数组a的每一个元素进行赋值
3、打印处数组a中每一个元素的地址
4、通过指针法将数组a中的每一个元素的值打印出来
*/
#include <stdio.h>
int main(){
int a[4];
int i;
for(i=0;i<4;i++){
printf("输入整数");
scanf("%d",&a[i]);
}
for(i=0;i<4;i++){
printf("结果");
printf("%p,%d\n",&a[i],*(a+i));
}
return 0;
}
(4)通过指针变量间接访问数组
#include <stdio.h>
int main()
{
int a[4] = {1,2,3,4};
char ch[] = {'a','b','c'};
int *p;
char *p2;
p = a;
p2 = ch;
*(p+2) = 100; //通过指针间接访问了数组a中下标为2的元素,并修改成100
*(p2+2) = 'A';
printf("%d\n",a[2]);
printf("%c\n",ch[2]);
return 0;
}
#include <stdio.h>
int main()
{
int ch[] = {1, 2, 3, 4};
printf("%d\n", ch[4]);
printf("%p %p\n", &ch[3], &ch[4]);
//数组名:数组的首地址
printf("ch: %p\n", ch);
//数组中的第0个元素的地址:数组的首地址
printf("&ch[0]: %p\n", &ch[0]);
printf("%d %d\n", ch[3], *(ch+3));
int a[4];
int i;
for (i = 0; i < 4; i++)
{
scanf("%d", &a[i]); //a+i
getchar();
}
//打印数组中每个元素的地址
for (i = 0; i < 4; i++){
printf("%p\n", &a[i]);
}
//通过指针法将数组a中的每一个元素的值打印出来
for (i = 0; i < 4; i++){
printf("%d\n", *(a+i));
}
int *p;
p = a; //指针p指向了数组a
//指针指向了一个数组,可以将指针当数组看待
for (i = 0; i < 4; i++){
printf("%d\n", p[i]); //通过下标发访问数组中的元素
}
// printf("%d\n", *(p+i));
return 0;
}
(5)数组指针越界
#include <stdio.h>
int main()
{
int b[4] = {10,20,30,40};
int a[4];
a[4] = 100;
printf("a[4]:%d\n",a[4]);
printf("b[0]:%d\n",b[0]);
printf("a:%u,b:%u\n",&a,&b); //打印数组a和b的首地址
return 0;
}
2、指针数组
(1)指针数组就是:存放指针的数组,本质是数组,数组中的每个元素都是指针
#include <stdio.h>
int main()
{
int a = 10,b = 20,c = 30;
int *p[3];
p[0] = &a;
p[1] = &b;
p[2] = &c;
return 0;
}
(2)注意:int *p[3];等价于 (int*)p[3];因为[]比*的优先级要高先与p匹配
(3)思考:如何通过一个数组存储10个人的姓名?
char *name[10] = {"zhangsan", "lisi", "wangwu", "zhaoliu, "tianqi"};
name 数组中保存了10个字符串常量的首地址
注意:没有保存字符串常量而是常量的首地址
3、指针变量的地址
(1)在定义指针变量的时候,编译器会分配一块空间来存储这个指针变量的值,分配的这块内存空间肯定有一个地址编码,那么这个地址编码肯定就是这个指针变量的地址。
#include <stdio.h>
int main()
{
int a = 10;
int *p;
p = &a;
//打印指针变量p的值以及变量a的地址(结果是相同的)
printf("p:%p,a:%p\n",p,&a);
//打印指针变量p的地址(存储指针变量的内存空间的首地址)
printf("&p:%p\n",&p);
return 0;
}
(2)注意:指针变量p的值保存的是另外一个变量a的地址,指针变量的地址是存储p这个指针变量的值的那块内存空间的首地址,这块空间保存的值是a的地址。
4、一级指针作为函数的形参
(1)函数的形参为数组
如果函数的形参是数组,该形参的定义方法为:
void func(int a[],int n){}
也可以将形参定义为指针类型:
void func(int *a,int n){}
通常我们使用第二种方法
(2)调用函数时需要传递字符串可将形参设计为char*类型
#include <stdio.h>
void Print(char *p) //调用函数时将字符串的地址赋值给指针变量p
{
printf("%c\n",p[0]);
}
int main()
{
Print("hello world!");
return 0;
}
(4)注意:如果函数的形参是指针,在函数体中一般先对指针的值进行判断,判断指针的值是否为NULL。
二、二级指针
(1)一个指针变量保存一个一级指针变量的地址,这种指针我们叫做二级指针。
(2)二级指针的定义
数据类型 **变量名;
int a = 10;
int *p = &a; //p为一级指针,保存变量a的地址
int **pp = &p; //pp为二级指针 保存一级指针p的地址
(3)二级指针的应用
//二级指针的应用
#include <stdio.h>
int main()
{
int a =10;
int *p = &a;
int **p2 = &p; //二级指针p2保存了一级指针p的地址(p2指向了p)
int ***p3 = &p2; //三级指针
//*p2 == p == &a
printf("%p %p %p\n",*p2,p,&a);
//**p2 == *p == *(&a) == a
printf("%d %d %d %d\n",**p2,*p,*(&a),a);
**p2 = 100;
printf("%d %d %d %d\n",**p2,*p,*(&a),a);
//*p3 == p2 = &p
//**p3 == *p2 == p == &a
//***p3 == **p2 == *p == *(&a) == a
printf("***p3:%d\n",***p3);
return 0;
}