目录
前言
对于所有的C语言学习者来说,指针是一道绕不过去的坎,相信看完这篇文章你就会搞懂什么是指针以及指针的用法。
一、指针简介
以下节选自维基百科对指针的定义:在计算机科学中,指针(英语:Pointer),是编程语言中的一类数据类型及其对象或变量,用来表示或存储一个存储器地址,这个地址的值直接指向(points to)存在该地址的对象的值。
我们需要明确以下两点
1. 指针是内存中一个最小单元的编号,也就是地址
2. 平时口语中说的指针,通常指的是指针变量,是用来存放内存地址的变量
我们可以通过 & (取地址操作符)取出变量的内存其实地址,把地址可以存放到一个变量中,这个
变量就是指针变量
#include<stdio.h>
int main()
{
int a = 3;//创建变量a
int* pa = &a;//通过pa将a的地址存起来
return 0;
}
二、指针和指针的类型
1.指针类型
C语言基本指针包括
int *
char *
float *等等
指针本质上是储存变量的地址,在32位平台上每一种指针的大小都是4个字节,64位平台占8个字节。
这时有人会问,既然每种指针的大小都相同,那么区分指针的类型不就没有什么意义了吗
非也,以下面的代码为例说明我们打开调试窗口选择内存
#include <stdio.h>
int main()
{
int n = 0x11223344;
char *pc = (char *)&n;
int *pi = &n;
*pc = 0;
*pi = 0;
return 0;
}
我们打开调试窗口选择内存,&n 会出现这样的界面
我们发现n的数据是倒着储存的,我们按F10继续调试让代码执行到*pc=0这句,我们想当然的认为n的值会被修改为0但实际并不然
我们发现只有把n的一部分修改了,修改的部分恰好占1个字节。我们再往下调试
发现整个变量被修改了,修改的部分恰好是4个字节 .
我们可以总结“指针解引用时指针变量的类型决定了指针的访问权限”。
注意!!!
指针也跟其它变量一样可以相减,计算出的是两个地址之间的元素个数。指针也可以比较大小,但是规定:指针可以与最后一个元素后面的内存比较,不允许与第一个元素之前的内存比较。
数组名是数组首元素的地址。我们要区分 数组名与&数组名 之间的区别
#include<stdio.h>
int main()
{
int arr[10] = { 0 };
printf("%p\n", arr);
printf("%p\n", &arr);
printf("%p\n", &arr[0]);
return 0;
}
我们可以猜测一下,这三条语句打印的内容是否相同
我们发现它们打印出来的内容 是相同的,但不能说明它们表达的含义是相同的
#include<stdio.h>
int main()
{
int arr[10] = { 0 };
printf("%p\n", arr);
printf("%p\n", arr+1);
printf("\n");
printf("%p\n", &arr);
printf("%p\n", &arr+1);
printf("\n");
printf("%p\n", &arr[0]);
printf("%p\n", &arr[0]+1);
return 0;
}
给上面示例中的每个地址都加上了1,我们发现它们加一以后的地址是不尽相同的,数组名+1是指向的是数组第二个元素的地址。&arr+1加上的是整个数组的大小,而&arr[0]+1与arr+1同理。
(1)一级指针
一级指针指向的是变量的地址。
(2)二级指针
既然有一级指针也就会有二级指针,二级指针是指向一级指针地址的指针。
同理也会有三级指针,四级指针等。此后不再赘述。
2.字符指针
在指针的类型中我们知道有一种指针类型为字符指针 char* ;
一般使用 :
int main()
{
char ch = 'w';
char *pc = &ch;
*pc = 'w';
return 0; }
还有另外一种使用方法:
int main()
{
const char* pstr = "hello bit.";
printf("%s\n", pstr);
return 0; }
char *p="abcdef";
p是存放首元素a的地址,后面是常量字符串(不能修改)
3.指针数组
int* arr1 [ 10 ]; // 整形指针的数组
char * arr2 [ 4 ]; // 一级字符指针的数组
char ** arr3 [ 5 ]; // 二级字符指针的数组
指针数组顾名思义,就是存放指针的数组。数组中的每个元素都是指针,
这里我们明确一个概念:
在数组中,去掉数组名剩下的部分是类型名。
我们看上面的三个例子:第一个数组类型为 int * [10] 第二个为char * [4] 第三个为char ** [5]
4.数组指针
数组指针是指向数组的指针
例:
int (*p)=&arr;
我们从上面的例子可以知道&arr取出的是整个数组的地址,平常的指针只能存放一个元素的地址,这是我们就会需要数组指针来存放。
这时我们可以想到,二维数组传参时,传递的是第一行元素的地址,因此可以通过数组指针来存放。
#include<stdio.h>
void print(int(*p)[10], int c, int r)
{
for (int i = 0; i < c; i++)
{
for (int j = 0; j < r; j++)
{
//printf("%d ", p[i][j]);
//printf("%d ",*(p[i]+j));
//printf("%d ",*((p+i)+j));
}
}
}
int main()
{
int arr[10][10] = { 1,2,3,4,5,6,7,87,5,4,2,2 };
print(arr, 10, 10);
return 0;
}
这三种写法都是等效的·。
5.函数指针(指向函数的指针)
#include <stdio.h>
void test()
{
printf("hehe\n");
}
int main()
{
printf("%p\n", test);
printf("%p\n", &test);
return 0; }
这两个printf()函数打印出来的内容是一样的
我们看一个例子:
#include<stdio.h>
int add(int x, int y)
{
return x + y;
}
int main()
{
int arr[10];
int(*p)[10] = &arr;
printf("%p\n", &add);
printf("%p\n", add);
return 0;
}
也就是说add与&add的内容是一样的。经过前面的例子我们有可能会打出一个问号?它们表示的内容是否一样。
结论为它们就是一样的。
int (*p)(int,int)=add;
这是函数指针的格式
我们就可以通过函数指针来调用函数
下面是解引用的过程
int ret=(*p)(2,3);
其中这个'*'是毫无意义的,写不写无所谓,写多少都可以。
我们看看下面两个例子(被称为逆天代码)
//代码1 (*(void (*)())0)();
//代码2
void (*signal(int , void(*)(int)))(int);
我们先看第一段代码
//void (*)() 是函数指针类型
//( void (*) () ) 把类型放在()中是强制类型转换
//( *( void (*)() )0 是将0强制类型转换为函数指针类型
这段代码来源于C陷阱与缺陷中 是将0地址处的值修改为0
第二段代码
//signal 是一个函数的声明
//signal函数的参数,第一个是int型,第二个是void(*)(int)的函数指针类型
//signal函数的返回值类型也是:void(*)(int)的函数指针
6.函数指针数组
#include<stdio.h>
int Add(int x, int y)
{
return x + y;
}
int Sub(int x, int y)
{
return x - y;
}
int Mul(int x, int y)
{
return x * y;
}
int Div(int x, int y)
{
return x / y;
}
int main()
{
char* arr[5];
int* arr2[4];
int (*pf1)(int, int) = Add;
int (*pf2)(int, int) = Sub;
int (*pf3)(int, int) = Mul;
int (*pf4)(int, int) = Div;
//函数指针数组
int (*pf[4])(int, int) = { Add,Sub,Mul,Div };
return 0;
}
总结
以上就是今天的内容了。