前言
本篇是个人学习笔记。有大量抄录,但并非转载,故仍记为原创,仅个人学习使用,侵删,有帮助到您 并非我本意。
如果全篇有哪些看不懂了,或者难以理解,请牢记这段话
*如果你有一个好习惯,每遇到一个指针,都应该问问:这个指针的类型是什么?指针指的类型是什么?该指针指向了哪里?(重点注意)*
1、什么是指针
指针是一个特殊的变量,它指向内存里的一个地址。
最简单的例子:
int *p;
int a=10;
p=&a;
cout<<*p;
//10
我的理解是,‘ * ’号跟随在变量名前,此时这个变量名就变成了一个指针,所以p是指针,而非*p,具体符号下面。
指针四方面:指针的类型、指针所指向的类型、指针的值或者叫指针所指向的内存区、指针本身所占据的内存区。
指针的类型:
从语法的角度看,只要把指针声明语句里的指针名字去掉,剩下的部分就是这个指针的类型。
(1)int*ptr;//指针的类型是int*
(2)char*ptr;//指针的类型是char*
(3)int**ptr;//指针的类型是int**
(4)int(*ptr)[3];//指针的类型是int(*)[3] 一维数组指针
(5)int*(*ptr)[4];//指针的类型是int*(*)[4] 一维指针数组指针
指针所指向的类型:
当你通过指针来访问指针所指向的内存区时,指针所指向的类型决定了编译器将把那片内存区里的内容当做什么来看待。
从语法上看,你只须把指针声明语句中的指针名字和名字左边的指针声明符*去掉,剩下的就是指针所指向的类型。例如:
(1)int*ptr; //指针所指向的类型是int
(2)char*ptr; //指针所指向的的类型是char
(3)int**ptr; //指针所指向的的类型是int*
(4)int(*ptr)[3]; //指针所指向的的类型是int()[3] 一维数组
(5)int*(*ptr)[4]; //指针所指向的的类型是int*()[4] 一维指针数组
指针的值----或者叫指针所指向的内存区或地址:
指针的值是指针本身存储的数值,这个值将被编译器当作一个地址,而不是一个一般的数值。在32 位程序里,所有类型的指针的值都是一个32 位整数,因为32 位程序里内存地址全都是32 位长。
我们说一个指针的值是XX,就相当于说该指针指向了以XX 为首地址的一片内存区域;
*如果你有一个好习惯,每遇到一个指针,都应该问问:这个指针的类型是什么?指针指的类型是什么?该指针指向了哪里?(重点注意)*
指针本身所占据的内存区:
指针本身占了多大的内存?你只要用函数sizeof(指针的类型)测一下就知道了。在32 位平台里,指针本身占据了4 个字节的长度。
2、运算符 & 和 *
这里&是取地址运算符,*是间接运算符。
&a 的运算结果是一个指针,指针的类型是a 的类型加个*,指针所指向的类型是a 的类型,指针所指向的地址,就是a 的地址。
对于 * p 的运算结果就五花八门了。总之*p 的结果是p 所指向的东西,这个东西有这些特点:它的类型是p 指向的类型,它所占用的地址是p所指向的地址。
所以当你遇到一个指针,想要获取指针的值,只需要*一下。当你想把一个数转化为指针,只需要&找到他的地址。
int a=12; int b; int *p; int **ptr;
p=&a; //&a 的结果是一个指针,p也是指针,所以p可以接收&a
//p和&a 类型是int*,指向的类型是int,指向的地址是a 的地址。
*p=24; //*p 的结果,也就是对指针p解引用(也是&a),显然,*p 就是变量a。
ptr=&p; //&p 的结果是个指针,该指针的类型是p 的类型加个*,在这里是int **。
//该指针所指向的类型是p的类型,这里是int*。该指针&p所指向的地址就是指针p 自己的地址。
*ptr=&b; //*ptr 是个指针&p,&b 的结果也是个指针,且这两个指针的类型和所指向的类型是一样的,所以用&b 来给*ptr赋值就是毫无问题的了。
**ptr=34; //*ptr的结果是ptr 所指向的东西,在这里是一个指针,对这个指针再做一次*运算,结果是一个int 类型的变量。
cout<<a<<endl;
cout<<b<<endl;
//24
//34
有了这个符号,我们就能做强制类型转换
unsigned int a;
int *ptr;
a=20345686;
ptr=20345686; //我们的目的是要使指针ptr 指向地址20345686
ptr=a; //我们的目的是要使指针ptr 指向地址20345686
//编译一下吧。结果发现后面两条语句全是错的。那么我们的目的就不能达到了吗?不,还有办法:
unsigned int a;
int *ptr; //TYPE 是int,char 或结构类型等等类型。
a=N; //N 必须代表一个合法的地址;
ptr=(int*)a; //这就可以了。
3、指针的算术运算
指针可以加上或减去一个整数。例如:
char a[20];
int *ptr=(int *)a; //强制类型转换并不会改变a 的类型
ptr++;
指针ptr 的类型是int*,它指向的类型是int,它被初始化为指向整型变量a。
第3句中,指针ptr被加了1,编译器是这样处理的:它把指针ptr 的值加上了sizeof(int),在32 位程序中,是被加上了4
由于char 类型的长度是一个字节,所以,原来ptr 是指向数组a 的第0 号单元开始的四个字节,此时指向了数组a 中的a[4]。
再看例子:
char a[20]="You_are_a_girl";
int *ptr=(int *)a;
ptr+=5;
在这个例子中,ptr 被加上了5,编译器是这样处理的:将指针ptr 的值加上5 乘sizeof(int),在32 位程序中就是加上了5 乘4=20。由于地址的单位是字节,故现在的ptr 所指向的地址比起加5 后的ptr 所指向的地址来说,向高地址方向移动了20 个字节。
ptr 已经指向了数组a 的合法范围之外了。虽然这种情况在应用上会出问题,但在语法上却是可以的。这也体现出了指针的灵活性。
4、函数和指针
指针做参数时,只要注意上下相对应就行,
比如
int func(int *x){}//此时参数是指针,
//所以传参数时,要传入指针,
int *p;
func(p);
int a;
func(&a);
void f(int **x){
cout<<**x;
}
int main(){
int *p,**ptr,a=10;
p=&a;
ptr=&p;
f(ptr);
return 0;
}
指针函数:
指针函数,简单的来说,就是一个返回指针的函数,其本质是一个函数,而该函数的返回值是一个指针。
int* fun(int x,int y);
//指针函数
int* f(int a,int b){
int * data = new Data;
data= a;
return data;
}
//调用指针函数
int * myData = f(4,5);
函数指针
函数指针,其本质是一个指针变量,该指针指向这个函数。总结来说,函数指针就是指向函数的指针。
int (*fun)(int x,int y);
int add(int x,int y){
return x+y;
}
int sub(int x,int y){
return x-y;
}
//函数指针
int (*fun)(int x,int y);
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
//第一种写法
fun = add;
qDebug() << "(*fun)(1,2) = " << (*fun)(1,2) ;
//第二种写法
fun = ⊂
qDebug() << "(*fun)(5,3) = " << (*fun)(5,3) << fun(5,3);
return a.exec();
}
第三种:
int *(*fun)(int x,int y);
返回值是指针的函数指针
5、指针数组和数组指针
实际上,一维数组的名字就是指针。
void f(int *x){
cout<<*x;
}
int *p,*ptr;
int b[7]={0,1,2,3,4,5,6};
int main(){
p=b;
f(p+3);
return 0;
}
int array[10]={0,1,2,3,4,5,6,7,8,9},value;
value=array[0]; //也可写成:value=*array;
value=array[3]; //也可写成:value=*(array+3);
value=array[4]; //也可写成:value=*(array+4);
*char pt[10] ; pt是一个数组,数组由10个char * 指针组成 ------指针数组
#include <stdio.h>
int main() {
int a = 1, b = 2, c = 3;
int *arr[] = {&a, &b, &c}; // 指针数组
for (int i = 0; i < 3; ++i) {
printf("%d ", *(arr[i]));
}
return 0;
}
*char (pt)[10] ; pt是一个指针,指向了10个元素构成的数组,切数组类型为char -----数组指针
#include <stdio.h>
int main() {
int arr[] = {1, 2, 3, 4, 5};
int (*ptr)[5] = &arr; // 数组指针
for (int i = 0; i < 5; ++i) {
printf("%d ", (*ptr)[i]);
}
return 0;
}
例:
char * (*c[10])(int **p)
在C语言中,char * (*c[10])(int **p)
是一个声明,它描述了一个复杂的指针和函数指针的组合。
int **p
:这是一个指向指向整型的指针的指针。它通常用于指向一个整数数组的指针,或者是一个指向整数指针数组的指针。char * (*c[10])
:这是一个数组声明,数组名为c
,包含10个元素。每个元素c[i]
是一个函数指针,这些函数接受一个int **p
类型的参数,并返回一个char *
类型的结果。char *
:这是函数返回类型,表示函数返回一个指向char
类型的指针。
综合起来,char * (*c[10])(int **p)
表示 c
是一个包含10个函数指针的数组,每个函数指针指向的函数都接受一个指向指向整数的指针 int **p
作为参数,并返回一个指向字符的指针 char *
。
这种声明通常用于创建函数指针数组,这些函数可以执行相似的操作,但是可能在内部实现上有所不同。例如,你可能有10个不同的函数,每个函数都接受一个整数数组的指针,并返回一个指向字符串的指针,这些函数可以存储在 c
数组中。
二维数组指针是什么
二维数组指针是指向二维数组的指针。它通常用于处理多维数组的情况,允许对二维数组进行更灵活的操作。
二维数组指针的声明类似 int (*ptr)[N]
,其中 ptr 是指向包含N列的整数数组的指针。
#include <stdio.h>
int main() {
int arr[2][3] = {{1, 2, 3}, {4, 6, 7}};
int (*ptr)[3] = arr; // 指向二维数组的指针
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 3; j++) {
printf("%d ", ptr[i][j]);
}
printf("\n");
}
printf("%d ", (*(*(ptr+1)+1)));
return 0;
}
1 2 3
4 6 7
6
//这里你会发现,二维数组指针和数组指针写法一样,因为二维数组本质上就是一维数组。
char s[80];
char *str[3]={
"Hello,thisisasample!",
"Hi,goodmorning.",
"Helloworld"
};
strcpy(s,str[0]); //也可写成strcpy(s,*str);
strcpy(s,str[1]); //也可写成strcpy(s,*(str+1));
strcpy(s,str[2]); //也可写成strcpy(s,*(str+2));
但是如果,你使用,int **p=arr;
能用,但不建议。
6、指针和结构类型的关系
可以声明一个指向结构类型对象的指针。
例十二:
struct MyStruct
{
int a;
int b;
int c;
};
struct MyStruct ss={20,30,40};
//声明了结构对象ss,并把ss 的成员初始化为20,30 和40。
struct MyStruct *ptr=&ss;
//声明了一个指向结构对象ss 的指针。它的类型是
//MyStruct *,它指向的类型是MyStruct。
请问怎样通过指针ptr 来访问ss 的三个成员变量?
答案:
ptr->a; //指向运算符,或者可以这们(*ptr).a,建议使用前者
ptr->b;
ptr->c;
7、字符串常量
char *pt = “aaa”;
此时“aaa”是字符串常量,被放在了只读区,不能被修改。
8、const char* ptr , char const *ptr, char * const
一、const char *ptr:指针常量
定义一个 指向字符常量 的指针,这里,ptr是一个指向 char* 类型的常量,所以不能用ptr来修改所指向的内容,换句话说,*ptr的值为const,不能修改。但是ptr的声明并不意味着它指向的值实际上就是一个常量,而只是意味着对ptr而言,这个值是常量。实验如下:ptr指向str,而str不是const,可以直接通过str变量来修改str的值,但是确不能通过ptr指针来修改。
二、char const *ptr:
此种写法和const char *等价
三、char * const ptr:常量指针
定义一个指向字符的指针常数,即const指针,实验得知,不能修改ptr指针,但是可以修改该指针指向的内容。