指针进阶(一)

news2024/7/6 8:47:50

指针进阶

    • 1. 字符指针
      • 面试题
    • 2. 指针数组
    • 3. 数组指针
      • 3.1 数组指针的定义
      • 3.2 &数组名VS数组名
    • 3.3 数组指针的使用
    • 4. 数组传参和指针传参
      • 4.1 一维数组传参
      • 4.2 二维数组传参
      • 4.3 一级指针传参
      • 4.4 二级指针传参

前言

指针的主题,我们在初级阶段的《指针》章节已经接触过了,我们知道了指针的概念:

1. 指针就是个变量,用来存放地址,地址唯一标识一块内存空间。
2. 指针的大小是固定的4/8个字节(32位平台/64位平台)。
3. 指针是有类型,指针的类型决定了指针的±整数的步长,指针解引用操作的时候的权限。
4. 指针的运算。
编号 == 地址 == 指针

1. 字符指针

在指针的类型中我们知道有一种指针类型为字符指针 char* ;
😃一般使用:

#include<stdio.h>
int main()
{
	char ch = 'w';
	char* pc = &ch;
	printf("%c\n", *pc);
	return 0;
}

在这里插入图片描述
😊另一种使用:

#include<stdio.h>
int main()
{
	const char* p = "abcdef";
	//给出指针首地址,打印整个字符串
	printf("%s\n", p);
	//指针解引用,打印1个字符
	printf("%c\n", *p);
	return 0;
}

在这里插入图片描述
😉如何理解上面代码呢?

const char* p = "abcdef";//实质:将“abcdef"首字符放入p中

so给出首地址即能打印出整个字符串,%c只能打印出a

面试题

🤓让我们看如下代码:

代码演示:
#include <stdio.h>
int main()
{
	char str1[] = "hello bit.";
	char str2[] = "hello bit.";
	const char* str3 = "hello bit.";
	const char* str4 = "hello bit.";
	
	if (str1 == str2)
		printf("str1 and str2 are same\n");
	else
		printf("str1 and str2 are not same\n");

	if (str3 == str4)
		printf("str3 and str4 are same\n");
	else
		printf("str3 and str4 are not same\n");
	return 0;
}

🏅输出结果:
在这里插入图片描述

🤔为什么str1和str2不相同,str3和str4相同呢?

这里str3和str4指向的是一个同一个常量字符串。C/C++会把常量字符串存储到单独的一个内存区域,当几个指针。指向同一个字符串的时候,他们实际会指向同一块内存。但是用相同的常量字符串去初始化不同的数组的时候就会开辟出不同的内存块。所以str1和str2不同,str3和str4不同
😁看以下图片更清晰理解
在这里插入图片描述

🧐若我们比较取地址&str3,&str4:

//代码演示:
#include<stdio.h>
int main()
{

	const char* str3 = "hello bit.";
	const char* str4 = "hello bit.";
	if (&str3 == &str4)
	{
		printf("Yes\n");
	}
	else
	{
		printf("No\n");
	}
	return 0;
}

🏅运行结果:
在这里插入图片描述

2. 指针数组

在《指针初阶》章节我们也学了指针数组,指针数组是一个存放指针的数组。
🧐这里我们再复习一下,下面指针数组是什么意思?

int* arr1[10]; //整形指针的数组
char* arr2[4]; //一级字符指针的数组
char** arr3[5];//二级字符指针的数组

👀指针数组一般不用于一维数组:

代码演示:
int main()
{
	int a = 1;
	int b = 2;
	int c = 3;
	int d = 4;
	//不会这样使用的
	int* arr[] = { &a, &b, &c, &d };
	return 0;
}

可以使用指针数组模拟一个二维数组
在这里插入图片描述

代码演示:
int main()
{
	int arr1[] = { 1,2,3,4,5 };
	int arr2[] = { 2,3,4,5,6 };
	int arr3[] = { 3,4,5,6,7 };
	        //int*  int*  int*
	//指针数组
	int* arr[] = { arr1, arr2, arr3 };
	int i = 0;
	for (i = 0; i < 3; i++)
	{
		int j = 0;
		for (j = 0; j < 5; j++)
		{
			printf("%d ", arr[i][j]);
		}
		printf("\n");
	}

	return 0;
}

🏅运行结果:
在这里插入图片描述

🤠指针数组还可以这样用:
在这里插入图片描述

代码演示
#include<stdio.h>
int main()
{
	//指针数组
	char* arr[5] = {"hello bit", "hehe", "penggeC", "bitejiuyeke", "C++"};

	int i = 0;
	for (i = 0; i < 5; i++)
	{
		printf("%s\n", arr[i]);
	}

	return 0;
}

🏅运行结果:
在这里插入图片描述

3. 数组指针

3.1 数组指针的定义

在这里插入图片描述
🤔下面代码哪个是数组指针?

int *p1[10];
int (*p2)[10];
//p1, p2分别是什么?

♥️解释:

int (*p)[10];
//解释:p先和*结合,说明p是一个指针变量,然后指着指向的是一个大小为10个整型的数组。所以p是一个指针,指向一个数组,叫数组指针。
//这里要注意:[]的优先级要高于*号的,所以必须加上()来保证p先和*结合。

3.2 &数组名VS数组名

数组名理解:
数组名是数组首元素的地址
但是存在2个例外:

  1. sizeof(数组名),这里的数组名表示整个数组,sizeof(数组名)计算的是整个数组的大小,单位是字节
  2. &数组名,这里的数组名表示整个数组,取出的是数组的地址
//代码案例
#include<stdio.h>
int main()
{
	int arr[10];
	printf("%p\n", arr);//int*
	printf("%p\n", arr+1);

	printf("%p\n", &arr[0]);//int*
	printf("%p\n", &arr[0] + 1);

	printf("%p\n", &arr);//
	printf("%p\n", &arr+1);

	//指针类型决定了指针+1,到底+几个字节

	return 0;
}

🏅分析案例
在这里插入图片描述

本例中 &arr 的类型是: int(*)[10] ,是一种数组指针类型
数组的地址+1,跳过整个数组的大小,所以 &arr+1 相对于 &arr 的差值是40

3.3 数组指针的使用

🤔那数组指针是怎么使用的呢?
既然数组指针指向的是数组,那数组指针中存放的应该是数组的地址。
看如下代码:

#include <stdio.h>
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,0 };
	int(*p)[10] = &arr;//把数组arr的地址赋值给数组指针变量p
	//但是我们一般很少这样写代码
	return 0;
}

😸一个数组指针的使用:

void print(int arr[3][5], int r, int c)
{
	int i = 0;
	for (i = 0; i < 3; i++)
	{
		int j = 0;
		for (j = 0; j < 5; j++)
		{
			printf("%d ", arr[i][j]);
		}
		printf("\n");
	}
}
int main()
{
	int arr[3][5] = { {1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7} };
	print(arr, 3, 5);

	return 0;
}

在这里插入图片描述

#include<stdio.h>
void print(int (*p)[5], int r, int c)
{
	int i = 0;
	for (i = 0; i < 3; i++)
	{
		int j = 0;
		for (j = 0; j < 5; j++)
		{
			printf("%d ", p[i][j]);
		}
		printf("\n");
	}
}

int main()
{
	int arr[3][5] = { {1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7} };
	print(arr, 3, 5);

	return 0;
}

在这里插入图片描述
😽学了指针数组和数组指针我们来一起回顾并看看下面代码的意思:

int arr[5];
int *parr1[10];
int (*parr2)[10];
int (*parr3[10])[5];

🥳解析:
在这里插入图片描述
在这里插入图片描述

4. 数组传参和指针传参

🤔在写代码的时候难免要把【数组】或者【指针】传给函数,那函数的参数该如何设计呢?

4.1 一维数组传参

#include <stdio.h>
void test(int arr[])//ok?
{}
void test(int arr[10])//ok?
{}
void test(int* arr)//ok?
{}
void test2(int* arr[20])//ok?
{}
void test2(int** arr)//ok?
{}
int main()
{
	int arr[10] = { 0 };
	int* arr2[20] = { 0 };
	test(arr);
	test2(arr2);
}

在这里插入图片描述

4.2 二维数组传参

void test(int arr[3][5])//ok?
{}
void test(int arr[][])//ok?
{}
void test(int arr[][5])//ok?
{}
//总结:二维数组传参,函数形参的设计只能省略第一个[]的数字。
//因为对一个二维数组,可以不知道有多少行,但是必须知道一行多少元素。
//这样才方便运算。
void test(int* arr)//ok?
{}
void test(int* arr[5])//ok?
{}
void test(int(*arr)[5])//ok?
{}
void test(int** arr)//ok?
{}
int main()
{
	int arr[3][5] = { 0 };
	test(arr);
}

在这里插入图片描述

4.3 一级指针传参

#include <stdio.h>
void print(int* p, int sz)
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d\n", *(p + i));
	}
}
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9 };
	int* p = arr;
	int sz = sizeof(arr) / sizeof(arr[0]);
	//一级指针p,传给函数
	print(p, sz);
	return 0;
}

🏅运行结果:
在这里插入图片描述

思考:
🤔当一个函数的参数部分为一级指针的时候,函数能接收什么参数?

void test(int* p)
{
}
int a = 10;
int* ptr = &a;
int arr[5];

test(arr);//传整型一维数组的数组名
test(&a);//传整型变量的地址
test(ptr);//传整型指针

4.4 二级指针传参

代码演示:
#include <stdio.h>
void test(int** ptr)
{
	printf("num = %d\n", **ptr);
}
int main()
{
	int n = 10;
	int* p = &n;
	int** pp = &p;
	test(pp);
	test(&p);
	return 0;
}

🏅运行结果:
在这里插入图片描述
思考:
🤔当函数的参数为二级指针的时候,可以接收什么参数?

void test(int** p)
{
}

int n = 10;
int* p = &n;
int** pp = &p;
int* arr[6];

test(&p);
test(pp);
test(arr);

💘不知不觉,指针进阶(一)以告一段落。通读全文的你肯定收获满满,不久的将来会继续更新指针进阶的内容,让我们继续为C语言学习共同奋进!!!

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

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

相关文章

腾讯音乐基于 Apache Doris + 大模型构建全新智能数据服务平台

当前&#xff0c;大语言模型的应用正在全球范围内引发新一轮的技术革命与商业浪潮。腾讯音乐作为中国领先在线音乐娱乐平台&#xff0c;利用庞大用户群与多元场景的优势&#xff0c;持续探索大模型赛道的多元应用。本文将详细介绍腾讯音乐如何基于 Apache Doris 构建查询高效、…

Python之调用shell两种写法(二十八)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 人生格言&#xff1a; 人生…

如何优雅的关闭流

https://blog.csdn.net/zy345293721/article/details/103654745 JDK1.7 开始使用 关闭流使用try-catch-finally 是jdk1.7 之前的语法 try (FileInputStream fis new FileInputStream(srcFile)){fis.read(fileContent);fis.close();} catch (IOException e) {e.printStackTr…

大模型参数高效微调技术原理综述(二)-BitFit、Prefix Tuning、Prompt Tuning

随着&#xff0c;ChatGPT 迅速爆火&#xff0c;引发了大模型的时代变革。然而对于普通大众来说&#xff0c;进行大模型的预训练或者全量微调遥不可及。由此&#xff0c;催生了各种参数高效微调技术&#xff0c;让科研人员或者普通开发者有机会尝试微调大模型。 因此&#xff0c…

【Unity3D】UI Toolkit样式选择器

1 前言 UI Toolkit简介 中介绍了样式属性&#xff0c;UI Toolkit容器 和 UI Toolkit元素 中介绍了容器和元素&#xff0c;本文将介绍样式选择器&#xff08;Selector&#xff09;&#xff0c;主要包含样式类选择器&#xff08;Class Selector&#xff09;、C# 类选择器&#xf…

STM32f103入门(11)DMA直接存储器读取

DMA DMA简介数据转运ADC扫描模式DMADMA ADC 连续单词扫描初始化如下连续循环扫描 DMA简介 DMA&#xff08;Direct Memory Access&#xff09;直接存储器存取 DMA可以提供外设和存储器或者存储器和存储器之间的高速数据传输&#xff0c;无须CPU干预&#xff0c;节省了CPU的资源…

Vue + Element UI 前端篇(三):工具模块封装

Vue Element UI 实现权限管理系统 前端篇&#xff08;三&#xff09;&#xff1a;工具模块封装 封装 axios 模块 封装背景 使用axios发起一个请求是比较简单的事情&#xff0c;但是axios没有进行封装复用&#xff0c;项目越来越大&#xff0c;会引起越来越多的代码冗余&am…

Vue + Element UI 前端篇(七):功能组件封装

组件封装 为了避免组件代码的臃肿&#xff0c;这里对主要的功能部件进行封装&#xff0c;保证代码的模块化和简洁度。 组件结构 组件封装重构后&#xff0c;试图组件结构如下图所示 代码一览 Home组件被简化&#xff0c;包含导航、头部和主内容三个组件。 Home.vue <te…

mysql表操作-约束删除、用户填加、授权和撤权

目录 一、表的约束删除 1.查看所有表的约束条件 2.删除主键 3.删除唯一键 4.删除check键值 5.删除check键值 6.删除not null键值并删除check键值 7.删除键外值 8.检查表的约束条件是否存在 二、设置数据库密码策略 1.查看数据库密码的策略 2.修改数据库密码的长度 …

视频汇聚/视频云存储/视频监控管理平台EasyCVR安全检查的相关问题及解决方法2.0

开源EasyDarwin视频监控TSINGSEE青犀视频平台EasyCVR能在复杂的网络环境中&#xff0c;将分散的各类视频资源进行统一汇聚、整合、集中管理&#xff0c;在视频监控播放上&#xff0c;TSINGSEE青犀视频安防监控汇聚平台可支持1、4、9、16个画面窗口播放&#xff0c;可同时播放多…

Leetcode 1572.矩阵对角线元素之和

给你一个正方形矩阵 mat&#xff0c;请你返回矩阵对角线元素的和。 请你返回在矩阵主对角线上的元素和副对角线上且不在主对角线上元素的和。 示例 1&#xff1a; 输入&#xff1a;mat [[1,2,3],[4,5,6],[7,8,9]] 输出&#xff1a;25 解释&#xff1a;对角线的和为&#xff…

C#使用proto

写多了go代码&#xff0c;被go mod tidy惯坏了&#xff0c;还以为全天下的都很好用呢&#xff0c;结果发现并不是这样。尤其是项目组的proto还是又封了个工具直接就能跑得&#xff0c;导致以为没那么复杂的事情变得复杂了起来。是有两套生成的规则&#xff0c;时间有点晚&#…

【漏洞复现】网御ACM上网行为管理系统bottomframe.cgi接口存在SQL注入漏洞

漏洞描述 网御上网行为管理系统(简称Leadsec ACM)是网御为互联网接入用户在信息内容安全、网络应用管理、组织运营效率、网络资源利用、法律风险规避及网络投资回报等方面提供的全方位解决方案。网御上网行为管理系统存在SQL注入漏洞。 网御 ACM上网行为管理系统 bottomfram…

docker快速安装-docker一键安装脚本

1.下载/配置安装脚本 touch install-docker.sh #!/bin/bash #mail:ratelcloudqq.com #system:centos7 #integration: docker-latestclear echo "######################################################" echo "# Auto Install Docker …

【ABAP】 如何实现点击不同按钮动态显示不同的选择屏幕?(附完整示例代码)

&#x1f482;作者简介&#xff1a; THUNDER王&#xff0c;阿里云社区专家博主&#xff0c;华为云云享专家&#xff0c;腾讯云社区认证作者&#xff0c;CSDN SAP应用技术领域优质创作者。在学习工作中&#xff0c;我通常使用偏后端的开发语言ABAP&#xff0c;SQL进行任务的完成…

Docker实战:docker compose 搭建Rocketmq

1、配置文件准备 1.1、 新建目录&#xff1a;/home/docker/data/rocketmq/conf mkdir /home/docker/data/rocketmq/conf1.2、 在上面目录下新建文件broker.conf文件&#xff0c;内容如下 brokerClusterName DefaultCluster brokerName broker-a brokerId 0 deleteWhen 0…

Linux命令之目录管理(详解)

Linux命令之目录管理 创建目录删除目录显示目录目录的拷贝返回上层目录目录的移动 创建目录 创建目录用 mkdir命令 mk–>make dir–>direction 同样的mkdir有很多选项&#xff0c;你可以通过用man命令查这个mkdir命令来获得 mkdir [选项] 目录名创建多级目录 mkdir -r /…

开源项目观察8月报

前言 总结8月份自己关注的一些项目/语言的新版本 大数据 hue 1月19: 4.11 https://docs.gethue.com/releases/release-notes-4.11.0/ 支持 iceberg 数据源 通过缓存 Livy session 中的信息来加速 SparkSQL&#xff0c;并适配 Spark UDF 支持 HPL/SQL: hive sql 的高级语法…

React 状态管理 - Redux 入门

目录 扩展学习资料 Redux基础 Redux动机 Redux核心概念 Redux的三个原则 Redux运转图 React & Redux的搭配使用 Redux API React-Redux API&#xff08;关联组件&#xff09; 从头创建一个工程 package.json /src/reducer/index.js /src/reducer/home/index.js…

springboot配置统一返回结果类

目录结构&#xff1a; Result类&#xff1a; package com.xxxx.common.result;import lombok.Data;Data public class Result<T> {//状态码private Integer code;//信息private String message;//数据private T data;//构造私有化private Result() { }//设置数据,返回对…