当前位置: 首页 > news >正文

【c语言】初阶指针

目录

1.指针是什么

2.指针和指针类型

2.1 指针的解引用

2.2 指针+-整数

3.野指针

3.1 野指针成因

3.2 如何规避野指针

4.指针的运算 

4.1 指针+-整数

4.2 指针-指针

4.3 指针的关系运算

5.指针和数组 

6.二级指针

7.指针数组


1.指针是什么

1. 指针是内存中一个最小单元(一个字节)的编号,也就是地址,每个内存单元有唯一的地址标识
2. 平时口语中说的指针,通常指的是指针变量,是用来存放内存地址的变量
内存单元的编号=地址=指针
int main() {
	int a = 10;//向内存中的栈区申请4个字节的空间,用这4个字节的空间存放10这个数值
	int* pa = &a;//将a的4个字节的第一个字节的地址存放在p变量中,p就是一个指针变量
	return 0;
}

 注意:存放在指针中的值都被当成地址处理

地址的产生:对于32位的机器,假设有32根地址线,那么假设每根地址线在寻址的时候产生高电平(高电压)和低电平(低电压)就是1或者0;那么32根地址线产生的地址就会是:2^32个地址。
每个地址标识一个字节,那我们就可以给
(2^32Byte == 2^32/1024KB == 2^32/1024/1024MB==2^32/1024/1024/1024GB == 4GB) 4G的空间进行编址则64位的机器可以给8GB的空间编址。
32位(X86)的机器上,地址是32个0或者1组成二进制序列,那地址就得用4个字节的空间来存储,所以一个指针变量的大小就应该是4个字节。
那如果在64位(X64)机器上,如果有64个地址线,那一个指针变量的大小是8个字节,才能存放一个地址。
指针的大小在 32 位平台是 4 个字节,在 64 位平台是 8 个字节

 指针变量的定义:int *p,*q(可以同时定义多个)或者int* p

2.指针和指针类型

2.1 指针的解引用

指针的类型决定了,对指针解引用的时候有多大的权限(能操作几个字节)。
int main()
{
	int n = 0x11223344;//两个16进制为一个字节
	char* pc = (char*)&n;//强制类型转换
	int* pi = &n;
	*pc = 0; 
	*pi = 0; 
	return 0;
}

char* 的指针解引用就只能访问一个字节,而 int* 的指针的解引用就能访问四个字节。 

八位二进制 / 两位16进制是一个字节

2.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);//pc向后跨一个字节
 printf("%p\n", pi);
 printf("%p\n", pi+1);//pc向后跨四个字节
 return  0; }

3.野指针

野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)
int main() {
	int* p = (int*)0x11223344;//将整型转换为int*类型
	*p;//解引用——野指针(编造的地址)
	return 0;
}

3.1 野指针成因

1.指针变量未初始化

2.指针越界访问

*p = -1;p++;

可以写成:*p++ = 1;等价于*(p++) = 1;

//++的优先级高于间接访问操作符,但p++的结果是p,解引用得到原指针指向的变量

3.指针指向的空间释放 

#include <stdio.h>
//空间释放
int* test() {
	int a = 0;
	return &a;
}
int main() {
	int* p = test();
	//空间回收;p变为野指针
	printf("%d\n", *p);
	return 0;
}

3.2 如何规避野指针

1. 指针初始化
  • 如果明确指针应该指向哪里,就初始化正确的地址;int a = 10;int* p = &a;
  • 如果指针不知道初始化什么值,为了安全,初始化NULL(空指针——0(0作为地址,用户程序是不能访问的))
2. 小心指针越界
3. 指针指向空间释放,及时置NULL
4. 避免返回局部变量的地址
5. 指针使用之前检查有效性
注意:只要遵循 指针只要不是空指针就一定指向一个明确的有效的地址 这一点就能规避野指针

4.指针的运算 

4.1 指针+-整数

使用指针打印数组内容 

#include <stdio.h>
int main() {
	int arr[] = { 0,1,2,3,4,5,6,7,8,9 };
	int* p = arr;
	for (int i = 0; i < 10; i++) {//数组在内存中连续存放
		printf("%d", *(p + i));
		//p指向的是数组首元素
		//p+i是数组下标为i的元素的地址
		//p+i跳过了i*sizeof(int)个字节

		//arr==p
		//arr+i==p+i
		//*(arr+i)==*(p+i)==arr[i]
		//*(i+arr)==*(i+p)==i[arr]——加法交换律3+5
		//上述变换说明了数组访问的本质(首元素地址+偏移量)以及操作符的本质
		// 也可以写成:
		//printf("%d", *(arr + i));
		//printf("%d", arr[i]);
		//printf("%d", i[arr]);
	}
	return 0;
}

4.2 指针-指针

指针-指针的前提:两个指针指向同一块区域,指针类型一定相同

指针-指针差值的绝对值是指针和指针之间的元素个数

#include <stdio.h>
int main() {
	int arr[10] = { 0 };
	printf("%d\n", &arr[9] - &arr[0]);
	printf("%d\n", &arr[0] - &arr[9]);
	return 0;
}

模拟实现strlen
1.计数器
2.递归 

3.指针-指针

#include <stdio.h>
size_t my_strlen(char* str) {
	char* start = str;
	while (*str != '\0') {
		str++;
	}
	return str - start;
}
int main() {
	char arr[] = "abcdef";
	size_t len = my_strlen(arr);
	printf("%zd\n", len);
	return 0;
}

4.3 指针的关系运算

#define N_VALUES 5
float values[N_VALUES];
float *vp;
for (vp = &values[0]; vp < &values[N_VALUES];)
{
     *vp++ = 0; }
for(vp = &values[N_VALUES]; vp > &values[0];)
{
    *--vp = 0; }
//最后一个与&values[0]比较的指针vp指向数组前

改变每一个元素的指针指向该元素的后面

代码简化:但是应该避免这样写,因为标准并不保证它可行。

for(vp = &values[N_VALUES-1]; vp >= &values[0];vp--) {
    *vp = 0; }
标准规定:
允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与指向第一个元素之前的那个内存位置的指针进行比较。
p可以与p2比较,但与p1比较可能不安全

为什么这么规定?

c++解释,数组前可能存在一片区域储存数组的大小以维护数组,指针访问数组最好从前向后遍历

5.指针和数组 

指针就是指针变量,存放一个地址,指针变量的大小:4/8

数组就是数组,可以存放一组数,数组的大小取决于元素的类型和个数

联系:

数组的数组名是数组首元素的地址,这个地址可以放在指针中;

int arr[] = {0,1,2,3,4,5,6,7,8,9};

int* p = arr;

通过指针可以访问数组的元素;

arr与p的相同点:*(arr+i)==*(p+i)==arr[i]==p[i]

注意:arr与p的不同点:p++;(正确,p是指针变量)arr++;(error,arr是常量)

数组名表是数组首元素的地址,但是有两个例外:

  • sizeof(数组名),数组名单独放在sizeof内部,表示整个数组,计算的是数组大小,单位是字节
  • &数组名,此时数组名表示整个数组,取出的是数组的地址,数组的地址和数组首元素的地址值相同,但是类型和意义不同
#include <stdio.h>
int main() {
	int arr[] = { 1,2,3,4,5,6,7,8 };
	//数组首元素的地址
	printf("%p\n", arr);
	printf("%p\n", arr+1);//跳过4个字节

	printf("%p\n", &arr[0]);
	printf("%p\n", &arr[0]+1);//跳过4个字节

	//整个数组的地址
	printf("%p\n", &arr);
	printf("%p\n", &arr+1);//跳过32个字节
	return 0;
}

6.二级指针

二级指针变量是存放一级指针的地址

#include <stdio.h>
int main() {
	int a = 10;
	int* p = &a;//一级指针变量
	int* * pp = &p;//二级指针变量
	//第一个*与int一起表示pp所指变量的类型为int*,第二个*表示pp为指针变量
	//int** * ppp = &pp;
	//.....
	**pp = a;
	return 0;
}

7.指针数组

指针数组是数组,是存放指针的数组。
eg:int* arr3[5];——存放整型指针的数组,五个元素,每个元素是int*类型

使用指针数组模拟一个二维数组 

#include <stdio.h>
int main() {
	int arr1[] = { 1,2,3,4,5 };
	int arr2[] = { 2,3,4,5,6 };
	int arr3[] = { 3,4,5,6,7 };
	int* arr[] = { arr1,arr2,arr3 };
	for (int i = 0; i < 3; i++) {
		for (int j = 0; j < 5; j++) {
			printf("%d ", arr[i][j]);
		}
		printf("\n");
	}
	return 0;
}
//不是二维数组:二维数组在内存中连续存放

相关文章:

  • Webpack:详解打包和管理JavaScript和CSS依赖
  • Linux开发工具——gcc
  • 深度学习篇---网络分析(1)
  • FPGA——FPGA状态机实现流水灯
  • Redis-set类型
  • Vue2_Vue.js教程
  • C#:is关键字
  • 瑞萨RA4M2使用心得-RA-Eco-RA4M2-100PIN-V1.0MCU
  • 聊天室项目之http知识
  • 学习笔记:商品经济(工业经济)
  • 大规模硬件仿真系统的编译挑战
  • LeNet-5简介及matlab实现
  • tiny_dnn_test250101
  • 5G从专家到小白
  • RT-Thread ulog 日志组件深度分析
  • 区块链技术在物联网中的应用与挑战
  • C语言启程1.0
  • C#:枚举(enum)
  • 3D Web轻量化引擎HOOPS Communicator在装配件管理上的具体优势
  • 【嵌入式学习3】基于python的tcp客户端、服务器
  • 新网站怎么做seo优化/搜外网友情链接
  • 成都万商云集做网站怎么样/建立网站的流程
  • 广告营销网站/苏州seo按天扣费
  • 摄影网站源码 国外/高质量软文
  • 建筑毕业设计代做网站/seo主要做哪些工作
  • app建设网站公司哪家好/百度网站快速排名公司