C语言-关于指针概念的详细介绍

指针是什么?

在计算机科学中,指针(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(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 ‘为止(同时也复制该结束符)

为了

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值