C_07_指针

news2024/11/13 15:37:16

指针

地址编号: 存的是值

指针: 存储的是地址编号值 的数据类型 是地址编号的数据类型,强调的是类型

指针变量: 数据类型为指针的变量,用于保存地址编号

地址编号

概述:

地址编号是内存每一个字节的编号统称。

​ int n = 10;

​ 在内存分配了4个字节

每个进程(运行着的程序)都有寻址范围

32位机的寻址范围时4G,0x00 00 00 00~0xff ff ff ff.

系统会为每一个字节分配一个32位的地址编号。便于系统管理内存

32位机的地址编号占4字节

64位机的地址编号占8字节

指针与指针变量

指针:是地址编号的数据类型,强调的是类型

指针变量:数据类型为指针的变量,用于保存地址编号

注意

在这里插入图片描述

指针变量定义

语法:

数据类型 * 变量名;

注意:

数据类型: 指针变量指向的地址中存储的数据 的数据类型

1,不同的指针只能存储对应的数据类型的地址

​ 如:

​ 整型的指针变量,只能存放整型变量的地址

2,如果在一行中定义多个指针变量,每个指针变量前面都需要加 * 来修饰

如:

int *p1;
char *p2;
float *p3;
double *p4;
void *p5;
...
int nums[] = {1,2,3,4,5};
int *p6 = nums;
int *p1,*p2,*p3;

指针变量的初始化

注意

1,当指针变量为局部变量,并没有为其赋初始值,此时因为是局部变量,所以系统为其赋 值为随机数,此时我们并不知道指针变量存
储的地址是什么.这种指针我们称为野指针
2,指针变量没有指向合法的地址建议初始化为NULL,这种指针我们称为空指针

int  *p  = NULL;      //空指针: 是NULL  不是0  0也有地址 而NULL 没有
int  *p;     //因为没有初始化所有初始化随机  所以就不知道指向哪里了  就是野指针

指针运算符

&

作用:取地址

int a = 10;
int *p = &a;
printf("%p\n",p);    //此时打印的是p的值即a的地址

注意:

​ 栈:静态全局区可取地址
​ 堆,常量池,代码区不可取

*

作用:取值

printf("%d\n",*p);   //将p中存储的地址中的值获取

作用**:改值**

*p =;
注意事项

野指针与空指针取值操作会出现段错误

哪怕用空指针也不用野指针 虽然空必然报错 但是错误是显性的 野指针对程序一定是坏的

因为 空指针 必报错 而野指针可能会在合法地址内也可能不在 所以 就很难排错

int  *p  = NULL;      //空指针: 是NULL  不是0  0也有地址 而NULL 没有
int  *p;     //因为没有初始化所有初始化随机  所以就不知道指向哪里了  就是野指针

指针的数据类型

int *p;
char *p;

口诀:

将指针变量名的变量名去除,剩余的就是指针变量的数据类型。

int *p  去掉变量名p   那就是 int *

指针存储的数据类型

int *p;
char *p;

口诀:

将指针变量的  变量名与最近的一个* 去除,剩余的就是指针变量存储地址对应的数据类型


int *p;
去掉 *p
就是int

作用:

分析等号两边数据类型一致

二级指针

概念:

又名 指针的指针

因为指针变量本质也是一个变量,在32位机中占4字节,64位机占8字节

如:

int a = 10;  //假设此时a在栈内存中的地址编号为0x00 00 00 01,长度4字节
int *p = &a; //将a的地址赋值给变量p,此时p存储的地址为a的地址即0x00 00 00 01 p本身也需要占用内存,其地址假设为 0x 00 00 00 06
int **p = &p; // 将p的地址 0x 00 00 00 06赋值给p2
1,二级指针使用  *取出的是什么?
     取出的是存储的一级指针本身的地址,不是一级指针存储的地址
     int num01=10;
	int num02=1;
	int *pl = &num01;
	int *p2 = &num02;
     *pl = 100;  //通过 * 但核心还是地址来改变值
	printf("num01 = %d\n",num01);    // 100  因为使用取值与改值 *将指针p存储的数据10进行了改值
	printf("num02 = %d\n",num02);    //  1


   int **p3 =&p1;   //将p1本身的地址赋值给p3 此时p3存储的是p1本身的地址
	 *p3 = p2;        // 将 p2 的地址赋值给 p3
   printf("**p3  = %d\n",**p3);  // 1    **p  是* *p3 也就是取*p3地址存的值 还是num02

void与指针

void的作用:

万能指针的作用 核心作用就是作为函数的形参。

1 void 作为函数的返回值类型,表示没有返回值或返回值为NULL2 void 与指针结合称为万能指针 ,可有存储任何一种类型变量的地址。
			但是不能直接对 void  *p 中的p 直接取值 因为 无法确定p的取值宽度 。

int num = 10;
int *p1 = #
char c = 'a';
char *p2 = &c;
void *p3 = #
void *p4 = &c;
p3 = &c;		//修改p3存储的地址

万能指针的作用:

  • 核心作用就是作为函数的形参。

万能指针的注意事项:

  • 不能直接对 void *p 中的p 直接取值 因为 无法确定p的取值宽度 。
    在这里插入图片描述

const与指针

常量指针

可以修改其存储的地址 ,无法修改

常量指针 是指针 可以修改其存储的地址,但是无法修改其存储的值。

语法:

数据类型 const *指针名称;
或者
const  数据类型 *指针名称;

示例:

#include <stdio.h>
int main(int argc, char const *argv[])
{
	int a = 10;
	int b = 1;
	//常量指针,本质是指针,可以修改其存储的地址,但是无法修改其存储的地址对应的值
	//const int *p = &a;  两种写法   写法a
	int const *p = &a;  //写法b
	p = &b;//改变p存储的地址
	//*p = 100;//改变p存储的地址对应的值,此处报错
	return 0;
}

在这里插入图片描述

指针常量

可以修改其存储的 ,无法修改存储地址

指针常量是常量 该指针存储的地址无法被修改,但是可以修改其存储的地址对应的值。

语法:

数据类型 * const 指针名称;
 int *  const p;    

在这里插入图片描述

常量指针常量

无法修改其存储的 ,也无法修改存储地址

概念: 常量指针指向常量

语法:

const 数据类型 * const 指针名;
或
数据类型 const * const 指针名;

示例

#include <stdio.h>
int main(int argc, char const *argv[])
{
	int a = 10;
	int b = 1;
	//常量指针常量
	//const int * const p = &a;
	int const * const p = &a;
	p = &b;//不可以改变p存储的地址
	*p = 100;//也不可以改变p存储的地址对应的值
	printf("a = %d\n",a);
	return 0;
}

在这里插入图片描述

数组与指针

数组名的本质

数组名 就是 数组中第一个元素的首地址

printf("arr的地址:  %p\n",arr);   // 求数组名地址
printf("arr[0]地址:%p\n",arr[0]);  //求地址首地址
                                                                       // 发现结果一样

数组名 就是 数组中第一个元素的首地址

在这里插入图片描述

#include <stdio.h>
int main(int argc, char const *argv[])
{
    // 证明数组名就是数组中第一个元素的地址
    int arr[] = {1, 3, 5, 7, 10};
    printf("arr的地址:  %p\n", arr);
    // 使用  (void *) 强制转换为 十六进制地址数
    printf("arr[0]地址:%p\n", (void *)&arr[0]);

    // 一维数组怎么操作  二维完全可以复刻   先定义指针赋值
    // 指针可以通过下标访问数组元素,这与数组名的使用方式相同:
    // 定义一个指向int的指针p,初始化为数组arr的首地址
    int *p = arr;
    printf("arr[1] = %d\n", arr[1]);   // 3  下标获取数组下标为1的元素
    printf("p[1]   = %d\n", p[1]);     // 3  指针访问数组下标为1的元素
    printf("*(p + 1)=%d\n", *(p + 3)); // 7   *(p+1) 等价于 p[1]
    printf("*P+1= %d\n", *p + 3);      //   4    这里是对*p  也就是数字取值后加1  不是位移单位
    printf("p[0]= %d\n", p[0]);        // 1  p[0] 等价*p

    printf("数组arr的存储首地址为   %p\n", &arr);  //   0x7ffd49bad8b0
    printf("p的地址为:             %p\n", p);     //   0x7ffd49bad8b0
    printf("p[0]的地址为:          %p\n", &p[0]); //   0x7ffd49bad8b0
    printf(" p+1的地址为:          %p\n", p + 1); //      0x7ffd49bad8b4
    // 这里的+1是加步长 步长取决数组数据类型的大小  比如是int 那就是加一步长  而此时的一步长就是4字节
    printf("*p+1=%d\n", *p + 1); //     2   核心思想  *   取值符  就是将此地址存储的数值拿出来
    // *p 就是将p存储的值拿出来了  也就是1  再后移一位 所以是2
    printf("*(p+1)=%d\n", *(p + 1)); //  3
    // 将当前位置+1个步长,取的是数组下标为1的元素
    return 0;
}

在这里插入图片描述

事例2:

#include <stdio.h>
int main(int argc, char const *argv[])
{
    char str[] = "hIc";
    char *p = str;
    // 此时数组中存储的元素为char,一个char的长度为1字节,顾此时1个步长=1字节
    printf("%c\n", *(p + 1));
    printf("%c\n", *(p + 2));
    printf("%c\n", *(p + 3));
    printf("%c\n", *p + 1); //*p获取的是其地址对应的值,其地址的值为h,h+1=i
    printf("%c\n", (*p) + 1);
    return 0;
}

指针数组与数组指针

概述

数组指针(数组的指针):指向数组,本质是指针
指针数组(存储指针的数组):本质是一个数组

示例1

#include <stdio.h>
int main(int argc, char const *argv[])
{
    int nums[] = {1, 2, 3};
    // 数组指针(数组的指针,指向数组,本质是指针)
    int *p = nums;
    int a = 1;
    int b = 2;
    int c = 3;
    int *p11 = &a;
    int *p12 = &b;
    int *p13 = &c;
    // 指针数组(存储指针的数组,本质是一个数组)
    int *p2[] = {p11, p12, p13};
    return 0;
}

示例2

#include <stdio.h>
int main(int argc, char const *argv[])
{
     int nums01[] = {1, 2, 3, 4, 5};
     int nums02[] = {11, 22, 33, 44, 55};
     int nums03[] = {111, 222, 333, 444, 555};
       // 二维数组
     int nums04[][5] = {
           {1, 2, 3, 4, 5},
           {11, 22, 33, 44, 55},
           {111, 222, 333, 444, 555}};
     // 指针数组
       int *ps[] = {nums01, nums02, nums03};
     printf("nums04[0][1] = %d\n", nums04[0][1]);
     printf("ps[0][1] = %d\n", ps[0][1]);
     printf("nums04[1][1]的=%d\n", nums04[1][1]);
     printf("(*(ps+1))[1]=%d\n", (*(ps + 1))[1]);
     printf("*(*(ps+1)+1)=%d\n", *(*(ps + 1) + 1));
     return 0;
}

示例3

#include <stdio.h>
int main(int argc, char const *argv[])
{
     // char strs[][50] = {"GaoLei","WangChenHui","YueZheng"};
     char *strs[] = {"GaoLei", "WangChenHui", "YueZheng"};
     printf("strs[0] = %s\n", strs[0]);       // GaoLei
     printf("strs[1] = %s\n", strs[1]);       // WangChenHui
     printf("strs[2] = %s\n", strs[2]);       // YueZheng
     printf("*(strs+0) = %s\n", *(strs + 0)); //
     printf("*(strs+1) = %s\n", *(strs + 1));
     printf("*(strs+2) = %s\n", *(strs + 2));
     printf("(*(strs + 0))+3 = %s\n", (*(strs + 0)) + 3);
     printf("(*(strs + 0))[3] = %c\n", (*(strs + 0))[3]);
     return 0;
}

示例4

#include <stdio.h>
int main(int argc, char const *argv[])
{
    // char strs[][50] = {"GaoLei","WangChenHui","YueZheng"};
    char *strs[] = {"GaoLei", "WangChenHui", "YueZheng"};
    printf("strs[0]的地址=%p\n", &strs[0]);
    printf("strs的地址=%p\n", strs);
    printf("&strs的地址=%p\n", &strs);
    char *p;
    p = strs;
    // char **p2 = &p;
    // char **p2 = &strs;
    char **p2 = strs;
    printf("*p2 = %s\n", *p2);
    printf("**p2 = %c\n", **p2);
    printf("*(p2+1) = %s\n", *(p2 + 1));
    printf("*(p2+2) = %s\n", *(p2 + 2));
    printf("(*(p2+1))+4 = %s\n", (*(p2 + 1)) + 4);
    return 0;
}
#include <stdio.h>
int main(int argc, char const *argv[])
{
      char arr[] = "xxz";
      char *p = arr;
      // ASCII码值 是 97-122  也就是  小写   a-z
      //  大写字母的 ASCII 码值范围是 65 到 90
      printf("*p+1== %c\n", *p + 1); //*p得到的是 p[0] 也就是 x的ASCII 码值 120  然后加一
      return 0;
}

步长:

通常指的是数组中元素的内存间隔。

步长 = 数组中存储的元素的数据类型所占的字节数 当为int 那加1 就是加一步长也就是加4 当为char 一步长就是1 加1 就是加1

函数与指针

函数名的本质

函数名本质上就是函数在代码区存储的首地址

指针作为形参

函数指针

作用:记录函数的地址
		语法:
		返回值类型 (*指针名称)(指向的函数的形参列表的数据类型) = 函数名;
			注意:
				形参列表的数据类型可有可无
函数指针调用函数
			指针名 (实参列表);
			变量名 = 指针名(实参列表);

事例:

#include <stdio.h>
extern void add(int a, int b);
extern void sub(int a, int b);
extern void test(void (*p)(int, int));
int main(int argc, char const *argv[])
{
    printf("main函数的地址=%p\n", main);
    int (*p)(int, char *[]) = main;
    // 返回值类型 (*指针名称)(指向的函数的形参列表的数据类型) = 函数名;
    void (*p2)(int, int) = add;
    // 函数指针调用函数
    // 指针名(实参列表);
    // 变量名 = 指针名(实参列表);
    //  p2 = sub;
    p2(1, 2);
    test(p2);
    return 0;
}
void test(void (*p)(int, int))
{
    p(10, 12);
}
void add(int a, int b)
{
     printf("%d+%d=%d\n", a, b, a + b);
}
void sub(int a, int b)
{
    printf("%d-%d=%d\n", a, b, a - b);
}

指针作为形参

#include <stdio.h>
extern void showArray(int nums[],int len);
int main(int argc, char const *argv[])
{
	int nums[] = {1,2,3,4,5};
	showArray(nums,5);
	return 0;
}
void showArray(int *nums,int len)
{
	for (int i = 0; i < len; i++)
		{
		// printf("%d\t",nums[i]);
		printf("%d\t",*(nums+i));
		}
			printf("\n");
}

指针作为返回值

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
// extern int* getNums();
extern void getNums(int nums[],int len);
int main(int argc, char const *argv[])
{
	srand(time(NULL));
	// int *p = getNums();
	// for (int i = 0; i < 10; i++)
	// {
	// printf("%d\t",p[i]);
	// }
	// printf("\n");
	int nums[10] = {0};
	getNums(nums,10);
for (int i = 0; i < 10; i++)
{
	printf("%d\t",nums[i]);
}
	printf("\n");
 return 0;
}
// int* getNums()
// {
// static int nums[10] = {0};
// for (int i = 0; i < 10; i++)
// {
// nums[i] = rand() % 100;
// }
// return nums;
// }
void getNums(int nums[],int len)
{
for (int i = 0; i < len; i++)
	{
	nums[i] = rand() % 100;
	}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2079176.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

轻松获取商品细节!淘宝天猫详情接口API带你看遍全球好物

随着互联网的飞速发展&#xff0c;网络购物已经成为人们生活中不可或缺的一部分。而作为国内最大的电商平台&#xff0c;淘宝和天猫自然成为了消费者们选购商品的首选之地。为了更好地满足用户需求&#xff0c;淘宝天猫推出了详情接口API&#xff0c;让用户能够更轻松地获取商品…

【人工智能】Transformers之Pipeline(十三):填充蒙版(fill-mask)

目录 一、引言 二、填充蒙版&#xff08;fill-mask&#xff09; 2.1 概述 2.2 技术原理 2.2.1 BERT模型的基本概念 2.2.2 BERT模型的工作原理 2.2.3 BERT模型的结构 2.2.4 BERT模型的应用 2.2.5 BERT模型与Transformer的区别和联系 2.3 应用场景 2.4 pipeline参数…

在银河麒麟服务器V10上源码编译安装mysql-5.7.42-linux-glibc2.12-x86_64

在银河麒麟服务器V10上源码编译安装mysql-5.7.42-linux-glibc2.12-x86_64 一、卸载MariaDB&#xff08;如果已安装&#xff09;二、下载MySQL源码包并解压三、安装编译所需的工具和库四、创建MySQL的安装目录及数据库存放目录五、编译安装MySQL六、配置MySQL七、设置环境变量八…

计算机系统-内存与CPU

CPU&#xff08;中央处理单元&#xff0c;Central Processing Unit&#xff09;是计算机系统的核心组件&#xff0c;负责执行程序中的指令并处理数据。它被称为计算机的“大脑”&#xff0c;因为它控制了计算机的所有计算和操作。CPU主要由以下几个部分组成&#xff1a; 算术逻…

如何在UE5.4中重新定位动画?

动画在游戏和电影制作中扮演着至关重要的角色&#xff0c;而在虚幻引擎5.4&#xff08;UE5.4&#xff09;这一强大的实时3D创作平台中&#xff0c;重新定位动画的能力更是将创意表达推向了新的高度。本文将引导您探索UE5.4中重新定位动画的技巧&#xff0c;确保您的动画作品不仅…

leetcode 169 多数元素

正文 本题较为简单&#xff0c;但是有一些比较有趣的方法&#xff0c;这里特来记录一下。 普通方法 遍历整个数组&#xff0c;使用 count 进行统计&#xff0c;然后选择出现次数大于 len(nums) / 2 的元素。 class Solution:def majorityElement(self, nums: List[int]) -&g…

工业相机测长仪的组成部分

关键字:工业相机测长仪,高精度测长仪,视觉测量系统,蓝鹏测控测长仪,工业测长仪, 本文介绍了蓝鹏测控公司机器视觉业务 测长仪的核心产品及技术特点&#xff0c;主要涵盖相机部分、相机防护系统、补光系统和软件部分。 &#xff08;一&#xff09;相机部分 我司的机器视觉业务…

双轴测径仪的四种样式!

双轴测径仪主要是用于外径及椭圆度的检测&#xff0c;适用于线缆电缆、橡胶塑料、金属、纸管等各种材质的线棒管材的外径及椭圆度尺寸检测。 双轴测径仪是由两个单测头垂直布置组合而成&#xff0c;目前根据用户的不同需求&#xff0c;为双轴测径仪设计了四种外观样式。 45角…

【开端】从黑神话。悟空看国产游戏技术能否引领全球

国产游戏技术能否引领全球&#xff1f; 近年来&#xff0c;国产游戏行业蓬勃发展&#xff0c;技术水平不断提升&#xff0c;多款作品在国际市场上崭露头角。从画面渲染到物理引擎&#xff0c;从AI技术到服务器架构&#xff0c;中国游戏开发者在各个领域都取得了显著进步。面对…

博主自用智能猫砂盆分享!CEWEY、空气萝卜、糯雪三款区别到底在哪里

不得不说&#xff0c;智能猫砂盆真的是我们这些经常外出的养猫人必备的一个智能产品了&#xff0c;自动铲屎的功能可以让我完全不用担心猫砂盆里会堆积猫便便&#xff0c;然后发臭生虫。有智能猫砂盆在&#xff0c;这些都不是问题&#xff0c;但是想要买一款合适的智能猫砂盆也…

sql-labs36-40通关攻略

第36关 一.判断闭合点 http://127.0.0.1/Less-36/?id1%df%20--http://127.0.0.1/Less-36/?id1%df%20-- 二.查询数据库 http://127.0.0.1/Less-36/?id-1%df%27%20union%20select%201,database(),3--http://127.0.0.1/Less-36/?id-1%df%27%20union%20select%201,database…

vue中使用vue-video-player插件播放视频 以及 audio播放音频

一、使用vue-video-player插件播放视频 安装 npm install vue-video-player --save 在main.js中引用 //引入视频播放插件 // main.js import VueVideoPlayer from vue-video-player import video.js/dist/video-js.css import vue-video-player/src/custom-theme.cssVue.use(V…

web3js连接测试网并完成交易

ps&#xff1a;有个需求是要等待确认交易。写这篇之后&#xff0c;我发现直接用回调函数要等好久好久好久。找到解决方案在这个链接但是有点麻烦。我已经弃用web3&#xff0c;直接使用ethersjs配合infura了&#xff0c;贼快。 本文将介绍如何使用web3js在Sepolia测试网完成一次…

应用案例|亚克力板CNC加工自动化上下料

在现代化制造领域&#xff0c;自动化和智能化已成为提升生产效率、降低成本的关键。针对当前CNC加工过程中亚克力板上下料环节的人工操作问题&#xff0c;富唯智能提出了基于AMR&#xff08;自主移动机器人&#xff09;的复合机器人解决方案。传统的人工取料、放置以及加工完成…

PSINS工具箱函数介绍——gpssimu

关于工具箱 gpssimu是生成GPS的位置和速度信息的函数&#xff0c;在psins240101\base\base1目录下 本文所述的代码需要基于PSINS工具箱&#xff0c;工具箱的讲解&#xff1a; PSINS初学指导&#xff1a;https://blog.csdn.net/callmeup/article/details/137087932 gpssimu是…

【ubuntu24.04】AX210/MT9621/USB网络共享访问无线网络

发现华硕的路由器访问网络经常有问题,比如clash 经常不能正常工作。 即使内网丢包严重? 期望能给台式机增加一个无线网卡访问外网。 我的五代网卡U12, 无法使用wap2企业版的无线网络:【ubuntu24.04】腾达U12 8812au无线网卡成功安装 普通的是没问题的。 对比了一些网卡,wifi…

认知杂谈37

今天分享 有人说的一段争议性的话 I 《别让焦虑困住自己》 嘿&#xff0c;朋友&#xff01;这大热天的&#xff0c;实在是热得让人心里发慌。 I 咱可别再给自己找不痛快啦&#xff0c;赶紧找个舒服的地儿坐下&#xff0c;泡上一杯茶&#xff0c;好好唠唠嗑&#xff0c;给咱的心…

如何克服编程学习中的挫折感:哲学与心理学的启示

在编程学习的道路上&#xff0c;挫折感几乎是每个人都会遇到的障碍。无论是新手还是资深开发者&#xff0c;都会在面对难题时感到沮丧、焦虑甚至是无助。然而&#xff0c;挫折不仅是挑战&#xff0c;更是成长的机会。在这篇博客中&#xff0c;我们将结合哲学与心理学的智慧&…

2024年8月27日(dockerfile应用,创建私有仓库,在企业中分享项目)

一、dockerfile应用 [rootdocker ~]# mkdir http0 [rootdocker ~]# cd http0/ [rootdocker http0]# vim abc.sh [rootdocker http0]# ls abc.sh [rootdocker http0]# vim abc.sh #!/bin/bashrm -rf /run/*httpd*exec /sbin/httpd -D FOREGROUND [rootdocker http0]# echo "…

【大模型理论篇】通用大模型架构分类及技术统一化

1. 背景 国内的 “百模大战” 以及开源大模型的各类评测榜单令人眼花缭乱&#xff0c;极易让人陷入迷茫。面对如此众多的大模型&#xff0c;我们该如何审视和选择呢&#xff1f;本文将从大模型架构的角度&#xff0c;对常见的开源大模型架构进行汇总与分析。资料来源于公开…