提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
这篇文章我们来浅析一下C语言指针,后续随着我对指针的深入,也会去写指针进阶的文章,下面我们来开始对初阶指针做一个讲解
提示:以下是本篇文章正文内容,下面案例可供参考
指针到底是什么呢?
通俗的来讲,指针其实就是内存单元的最小编号,平时口中所说的指针,通常是指的指针变量,也就是存放内地址的变量。
总结起来:指针就是地址,口语中说的指针通常指的是指针变量。
指针有多种类型,比如int*,char*,double*,等等等等,然后我们写一个程序测试一下这些指针的长度
这样我们可以知道,所有的指针的类型字节大小都相等,那为什么我们不同意指针类型呢,下面我们来讲一下这些指针类型的意义:
?比如:char*的指针解引用只能访问一个字节,而int*的指针的解引用就能访问四个字节
通过上面两张图片即可看出,不同的类型的指针还是存在一定区别的。
int*的指针可以解引用四个字节,
char*的指针只能解引用一个字节。
所以指针类型决定指针解引用时访问多少个字节(指针的权限)
总结:type*p;
1.决定p指向的对象的类型
2.p解引用的时候访问的对象的大小是sizeof(type)。
如图,指针的类型还决定了指针+1/-1时的步长,整形指针跳过四个字节,字符指针跳过1个字节。
总结一下:指针类型决定了对指针解引用操作的权限,即解引用时能有多大的权限(能操作几个字节)。
什么是野指针呢,野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)
野指针的其中一个成因:指针未初始化
#include<stdio.h>
int main()
{
int*p;
*p=20;
reutnr 0;
}
上述这个代码就是一个野指针的案例
#include<stdio.h>
int main()
{
int arr[10]={0};
int*p=arr;
int i=0;
for(i=0;i<=11;i++){
*(p++)=i;
}
return 0;
}
我们再来看一下这串代码,我们数组的大小是10,但是一共要循环12次,所以循环十次以后,后面的范围就越界了,这时就会形成一个野指针
如图便可以看出,这便是一个指针越界访问的例子。
#include<stdio.h>
int*test()
{
int a=110;
return &a;
}
int main()
{
int*p=test();
printf("%d\n",*p);
return 0;
}
上述代码中,a的空间进入函数创建,出函数时还给操作系统了,所以再次通过p去访问时就会形成一个野指针。
1.明确知道指针应该初始化为谁的地址,就初始化为谁。
2.不知道初始化为什么值,暂时初始化为空值。
#include<stdio.h>
int main()
{
int arr[10]={0};
//不适用下标访问数组
int*p=&arr[0];
int sz=sizeof(arr)/sizeof(arr[0]);
for(int i=0;i<sz;i++){
*p=i;
p++;
}
p=arr;
for(int i=0;i<sz;i++){
printf("%d",arr[i]);
}
return 0;
}
上图中的代码就实现了运用指针实现指针+-整数,各位也可以在自己编译器上实现一编,便于更好的去理解这串代码。
? ?
#include<stdio.h>
int main()
{
int arr[10]={0};
printf("%d\n",&arr[9]-&arr[0]);
return 0;
}
再看这串代码,这串代码运行后的答案为9,那为什么为9呢,这一共是十个元素,虽然十个元素都是0,但是&arr使我们得到的不是元素值,而是元素的地址的值,所以中间一共有九个元素,所以结果为9,当然你认为结果是36也是可以理解的,因为一共有9个元素,一个元素占四个字节,所以36也是能说的过去的。
这里有个地方我们要记住,指针-指针得到的绝对值是指针之间的元素个数
指针-指针的前提条件:指针和指针指向了同一块空间。
地址是由大小的,
指针的关系运算就是比较指针的大小
#include<stdio.h>
#define N_VALUES 5
float values[N_VALUES];
float* vp;
int main() {
for (vp = &values[N_VALUES]; vp > &values[0];vp--) {
*--vp = 0;
}
return 0;
}
看这串代码,便是利用指针的关系运算来实现对数组的初始化,一开始vp=5,然后每一轮循环都会-1,然后从第五个元素开始逐步进行初始化,五次循环之后,数组全部初始化为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);
上例中,一般而言数组名array 代表数组本身,类型是int[10],但如果把array 看做指针的话,它指向数组的第0 个单元,类型是int* 所指向的类型是数组单元的类型即int。因此*array 等于0 就一点也不奇怪了。同理,array+3 是一个指向数组第3 个单元的指针,所以*(array+3)等于3。其它依此类推。
例十:
char *str[3]={
"Hello,thisisasample!",
"Hi,goodmorning.",
"Helloworld"
};
char s[80];
strcpy(s,str[0]); //也可写成strcpy(s,*str);
strcpy(s,str[1]); //也可写成strcpy(s,*(str+1));
strcpy(s,str[2]); //也可写成strcpy(s,*(str+2)););
上例中,str 是一个三单元的数组,该数组的每个单元都是一个指针,这些指针各指向一个字符串。把指针数组名str 当作一个指针的话,它指向数组的第0 号单元,它的类型是char **,它指向的类型是char *。
*str 也是一个指针,它的类型是char *,它所指向的类型是char,它指向的地址是字符串"Hello,thisisasample!"的第一个字符的地址,即'H'的地址。注意:字符串相当于是一个数组,在内存中以数组的形式储存,只不过字符串是一个数组常量,内容不可改变,且只能是右值.如果看成指针的话,他即是常量指针,也是指针常量.
str+1 也是一个指针,它指向数组的第1 号单元,它的类型是char**,它指向的类型是char*。
*(str+1)也是一个指针,它的类型是char*,它所指向的类型是char,它指向"Hi,goodmorning."的第一个字符'H'
下面总结一下数组的数组名(数组中储存的也是数组)的问题:
声明了一个数组TYPE array[n],则数组名称array 就有了两重含义:
第一,它代表整个数组,它的类型是TYPE[n];
第二,它是一个常量指针,该指针的类型是TYPE*,该指针指向的类型是TYPE,也就是数组单元的类型,该指针指向的内存区就是数组第0 号单元,该指针自己占有单独的内存区,注意它和数组第0 号单元占据的内存区是不同的。该指针的值是不能修改的,即类似array++的表达式是错误的。在不同的表达式中数组名array 可以扮演不同的角色。在表达式sizeof(array)中,数组名array 代表数组本身,故这时sizeof 函数测出的是整个数组的大小。
在表达式*array 中,array 扮演的是指针,因此这个表达式的结果就是数组第0 号单元的值。sizeof(*array)测出的是数组单元的大小。
表达式array+n(其中n=0,1,2,.....)中,array 扮演的是指针,故array+n 的结果是一个指针,它的类型是TYPE *,它指向的类型是TYPE,它指向数组第n号单元。故sizeof(array+n)测出的是指针类型的大小。在32 位程序中结果是4
例十一:
int array[10];
int (*ptr)[10];
ptr=&array;:
上例中ptr 是一个指针,它的类型是int(*)[10],他指向的类型是int[10] ,我们用整个数组的首地址来初始化它。在语句ptr=&array中,array 代表数组本身。
本节中提到了函数sizeof(),那么我来问一问,sizeof(指针名称)测出的究竟是指针自身类型的大小呢还是指针所指向的类型的大小?
答案是前者。例如:
int(*ptr)[10];
则在32 位程序中,有:
sizeof(int(*)[10])==4
sizeof(int[10])==40
sizeof(ptr)==4
实际上,sizeof(对象)测出的都是对象自身的类型的大小,而不是别的什么类型的大小。
什么是二级指针,在之前学数组的时候我们知道,二维数组就是在一个数组中再嵌套一个有长度的数组。
以二维数组来帮助理解一下二级指针,一级指针是用来存放和指针类型相同的变量的地址,而需要存放指针变量的指针就是二级指针。
二级指针的定义:
type** variate;
二级指针的赋值:
int a = 3;
int* pa = &a;
int** ppa = &pa;
?二级指针的使用:
#include <stdio.h>
int main(){
int a = 3;
int* pa = &a;
int** ppa = &pa;
printf("%p\n", *ppa);
return 0;
}
解释:?一级指针pa
中的地址是变量a
中的地址,二级指针ppa
中的地址是一级指针的地址,用一个图可以表示这个结构
如果一个数组中的所有元素保存的都是指针,那么我们就称它为指针数组。其一般形式为:
?? ??数据类型? ? *数组名[常量表达式][常量表达式]...... ;
它是一个数组,数组的元素都是指针,数组占多少个字节由数组本身的大小决定,每个元素都是一个指针。
?例如:char *arr[]={“Sunday”,“Monday”},存储了两个指针,第一个指针指向了字符串"Sunday",第二个指针指向了字符串"Monday",而sizeof(arr)=8,因为在32位平台,指针类型大小占4个字节。指针数组最重要的用途是对多个字符串进行处理操作,因为字符指针比二维数组更快更有效。
下面是一个简单的例子。
#include <stdio.h>
int main()
{
//定义三个整型数组
int a[5] = { 1,2,3,4,5 };
int b[5] = { 6,4,8,3,1 };
int c[5] = { 2,5,8,6,1 };
//定义一个存放指向整型变量的指针的数组arr
int* arr[] = { a,b,c };
//通过接引用打印出三个一维数组的元素
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 5; j++)
{
printf("%d ", *(arr[i]+j));
}
printf("\n");
}
return 0;
}