3.指针(1):指针基础知识

前言:本篇博客将系统梳理与指针相关的基础概念,希望对读者的理解有所帮助~
一、内存和地址
1.内存
(1)每个内存单元大小:1个字节
(2)每个内存单元都有一个编号。 内存单元的编号<=>地址<=>指针内存单元图示
(3)内存单元单位补充:
bit-比特位
byte-字节
KB
MB
GB
TB
PB
2.编址的理解方式
(1)地址总线:32位机器有32根地址总线,每根线有两态(0/1),可以表示个不同的地址
编址的理解
二、指针变量和地址
1.取地址操作符(&)

#include<stdio.h>
int main()
{
	int a=10;
	&a;
	printf("%p\n",&a);
	return 0;
}	

在这里插入图片描述

会打印处理:0x007FFD38,取出a所占4个字节中最小的地址
2.指针变量和解引用操作符(*)
(1)指针变量:用来存放地址
(2)拆解指针类型

int a = 10; 
int * pa = &a; //*说明pa是指针变量,int说明pa指向的对象是int类型

在这里插入图片描述

(3)解引用操作符(间接访问操作符)*

int main() 
{
	int a = 100; 
	int* pa = &a; 
	*pa = 0; //*pa就是通过pa里面存的地址,找到a并将其修改
	return 0; 
}

3.指针变量的大小:地址线(平台)决定指针变量的大小,相同平台下大小都相同

#include <stdio.h> 
//指针变量的⼤⼩取决于地址的⼤⼩ 
//32位平台下地址是32个bit位(即4个字节) 
//64位平台下地址是64个bit位(即8个字节) 
int main() 
{                                                          
	printf("%zd\n", sizeof(char *)); 
	printf("%zd\n", sizeof(short *)); 
	printf("%zd\n", sizeof(int *)); 
	printf("%zd\n", sizeof(double *)); 
	return 0; 
} 

在这里插入图片描述在这里插入图片描述

三、指针变量类型的意义
1.指针的解引用:指针类型决定对指针解引用的时候有多大的权限(一次能操作几个字节)
(1)char*:一次访问一个字节
(2)int*:一次访问四个字节
2.指针±整数:指针的类型决定了指针向前或者向后走一步有多大(步长)

#include <stdio.h> 
int main() 
{ 
	int n = 10; 
	char *pc = (char*)&n; 
	int *pi = &n; 
	printf("%p\n", &n); 
	printf("%p\n", pc); 
	printf("%p\n", pc+1); 
	printf("%p\n", pi); 
	printf("%p\n", pi+1); 
	return 0; 
}

输出结果

3.void指针:无具体类型的指针(泛型指针)
(1)可以接受任意类型的地址
(2)不能直接进行指针的±整数和解引用的运算(不知道访问几个字节)
例子:使用void
来接收地址

#include <stdio.h> 
int main() 
{ 
	int a = 10; 
	void* pa = &a; 
	void* pc = &a; 
	*pa = 10; 
	*pc = 0; 
	return 0; 
} 

输出结果

(3)void*的作用:一般用在函数参数的部分,接受不同类型的地址以实现泛型编程。

四、const修饰指针
1.const修饰变量

#include <stdio.h> 
int main() 
{ 
	int m = 0; 
	m = 20;//m是可以修改的 
	const int n = 0; //n具有常属性,不可以被修改,但本质上还是变量
	n = 20;//n是不能被修改的 
	return 0; 
} 

但是:通过指针可以修改n
2.const修饰指针变量
(1)int const* p:限制*p(p指向的内容不能被修改)
(2)int const p:限制p(p本身不能被修改)
(3)int const
const p: 两个都限制
五、指针运算
1.指针±整数

#include <stdio.h> 
//指针+- 整数 
int main() 
{ 
	int arr[10] = {1,2,3,4,5,6,7,8,9,10}; 
	int *p = &arr[0]; 
	int i = 0; 
	int sz = sizeof(arr)/sizeof(arr[0]); 
	for(i=0; i<sz; i++) 
	{ 
		printf("%d ", *(p+i));//p+i 这⾥就是指针+整数 ,打印1 2 3 4 5 6 7 8 9 10
	} 
	return 0; 
} 

2.指针-指针
(1)前提:两个指针指向同一块内存空间
(2)|指针-指针|(绝对值)=两个指针之间元素的个数

#include <stdio.h> 
int my_strlen(char *s) 
{ 
	char *p = s; 
	while(*p != '\0' ) 
	p++; 
	return p-s; 
} 
int main() 
{ 
	printf("%d\n", my_strlen("abc")); //利用指针-指针可求字符串长度
	return 0; 
}

3.指针的关系运算(地址和地址比大小)

#include <stdio.h> 
int main() 
{ 
	int arr[10] = {1,2,3,4,5,6,7,8,9,10}; 
	int *p = &arr[0]; 
	int i = 0; 
	int sz = sizeof(arr)/sizeof(arr[0]); 
	while(p<arr+sz) //指针的⼤⼩⽐较 
	{ 
		printf("%d ", *p); //将数组中的十个元素按顺序打印到屏幕上
		p++; 
	} 
	return 0; 
} 

六、野指针
1.野指针成因
(1)指针未初始化
(2)指针越界访问(通常是超出数组的范围)
(3)指针指向的空间释放**(空间还给操作系统)**

#include <stdio.h>                          	
int* test()                                  	
{ 									           
	int n = 100; 								
	return &n; 									
} 											   	
int main() 
{
	int*p = test(); 
	printf("%d\n", *p); 
	return 0; 
} 

2.如何规避野指针
(1)指针初始化:不明确在指针指向哪里,可以赋值NULL。0也是地址,只不过无法使用,使用时会报错
(2)小心指针越界(C语言本身不会检查数组的越界行为)
(3)指针变量不再使用时,及时置NULL,指针使用前检查有效性

int main() 
{ 
	int arr[10] = {1,2,3,4,5,67,7,8,9,10}; 
	int *p = &arr[0]; 
	for(i=0; i<10; i++) 
	{ 
		*(p++) = i; 
	} 
	//此时p已经越界了,可以把p置为NULL 
	p = NULL; 
	//下次使⽤的时候,判断p不为NULL的时候再使⽤ 
	//... 
	p = &arr[0];//重新让p获得地址 
	if(p != NULL) //判断 
	{ 
		//... 
	} 
	return 0; 
}

(4)避免返回局部变量的地址【1.(3)】

七、assert断言
1.assert.h头文件定义了宏assert(),用于在运行时确保程序符合指定条件,如果不符合就报错。
2.assert()宏接受一个表达式作为参数。
(1)表达式为真则继续运行
(2)表达式为假则报错,显示没有通过的表达式及文件名,行号

(3)使用assert()的好处
①自动识别文件和出问题的行号
②无需更改代码就可以开启/关闭assert:如果不需要断言,在#include<assert.h>前面加上:#define NDEBUG
八、指针的使用和传址调用
1.strlen的模拟实现

int my_strlen(const char * str) (即char const*str,不准改arr的内容)
{ 
	int count = 0; 
	assert(str); 
	while(*str) 
	{ 
		count++; 
		str++; 
	} 
	return count; 
} 
int main() 
{ 
	int len = my_strlen("abcdef"); 
	printf("%d\n", len); 
	return 0; 
} 

2.传值调用和传址调用
(1)传值调用:形参是实参的一份临时拷贝,对形参的修改不影响实参
(2)传址调用:在函数内部可以修改主调函数的变量

x和y的交换不影响a和b,因此Swap1失败
在这里插入图片描述
改进代码如下:

#include <stdio.h> 
void Swap2(int*px, int*py) 
{ 
	int tmp = 0; 
	tmp = *px; 
	*px = *py; 
	*py = tmp; 
} 
int main() 
{ 
	int a = 0; 
	int b = 0; 
	scanf("%d %d", &a, &b); 
	printf("交换前:a=%d b=%d\n", a, b); 
	Swap1(&a, &b); 
	printf("交换后:a=%d b=%d\n", a, b); 
	return 0; 
} 

输出结果

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值