概述
目录
一、指针
1.1概念
1.2指针的相关操作
1.3指针和变量的关系
1.4指针的基本使用
1.5指针变量的大小
1.6指针的运算
二、指针与字符串
三、指针与一维数组
四、指针与二维数组
4.1指针和二维数组
4.2数组指针
4.3指针数组
五、二级指针
六、指针与函数
6.1指针函数
6.2函数指针
6.3函数指针数组
6.4函数指针数组指针
七、总结
一、指针
1.1概念
内存中每个字节都有自己的地址,地址有一个独一无二的编号,这个编号就叫做指针(也就是地址)。专门用来存放指针的变量叫做指针变量。
一般称呼:
地址:地址编号
指针:指针变量
1.2指针的相关操作
& :取地址符,获取变量的地址。
对多字节的变量取地址得到的是首地址,也就是(地址)编号最小的哪个。
* :在定义指针变量时,* 只起到一个标识作用,表示定义的是一个指针变量。
在其他场景下,表示操作指针指向空间的内容。
1.3指针和变量的关系
指针变量中保存的是某个变量的地址。
eg:
int a = 10; //变量a中保存的是常量10
int *p = &a; //指针变量p中保存的是变量a的首地址
1.4指针的基本使用
变量的类型决定了这个变量内存的大小。
int a;
a = 10; //通过变量名 操作其内存空间
printf("%p",&a); //通过取地址符 & 可以得到变量的地址
//通过 %p 来打印地址
格式:
数据类型 *指针变量名;
同样可以通过指针变量名来给指针赋值。
eg:
int a = 10;
int *p; //也可以写为 int *p = &a;
p = &a ; //指针保存了变量a的地址,我们称为指针p指向变量a
注意:
1.普通的变量保存的地址没有意义。
2.指针只能保存已经分配好的地址,不能手动指定。
eg:
int a;
int *p = a; //正确
*p = 10; //错误
3.指针的类型决定了指针能操作的内存大小。
eg:char * 类型的指针只能操作 1 个字节
int * 类型的指针只能操作 4 个字节
4. 未初始化的指针里面保存的是随机值,是有害的,被称之为“野指针”。
可以将指针指向 NULL 来解决野指针问题。
5.可以同时定义多个指针 。
eg:
int *p1, p2; //错误写法,其中p1是指针 p2是int类型的变量
int *p3, *p4; //p3,p4都是指针
6. 常量没有指针。
1.5指针变量的大小
32位系统中 指针的大小都是4字节
64位系统中 指针的大小都是8字节
1.6指针的运算
指针运算的本质是操作指针中保存的地址。只有相同类型的指针之间做运算才有意义,因为他们操作的内存大小相同。
指针能做的运算有:
算数运算: + - ++ --
关系运算:> < == !=
赋值运算:=
注意:
1.只有指针指向的是连续的空间时,指针之间的运算才有意义。
例如,指针指向的是一个一维数组 。
2.指针变量名加上一个整数n,表示加上n个指针类型的大小。
3.指针的强转是安全的 因为在一个系统中指针的大小是相同的。
4.指针可以被重新赋值。
二、指针与字符串
字符串常量储存在字符串常量区
字符串常量区在虚拟内存的 ro段(readonly)这是只读数据段。
所以字符串常量区的内容不可以改变 。
栈区的内容可以修改,所以我们可以用指针改变栈区保存的字符串。
也可以使用指针直接指向字符串常量,此时的字符串常量实在字符串常量区,不可以改变。
eg:
char s1[32] = "hello world"; //表示用字符串常量区的hello world来初始化栈区的数组s1
*s1 = 'H'; //正确的
//修改的是栈区的数组s1,而不是字符串常量区的hello world
char *p = "hello world" //表示通过指针p直接指向字符串常量区的字符串hello world
*p = 'H'; //错误的
//字符串常量区的内容不可以被修改
三、指针与一维数组
int s[5] = {10,20,30,40,50};
其中一维数组的数组名(s)就是数组的首地址,s是一个常量
-->这样我们就可以得到是 s[i] <==>*(s+i)
int *p = s;
指向一维数组的指针保存的是一维数组的首地址,p是一个变量
-->这样我们就可以得到 s[i] <==>*(s+i) <==> p[i] <==> *(p+i)
通过指针和一维数组的结合,我们可以实现一维数组的遍历????
int s[5] = {1,2,3,4,5};
int i = 0;
for(i=0; i<5; i++){
printf("%d ",*(p+i));
}
printf("n");
四、指针与二维数组
4.1指针和二维数组
int s[3][4] = {{1,2,3,4},
{5,6,7,8},
{9,10,11,12}};
其中二维数组的数组名(s)就是数组的首地址,s是一个常量。
二维数组名操作的空间是一整行元素,也称之为行指针。
对二维数组的数组名取 * 操作 表示“降维”操作
这样我们可以得到-->
s[i] <==> *(s+i)
s[i][j] <==> *(s[i]+j) <==> *(*(s+i)+j)
通过指针和二维数组的结合,我们可以实现一维数组的遍历????
int s[3][4] = {{1,2,3,4},
{5,6,7,8},
{9,10,11,12}};
int i = 0;
int j = 0;
for(i=0; i<3; i++){
for(j=0; j<4; j++){
printf("%d ",*(*(s+i)+j));
}
printf("n");
}
注意:
二维数组的数组名是一个行指针,所以操作的是一行元素 已经超过基本类型的范围了。
所以不能用普通的指针来指向二维数组,因为普通的指针不能取 ** 操作 。
4.2数组指针
本质是一个指针,用来指向二维数组的。也叫做行指针。
多用于二维数组作为函数的参数传递时。
格式:
数据类型 (*数组指针名)[列宽];
eg:
int s[3][4] = {{1,2,3,4},
{5,6,7,8},
{9,10,11,12}}; //定义了一个 3行4列的二维数组
int (*p)[4] = s; //定义了个数组指针 并让这个指针指向二维数组s
指向二维数组的指针保存的是二维数组的首地址,p是一个变量。
这样我们就可以得到-->
s[i][j] <==> *(s[i]+j) <==> *(*(s+i)+j) <==>
p[i][j] <==>*(p[i]+j) <==> *(*(p+i)+j)
4.3指针数组
本质是一个数组,数组中每一个元素都是一个指针。
格式:
数据类型 *指针数组名[下标];
eg:
char *s[5]; //定义了一个数组名为s的数组 数组中一共有5个元素
//每一个元素都是一个 char * 类型的指针
//对 s[i] 操作和操作 单个的 int * 指针是一样的
s[5] = {NULL}; //指针数组的初始化(指向NULL)
s[5] = {"hello","beijin","nihao"}; //不完全初始化
五、二级指针
二级指针是用来保存一级指针的地址的。
多用于一级指针的地址作为函数的参数传递时。
变量、一级指针、二级指针的关系????
int a = 10; //变量
int *p = &a; //一级指针
int **q = &p; //二级指针
**q = 1234; //相当于给 a 赋值
这样我们就可以得到-->
a <==> *p <==> **q
&a <==> p <==> *q
&p <==> q
注意:
一级指针保存一级指针的地址没有意义
因为一级指针不能取 ** 操作
六、指针与函数
6.1指针函数
本质是一个函数,返回值是一个指针类型。
格式:
返回值类型 *函数指针名(函数形参表);
注意:
1.不能返回局部变量的地址。(局部变量的地址在函数结束时会被系统回收)
2.可以返回全局变量的地址。
3.可以返回static关键字修饰的局部变量的地址。
4.可以返回传递给函数的参数的地址。(函数形参表中传入的地址)
eg:
int *my_func(int x,int y){
int sum = x + y;
return ∑ //错误的,sum是局部变量 在函数结束时会被回收
}
int *my_func(int x, int y,int *sum){
*sum = x + y;
return sum; //正确的
}
6.2函数指针
本质是一个指针,可以指向一个函数。
格式:
返回值类型 (*函数指针名)(函数的形参表);
eg:
#include <stdio.h>
int my_add(int x, int y){
return x+y;
}
int main(int argc, const char *argv[])
{
int a = 10;
int b = 20;
printf("%dn", my_add(a, b));//30
int (*p)(int, int) = NULL;
//定义了一个函数指针 指针名叫p 可以指向一个 返回值为int
//形参列表为(int, int)类型的函数
p = my_add; //让函数指针p指向函数my_add
//函数名就是函数的首地址
//指针指向函数之后 就可以通过指针来调用函数了
printf("%dn", p(a, b));//30
return 0;
}
函数指针的经典使用场景--->回调函数
eg:
#include <stdio.h>
int my_add(int x, int y){ //函数my_add
return x+y;
}
int my_sub(int x, int y){ //函数mu_sub
return x-y;
}
int jisuan(int x, int y, int (*p)(int, int)){ //第三个参数就是一个函数指针
return p(x, y); //调用函数指针p指向的函数(回调)
//通过传递不同的函数实现不同的功能
}
int main(int argc, const char *argv[])
{
int a = 10;
int b = 20;
printf("a+b = %dn", jisuan(a, b, my_add));//调用函数my_add
printf("a+b = %dn", jisuan(a, b, my_sub));//调用函数my_sub
return 0;
}
6.3函数指针数组
本质是一个数组,数组中每一个元素都是一个函数指针。
格式:
返回值类型 (*函数指针名[下标])(函数的形参表)
eg:
#include <stdio.h>
int my_add(int x, int y){
return x+y;
}
int my_sub(int x, int y){
return x-y;
}
int main(int argc, const char *argv[])
{
int (*s[2])(int, int) = {NULL}; //定义了一个函数指针数组,数组名叫s 数组中共有2个元素
//每个元素都是一个可以指向返回值为int
//形参列表为 (int, int) 的函数指针
s[0] = my_add; //将每个元素指向函数
s[1] = my_sub;
//当函数指针数组的元素指向函数之后 就可以通过他调用函数了
int a = 10;
int b = 20;
printf("a+b = %dn", s[0](a, b));//通过函数指针数组中的元素调用函数
printf("a-b = %dn", s[1](a, b));
return 0;
}
6.4函数指针数组指针
本质是一个指针,指向一个函数指针数组。
格式:
返回值类型 (*(*函数指针数组指针名))(函数的形参表);
eg:
#include <stdio.h>
int my_add(int x, int y){
return x+y;
}
int my_sub(int x, int y){
return x-y;
}
int main(int argc, const char *argv[])
{
int (*s[2])(int, int) = {my_add, my_sub};//定义了一个函数指针数组,
//数组名叫s 数组中共有2个元素
//每个元素都是一个可以指向返回值为int
//形参列表为 (int, int) 的函数指针
//s[0]指向函数my_add;s[1]指向函数my_sub
int a = 10;
int b = 20;
printf("a+b = %dn", s[0](a, b));//30
printf("a-b = %dn", s[1](a, b));//-10
int (*(*p))(int, int) = NULL; //定义了一个 函数指针数组指针
p = s; //让函 数指针数组指针 p 保存 函数指针数组 的首地址
printf("a+b = %dn", p[0](a, b));//通过指针就可以访问函数指针数组的元素了
printf("a-b = %dn", (*(p+1))(a, b));
return 0;
}
七、总结
int p = 0; //变量
int p[5] = {NULL}; //数组
int *p = NULL; //一级指针
int **P = NULL; //二级指针
int (*p)[5] = NULL; //数组指针
int *p[5] = {NULL}; //指针数组
int *p(); //指针函数
int (*p)() = NULL; //函数指针
int (*p[5])() = {NULL}; //函数指针数组
int (*(*p))() = NULL; //函数指针数组指针
最后
以上就是深情毛巾为你收集整理的C语言指针总结一、指针二、指针与字符串三、指针与一维数组四、指针与二维数组五、二级指针六、指针与函数 七、总结的全部内容,希望文章能够帮你解决C语言指针总结一、指针二、指针与字符串三、指针与一维数组四、指针与二维数组五、二级指针六、指针与函数 七、总结所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复