指针是什么?
在计算机科学中,指针(Pointer)是编程语言中的一个对象,利用地址,它的值直接指向(points to)存在电脑存储器中另一个地方的值。由于通过地址能找到所需的变量单元,可以说,地址是指向该变量的单元,因此,将地址形象化的称为“指针”。意思是通过它能找到以它为地址的内存单元
int main(){
int a = 10;//在内存中开辟一块空间
int *p = &a;//在这里我们对变量a,取出它的地址,可以使用&操作符
return 0; //将a的地址存放在p变量中,p就是一个指针变量
}
总结:指针就是变量,用来存放地址的变量。(存放在指针中的值都被当成地址处理)
在32位的机器上,地址是32个0或者1组成的二进制序列,那地址就得用4个字节的空间来存储,所以一个指针变量的大小应该是4个字节
如果在64位机器上,如果有64个地址线,那一个指针变量的大小是8个字节,才能存放一个地址
指针是用来存放地址的,地址是唯一标示一块地址空间的
指针的大小在32位平台是4个字节,在64位平台是8个字节
#include<stdio.h>
//指针的解引用
int main() {
int a = 0x11223344;
int* p = &a;
*p = 0;
printf("%p\n",p);
return 0;
}
指针类型决定了指针进行解引用操作的时候,能够访问空间的大小
int *p;*p能够访问4个字节
char *p;*p能够访问1个字节
double*p;*p能够访问8个字节
指针和指针类型
当有这样的代码:
int num=10;
p= #
要将&num(num的地址)保存到p中,我们知道p就是一个指针变量,那它的类型是怎样的呢?我们给指针变量相应的类型。
char *pc=NULL;
int *pi=NULL;
short *ps=NULL;
long *pl=NULL;
float *pf=NULL;
double *pd=NULL;
这里可以看到,指针的定义方式是:type+*,其实:char *类型的指针是为了存放char类型变量的地址,short*类型的指针是为了存放short类型变量的地址。int*类型的指针是为了存放int类型变量的地址
指针类型的意义:
指针+整数
#include<stdio.h>
int main() {
int n = 10; // 定义一个整型变量 n 并初始化为 10
char* pc = (char*)&n; // 将变量 n 的地址强制转换为字符指针类型,并赋值给 pc
int* pi = &n; // 定义一个整型指针 pi,并将变量 n 的地址赋值给它
printf("%p\n",&n); // 以十六进制指针形式打印变量 n 的地址
printf("%p\n", pc); // 打印 pc 所指向的地址(即 n 的地址)
printf("%p\n", pc + 1); // 打印 pc 所指向地址向后移动 1 个字节后的地址
printf("%p\n", pi); // 打印 pi 所指向的地址(即 n 的地址)
printf("%p\n", pi + 1); // 打印 pi 所指向地址向后移动 4 个字节后的地址(因为 int 通常是 4 个字节)
return 0;
}
指针类型决定了:指针走一步走多远(指针的步长)
int*p;p+1 —>4
char*p;p+1 —>1
double*p;p+1 —>8
总结:指针的类型决定了指针向前或向后走一步有多大(距离)
野指针
概念:野指针就是指针指向的位置是不可知的(随机的,不正确的,没有明确限制的)
野指针成因
1.指针未初始化
int main(){
int *p; //局部的指针表里,就被初始化随机值
*p = 20;
return 0;
}
2.指针越界访问
#include<stdio.h>
int main() {
int arr[10] = {0};
int* p = arr;
int i = 0;
for (i = 0; i <= 11;i++) {
//当指针指向的范围超出数组arr的范围时,p就是野指针
*(p++) = i;
}
return 0;
}
如何规避野指针
1.指针初始化 2.小心指针越界 3.指针指向空间释放即置为NULL 4.指针使用之前检查有效性
指针运算
- 指针+-整数
- 指针-指针
- 指针的关系运算
指针+-整数
int main() {
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int i = 0;
int* p = arr;
int sz = sizeof(arr) / sizeof(arr[0]);
for (i = 0; i < sz; i++) {
printf("%d",*p);
*p++ ;
}
return 0;
}
指针-指针
#include<stdio.h>
int main() {
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
printf("%d\n",& arr[9] - &arr[0]);
return 0;
}
#include<stdio.h>
//求字符串长度 通过指针移动位置无需事先知道字符出阿奴禅
int my_strlen(char* str) {//接受字符指针‘str’作为参数,表示要计算长度的字符串
char* start = str; //定义初始字符指针start
char* end = str; //定义初始字符指针end
while (*end!='\0') { //end字符指针不指向结束符‘\0’时,执行循环
end++; //字符位置向前加一
}
return end - start; //start值为首元素,end为最后一个元素,相减即为
}
int main() {
char arr[] = "happy";
int len = my_strlen(arr);
printf("%d\n",len);
return 0;
}
指针的关系运算
for (vp = &values[N_VALUES]; vp > &values[0];) {
*--vp = 0;
}
指针和数组
在C语言中,指针和数组之间的关系十分密切,通过数组下标所能完成的操作都可以通过指针来完成,一般来说,用指针编写的程序比用数组下标编写的程序执行速度快。
声明
int a[10];
定义了一个长度为10的数组a,换句话说,它定义了一个由10个对象组成的集合,这10个对象存储在相邻的内存区域中,名字分别为a[0],a[1],...,a[9]
a[0] | a[1] | a[9] |
a[i]表示该数组的第i个元素。如果pa的声明为
pa =&a[0];
则可以将指针pa指向数组a的第0个元素,也就是说,pa的值为数组元素a[0]的地址
这样,赋值语句
x=*pa;
将把数组元素a[0]中的内容复制到变量x中。
如果pa指向数组中的某个特定元素,那么,根据指针运算的定义,pa+1将指向下一个元素,pa+i将指向pa所指向数组元素的第i个元素,而pa-i将指向pa所指向数组元素之前的第i个元素,因此,如果指针pa指向a[0],那么*(pa+1)引用的是数组元素a[1]的内容,pa+i是数组元素a[i]的地址,而*(pa+i)引用的是数组元素a[i]的内容
pa; | pa+1; | pa+2; |
a[0]
无论数组a中的元素的类型或数组长度是什么,上面的结论都成立。“指针加1”就意味着pa+1指向pa所指向的对象的下一个对象。相应的,pa+i指向pa所指向的对象之后的第i个元素。
字符指针与函数
字符串常量是一个字符数组,例如:
“I am a string”
在字符串的内部表示中字符数组以空字符“\0”结尾,所以,程序可以通过检查空字符找到字符数组的结尾。字符串常量占据的存储单元数也因此比双引号内的字符数大1.
字符串常量最常见的用法也许是作为函数参数,例:
printf("hello,world\n");
当类似于这样的一个字符串出现在程序中时,实际上是通过字符指针访问该字符串的。在上述语句中,printf接受的是一个指向字符数组第一个字符的指针。也就是说,字符串常量可通过一个指向其第一个元素的指针访问。
除了作为函数参数外,字符串常量还有其他用法,假定指针pmessage的声明如下
char *pmessage;
那么,语句
pmessage ="now is the time";
将把一个指向该字符数组的指针赋值给pmessage,该过程并没有进行字符串的复制,而是涉及到指针的操作。C语言没有提供将整个字符串作为一个整体进行处理的运算符
下面两个定义之间有很大区别
char amessage[]="now is the time"; //定义一个数组
char *pmessage ="now is the time"; //定义一个指针
上述声明,amessage是一个仅仅足以存放初始化字符串以及空字符串 ‘ \0 ’ 的一维数组。数组中的单个字符可以进行修改,但amessage 始终指向同一个存储位置。另一方面,pmessage是一个指针,其初值指向一个字符串常量,之后它可以被修改以指向其他地址,但如果试图修改字符串的内容,结果是没有定义的。
函数strcpy( s , t )把指针t指向的字符串复制到指针s指向的位置
//strcpy函数:将指针t指向的字符串复制到指针s指向的位置:使用数组下标实现
void strcpy(char *s,char *t)
{
int i;
i = 0;
while{(s[i]=t[i]) != '\0'}
i++;
}
//strcpy函数:将指针t指向的字符串复制到指针s指向的位置:使用指针方式实现
void strcpy(char *s,char *t)
{
while((*s = *t) !='\0'){ //判断赋值的结果是否不等于'\0'(既字符串的结束符)。如果不是结束符,就执行循环体
s++; //分别将两个指针向后移动一位,以便在下一次循环中处理下一个字符
t++;
}
}
这个函数的作用就是逐个字符地将字符串 t 的内容复制到字符串 s 中,直到遇到字符串 t 的结束符 ‘ \0 ’ 为止。
但相对于经验丰富程序员来说,更喜欢这样编写
void strcpy(char *s,char *t)
{
while ((*s++ = *t++) !='\0');
}
在该版本中,s和t的自增运算放到了循环的测试部分中,表达式*t++的值是执行自增运算之前t所指向的字符。后缀运算符++表示在读取该字符之后才改变t的值。s执行自增运算之前,字符就被存储到了指针s指向的旧位置。该字符值同时也用来和空字符’ \0 ‘ 进行比较运算,以控制循环的执行。最后的结果是依次将t指向的字符复制到s指向的位置,直到遇到结束符’ \0 ‘为止(同时也复制该结束符)
为了