【C语言进阶】深入C语言指针:基础到进阶的跨越

2024-08-25 22:28

本文主要是介绍【C语言进阶】深入C语言指针:基础到进阶的跨越,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

📝个人主页🌹:Eternity._
⏩收录专栏⏪:C语言 “ 登神长阶 ”
🤡往期回顾🤡:C语言数据在内存中的存储
🌹🌹期待您的关注 🌹🌹

在这里插入图片描述

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

❀C语言指针进阶

  • 📒1. 字符指针
  • 📚2. 指针数组
  • 📜3. 数组指针
    • 🌄数组指针的定义
    • 🏞️&数组名VS数组名
    • ⛰️数组指针的使用
  • 📝4. 数组参数、指针参数
    • 🌞一维数组传参
    • 🌙二维数组传参
    • ⭐一级指针传参
    • 🍁二级指针传参
  • 📖5. 总结


前言:在编程的浩瀚星空中,指针犹如一颗璀璨的星辰,它不仅照亮了C语言乃至许多其他编程语言的深邃之路,更是衡量一个程序员技能深度与广度的重要标尺。对于每一位渴望在编程领域深耕细作的开发者而言,掌握指针的进阶应用,无疑是通往高手殿堂的必经之路

指针,作为直接操作内存的强大工具,其魅力在于那份对底层机制的深刻洞察与精准控制。然而,正如攀登高峰需历经艰难险阻,指针的进阶学习同样充满了挑战与未知。从基础的指针声明、解引用,到复杂的指针运算、动态内存管理,再到高级的数据结构如链表、树等的实现与优化,每一步都需要我们付出辛勤的努力与不懈的探索

但正是这些挑战,塑造了指针进阶学习的独特价值。它要求我们不仅要掌握扎实的理论基础,更要具备灵活的思维方式和丰富的实践经验。通过不断地学习与实践,我们将逐渐领悟指针的精髓,学会如何运用指针解决复杂问题,优化程序性能,提升代码质量

让我们一同踏上这场充满挑战与收获的指针进阶之旅吧!


📒1. 字符指针

在C语言中,字符指针是一种特殊的指针类型,它用来存储字符(通常是一个字符串的首地址)的地址。字符指针非常有用,因为它们允许你以数组的形式处理字符串,但实际上是通过指针来操作

int main()
{char ch = 'w';char* pc = &ch;*pc = 'w';return 0;
}

除了放字符还可以放置字符串

int main()
{const char* pstr = "hello";printf("%s\n", pstr);return 0;
}

但是在这段代码中,并没有将hello放到字符指针 pstr 里,本质是把字符串 hello首字符的地址放到了pstr中,也就是把一个常量字符串的首字符 h 的地址存放到指针变量 pstr 中

在这里插入图片描述


我们再来看一道经典的题目来了解以下字符指针

int main()
{char str1[] = "hello world.";char str2[] = "hello world.";const char* str3 = "hello world.";const char* str4 = "hello world.";if (str1 == str2)printf("str1 and str2 are same\n");elseprintf("str1 and str2 are not same\n");if (str3 == str4)printf("str3 and str4 are same\n");elseprintf("str3 and str4 are not same\n");return 0;
}

在这里插入图片描述

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


📚2. 指针数组

C语言中的指针数组是一种特殊的数组,其元素是指针类型,即每个数组元素都存储了一个地址。这些地址可以指向整数、浮点数、结构体、字符串(字符数组的首地址)或其他任何类型的数据。指针数组特别适用于处理多个字符串、动态分配的数据结构集合,或者当你需要存储一系列指向不同对象的指针时

我们在学习指针初阶时,已经简单了解过了,指针数组是一个存放指针的数组

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

📜3. 数组指针

在C语言中,数组指针(有时也被称为指向数组的指针)是一个特殊的指针,它指向一个数组的首元素地址,但其类型表明它指向的是一个数组,而不仅仅是一个单独的元素。然而,需要注意的是,C语言标准中并没有直接称为“数组指针”的类型;我们通常所说的“数组指针”实际上是指向数组首元素的指针,但我们可以根据上下文理解其指向整个数组的含义


🌄数组指针的定义

定义:

int (*p)[10];
//解释:p先和*结合,说明p是一个指针变量,然后指着指向的是一个大小为10个整型的数组。
//所以p是一个指针,指向一个数组,叫数组指针。

这里要注意:[]的优先级要高于 * 号的,所以必须加上()来保证p先和 * 结合


🏞️&数组名VS数组名

我们在之前学习指针的时候知道arr是数组名,数组名表示数组首元素的地址,那&数组名是什么含义呢?

示例1:

int main()
{int arr[10] = { 0 };printf("%p\n", arr);printf("%p\n", &arr);return 0;
}

在这里插入图片描述
数组名和&数组名打印的地址竟然是一样的?难道它们没有一点区别嘛?我们继续往下看


示例2:

int main()
{int arr[10] = { 0 };printf("arr = %p\n", arr);printf("&arr= %p\n", &arr);printf("arr+1 = %p\n", arr + 1);printf("&arr+1= %p\n", &arr + 1);return 0;
}

在这里插入图片描述
&arr和arr,虽然值是一样的,但是意义应该不一样的

实际上: &arr 表示的是数组的地址,而不是数组首元素的地址。 &arr 的类型是: int(*)[10] ,是一种数组指针类型,数组的地址+1,跳过整个数组的大小,所以 &arr+1 相对于 &arr 的差值是40


⛰️数组指针的使用

int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,0 };int(*p)[10] = &arr;//把数组arr的地址赋值给数组指针变量preturn 0;
}

但是我们一般很少这样写代码,数组指针一般用于函数的使用


void print_arr(int(*arr)[5], int row, int col)
{int i = 0;for (i = 0; i < row; i++){for (int j = 0; j < col; j++){printf("%d ", arr[i][j]);}printf("\n");}
}
int main()
{int arr[3][5] = { 1,2,3,4,5,6,7,8,9,10 };//数组名arr,表示首元素的地址//但是二维数组的首元素是二维数组的第一行//所以这里传递的arr,其实相当于第一行的地址,是一维数组的地址//可以数组指针来接收print_arr(arr, 3, 5);return 0;
}

📝4. 数组参数、指针参数

🌞一维数组传参

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);
}

在一维数组传参中,以上传参都是可行的


🌙二维数组传参

void test1(int* arr)//ok?
{}
void test2(int* arr[5])//ok?
{}
void test3(int(*arr)[5])//ok?
{}
void test4(int** arr)//ok?
{}
void test5(int arr[3][5])//ok?
{}
void test6(int arr[][])//ok?
{}
void test7(int arr[][5])//ok?
{}
int main()
{int arr[3][5] = { 0 };test7(arr);
}

在这一堆传参中,只有6会传参失败,二维数组传参,函数形参的设计只能省略第一个[]的数字。
因为对一个二维数组,可以不知道有多少行,但是必须知道一行多少元素。这样才方便运算


⭐一级指针传参

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** ptr)
{printf("num = %d\n", **ptr);
}
int main()
{int n = 10;int* p = &n;int** pp = &p;test(pp);test(&p);return 0;
}

📖5. 总结

在探索C语言指针进阶的旅途中,我们一同跨越了从基础概念到高级应用的广阔天地。指针,作为C语言乃至许多其他编程语言中不可或缺的利器,其强大与灵活让我们能够深入操作系统的底层,高效管理内存,实现复杂的数据结构和算法。通过这一系列的探讨,我们不仅加深了对指针基本原理的理解,如指针的声明、初始化、运算以及如何通过指针访问变量,还深入学习了指针与数组、字符串等高级特性的结合应用,这些技能无疑为我们在编程道路上铺就了坚实的基石

然而,指针的进阶学习并非一蹴而就,它需要我们不断地实践、反思与总结。在这个过程中,我们可能会遇到各种挑战和困惑,但请记住,正是这些经历塑造了我们对编程更深层次的理解与感悟。保持好奇心,勇于探索未知,是每一位编程爱好者应当秉持的态度

在此,我们暂告一段落,但学习的脚步永不停歇。下节内容中,我们将继续深化对指针的理解,探索指针在函数参数传递、以及一些题目的思考。 让我们带着这份对技术的热爱与追求,继续前行,在C语言的海洋中乘风破浪,发现更多未知的精彩

在这里插入图片描述

希望本文能够为你提供有益的参考和启示,让我们一起在编程的道路上不断前行!
谢谢大家支持本篇到这里就结束了,祝大家天天开心!

在这里插入图片描述

这篇关于【C语言进阶】深入C语言指针:基础到进阶的跨越的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1106824

相关文章

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

Java进阶13讲__第12讲_1/2

多线程、线程池 1.  线程概念 1.1  什么是线程 1.2  线程的好处 2.   创建线程的三种方式 注意事项 2.1  继承Thread类 2.1.1 认识  2.1.2  编码实现  package cn.hdc.oop10.Thread;import org.slf4j.Logger;import org.slf4j.LoggerFactory

深入探索协同过滤:从原理到推荐模块案例

文章目录 前言一、协同过滤1. 基于用户的协同过滤(UserCF)2. 基于物品的协同过滤(ItemCF)3. 相似度计算方法 二、相似度计算方法1. 欧氏距离2. 皮尔逊相关系数3. 杰卡德相似系数4. 余弦相似度 三、推荐模块案例1.基于文章的协同过滤推荐功能2.基于用户的协同过滤推荐功能 前言     在信息过载的时代,推荐系统成为连接用户与内容的桥梁。本文聚焦于

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

科研绘图系列:R语言扩展物种堆积图(Extended Stacked Barplot)

介绍 R语言的扩展物种堆积图是一种数据可视化工具,它不仅展示了物种的堆积结果,还整合了不同样本分组之间的差异性分析结果。这种图形表示方法能够直观地比较不同物种在各个分组中的显著性差异,为研究者提供了一种有效的数据解读方式。 加载R包 knitr::opts_chunk$set(warning = F, message = F)library(tidyverse)library(phyl

透彻!驯服大型语言模型(LLMs)的五种方法,及具体方法选择思路

引言 随着时间的发展,大型语言模型不再停留在演示阶段而是逐步面向生产系统的应用,随着人们期望的不断增加,目标也发生了巨大的变化。在短短的几个月的时间里,人们对大模型的认识已经从对其zero-shot能力感到惊讶,转变为考虑改进模型质量、提高模型可用性。 「大语言模型(LLMs)其实就是利用高容量的模型架构(例如Transformer)对海量的、多种多样的数据分布进行建模得到,它包含了大量的先验

[MySQL表的增删改查-进阶]

🌈个人主页:努力学编程’ ⛅个人推荐: c语言从初阶到进阶 JavaEE详解 数据结构 ⚡学好数据结构,刷题刻不容缓:点击一起刷题 🌙心灵鸡汤:总有人要赢,为什么不能是我呢 💻💻💻数据库约束 🔭🔭🔭约束类型 not null: 指示某列不能存储 NULL 值unique: 保证某列的每行必须有唯一的值default: 规定没有给列赋值时的默认值.primary key:

【C++高阶】C++类型转换全攻略:深入理解并高效应用

📝个人主页🌹:Eternity._ ⏩收录专栏⏪:C++ “ 登神长阶 ” 🤡往期回顾🤡:C++ 智能指针 🌹🌹期待您的关注 🌹🌹 ❀C++的类型转换 📒1. C语言中的类型转换📚2. C++强制类型转换⛰️static_cast🌞reinterpret_cast⭐const_cast🍁dynamic_cast 📜3. C++强制类型转换的原因📝

【Linux 从基础到进阶】Ansible自动化运维工具使用

Ansible自动化运维工具使用 Ansible 是一款开源的自动化运维工具,采用无代理架构(agentless),基于 SSH 连接进行管理,具有简单易用、灵活强大、可扩展性高等特点。它广泛用于服务器管理、应用部署、配置管理等任务。本文将介绍 Ansible 的安装、基本使用方法及一些实际运维场景中的应用,旨在帮助运维人员快速上手并熟练运用 Ansible。 1. Ansible的核心概念