C语言指针与数组深入剖析及优化示例 指针解读 数组与指针的关系

ops/2024/12/24 11:03:11/
cle class="baidu_pl">
cle_content" class="article_content clearfix">
content_views" class="htmledit_views">

 说明:

         这是个人对该在Linux平台上的class="tags" href="/C.html" title=C>C语言学习网站笨办法学class="tags" href="/C.html" title=C>C上的每一个练习章节附加题的解析和回答

ex14:

  • 重新编写这些函数࿰c;使它们的数量减少。比如࿰c;你真的需要<code>can_print_itcode>吗?
     
    <code class="language-cs">if(isalpha(ch) || isblank(ch)) {printf("'%c' == %d ", ch, ch);
    }code>
  • 使用<code>strlencode>函数࿰c;让<code>print_argumentscode>知道每个字符串参数都有多长࿰c;之后将长度传入<code>print_letterscode>。然后重写<code>print_letterscode>࿰c;让它只处理固定的长度࿰c;不按照<code>'\0'code>终止符。你需要<code>#include <string.h>code>来实现它。
    <code class="language-cs">#include <stdio.h>
    #include <ctype.h>
    #include <string.h>// forward declarations
    int can_print_it(char ch);
    void print_letters(char arg[],char str_len);void print_arguments(int argc, char *argv[])
    {int i = 0;for(i = 0; i < argc; i++) {print_letters(argv[i],strlen(argv[i]));}
    }void print_letters(char arg[],char str_len)
    {int i = 0;for(i = 0; i < str_len; i++) {char ch = arg[i];if(isalpha(ch) || isblank(ch)) {printf("'%c' == %d ", ch, ch);}}printf("\n");
    }int main(int argc, char *argv[])
    {print_arguments(argc, argv);return 0;
    }
    code>
  • 使用<code>mancode>来查询<code>isalphacode>和<code>isblankcode>的信息。使用其它相似的函数来只打印出数字或者其它字符。


    其他类似函数:
    isdigit: 检查字符是否为数字(0-9)。
    ispunct: 检查字符是否为标点符号。
    isxdigit: 检查字符是否为十六进制数字。
    isalnum: 检查字符是否为字母或数字。
    iscntrl: 检查字符是否为控制字符。
    <code class="language-cs">#include <stdio.h>
    #include <ctype.h>
    #include <string.h>// Function prototypes
    void print_arguments(int argc, char *argv[]);
    void print_letters(const char *arg);
    void print_digits(const char *arg);
    void print_punctuation(const char *arg);void print_arguments(int argc, char *argv[]) {for (int i = 0; i < argc; i++) {printf("Argument %d: %s\n", i, argv[i]);printf("Letters: ");print_letters(argv[i]);printf("Digits: ");print_digits(argv[i]);printf("Punctuation: ");print_punctuation(argv[i]);printf("\n");}
    }void print_letters(const char *arg) {for (int i = 0; arg[i] != '\0'; i++) {char ch = arg[i];if (isalpha(ch) || isblank(ch)) {printf("'%c' == %d ", ch, ch);}}printf("\n");
    }void print_digits(const char *arg) {for (int i = 0; arg[i] != '\0'; i++) {char ch = arg[i];if (isdigit(ch)) {printf("'%c' == %d ", ch, ch);}}printf("\n");
    }void print_punctuation(const char *arg) {for (int i = 0; arg[i] != '\0'; i++) {char ch = arg[i];if (ispunct(ch)) {printf("'%c' == %d ", ch, ch);}}printf("\n");
    }int main(int argc, char *argv[]) {print_arguments(argc, argv);return 0;
    }
    code>
  • 上网浏览不同的人喜欢什么样的函数格式。永远不要使用“K&R”语法࿰c;因为它过时了࿰c;而且容易使人混乱࿰c;但是当你碰到一些人使用这种格式时࿰c;要理解代码做了什么。

          旧的“K&R”函数定义形式(Kernighan and Ritchie风格)࿰c;来源于class="tags" href="/C.html" title=C>C语言的初始版本࿰c;在ANSI class="tags" href="/C.html" title=C>C(1989年)现代风格之前非常流行。这种形式在现代class="tags" href="/C.html" title=C>C编程中已经过时࿰c;但仍然可以在一些老旧的代码中看到。它的特点是函数参数的类型声明放在函数体之前࿰c;单独列出c;而不是在函数头部直接定义参数及其类型。
    <code class="language-cs">int sum(a, b)
    int a;  // 参数类型声明
    int b;  // 参数类型声明
    {return a + b;
    }
    code>

    ex15:

  • 一些指针的疑问总结:

    指针的类型和不同级数的异同点?
    所有的指针(无论几级指针都是一个存储指针地址的变量࿰c;都是只有操作系统的位数来决定的࿰c;要么是64位要么是32位)࿰c;而指针的类型(例如:int * ࿰c;char *)决定了指针每次读取数据的内存地址偏移量
    例子:
    <code class="language-cs">#include <stdio.h>int main() {char arr_char[] = "Hello"; int arr_int[] = {10, 20, 30, 40};  char *p_char = arr_char;int *p_int = arr_int;// 输出原始地址printf("Original char pointer address: %p\n", p_char);printf("Original int pointer address: %p\n", p_int);// 输出加 1 后的地址printf("char pointer after p_char + 1: %p\n", p_char + 1);//偏移量1printf("int pointer after p_int + 1: %p\n", p_int + 1);//偏移量4return 0;
    }
    /*输出
    Original char pointer address: 0x7ffeeef25a50
    Original int pointer address: 0x7ffeeef25a60
    char pointer after p_char + 1: 0x7ffeeef25a51
    int pointer after p_int + 1: 0x7ffeeef25a64
    */code>

    <code>有关指针*code> 和 <code>&code> 操作符解释:
    * 是解引用操作符࿰c;用于访问指针指向的值。解引用一个一级指针时࿰c;访问的是指针指向的数据;解引用一个二级指针时࿰c;先访问一级指针࿰c;再访问一级指针指向的数据。
    & 是取地址操作符࿰c;用于获取变量的地址。当你对一个变量应用 & 时࿰c;编译器会根据变量的类型生成指针࿰c;并保存该指针的类型信息。

    编译器如何在内存中区分不同级别指针?
    一级指针(例如 int *ptr)会在内存中存储一个指向 int 类型数据的地址。
    二级指针(例如 int **pptr)会在内存中存储一个指向一级指针的地址࿰c;访问时会先取出一级指针࿰c;再使用一级指针去访问实际数据。
    <code class="language-cs">int x = 5;
    int *p1 = &x;   // p1 是一级指针࿰c;存储 x 的地址
    int **p2 = &p1;  // p2 是二级指针࿰c;存储 p1 的地址
    /*内存表示
    x = 5
    p1 -> 地址1    (指向 x)
    p2 -> 地址2    (指向 p1)
    */code>

    当我们解引用 <code>p2code> 时࿰c;首先得到 <code>p1code> 的值(即 <code>&xcode>)࿰c;然后通过 <code>p1code> 访问 <code>xcode>。

  • 再次解读一个非常好的例子:

    <code class="language-cs">#include <stdio.h>int main(){int int_var = 16909060; // 0x01020304 in memory//16909060 对应的十六进制是 0x01020304。在小端模式内存中࿰c;int_var 会以4个字节存储这个值(0x04 0x03 0x02 0x01࿰c;从低地址到高地址)char *char_ptr = (char *)&int_var;printf("char_ptr[0]: %d\n", char_ptr[0]); // 输出最低字节printf("char_ptr[1]: %d\n", char_ptr[1]); // 输出次低字节/*上述的char_ptr[0]其实就是是指针数组的索引操作࿰c;实际上是访问char_ptr指向数组的第二个元素而char_ptr指向int_var的指针࿰c;而int_var被强制转化为char *型指针(偏移量为1)即int_var在内存中其实相当于四个元素的数组࿰c;第一个元素为0x04࿰c;第二个为0x03...char_ptr[0]也相当于对char_ptr的解引用:*char_ptr,故char_ptr[1]等价于*(char_ptr+1)*/return 0;
    }
    code>

    根据以上分析可以推断出输出结果为:

    <code class="language-cs">char_ptr[0]: 4
    char_ptr[1]: 3code>

    指针词库

    现在我打算向你提供一个词库࿰c;用于读写指针。当你遇到复杂的指针语句时࿰c;试着参考它并且逐字拆分语句(或者不要使用这个语句࿰c;因为有可能并不好):

    <code>type *ptrcode>

    <code>typecode>类型的指针࿰c;名为<code>ptrcode>。

    <code>*ptrcode>

    <code>ptrcode>所指向位置的值。

    <code>*(ptr + i)code>

    (<code>ptrcode>所指向位置加上<code>icode>)的值。

    ckquote>

    注:以字节为单位的话࿰c;应该是<code>ptrcode>所指向的位置再加上<code>sizeof(type) * icode>。

    ckquote>

    <code>&thingcode>

    <code>thingcode>的地址。

    <code>type *ptr = &thingcode>

    名为<code>ptrcode>࿰c;<code>typecode>类型的指针࿰c;值设置为<code>thingcode>的地址。

    <code>ptr++code>

    自增<code>ptrcode>指向的位置。
     

  • 使用访问指针的方式重写所有使用数组的地方。

    将原来使用 <code>ages[i]code> 的地方࿰c;可以通过 <code>*(ages + i)code> 来访问。
     
  • 使用访问数组的方式重写所有使用指针的地方。

    将原来使用 <code>*(names + i)code> 的地方࿰c;可以通过 <code>names[i]code> 来访问。
     
  • 使用指针来处理命令行参数࿰c;就像处理<code>namescode>那样。


    printf("Argument %d: %s\n", i, *(argv + i));

     
  • 在程序末尾添加一个<code>forcode>循环࿰c;打印出这些指针所指向的地址。你需要在<code>printfcode>中使用<code>%pcode>。
     
    <code class="language-cs">// 打印指针所指向的地址
    void print_addresses(int *ages, char **names, int count)
    {int i = 0;while (i < count) {printf("Address of names[%d]: %p, Address of ages[%d]: %p\n", i, &names[i], i, &ages[i]);i++;}
    }code>
  • 对于每一种打印数组的方法࿰c;使用函数来重写程序。试着向函数传递指针来处理数据。记住你可以声明接受指针的函数࿰c;但是可以像数组那样用它。
     
    <code class="language-cs">#include <stdio.h>// 函数声明:打印年龄和名字
    void print_using_pointers(int *ages, char **names, int count);
    void print_using_arrays(int *ages, char **names, int count);
    void print_using_pointers_from_argv(char **argv, int argc);
    void print_addresses(int *ages, char **names, int count);int main(int argc, char *argv[])
    {// 创建数组int ages[] = {23, 43, 12, 89, 2};char *names[] = {"Alan", "Frank", "Mary", "John", "Lisa"};// 获取数组的元素个数int count = sizeof(ages) / sizeof(int);// 使用指针的方式打印print_using_pointers(ages, names, count);printf("---\n");// 使用数组的方式打印print_using_arrays(ages, names, count);printf("---\n");// 使用指针处理命令行参数print_using_pointers_from_argv(argv, argc);printf("---\n");// 打印指针所指向的地址print_addresses(ages, names, count);return 0;
    }// 使用指针方式打印
    void print_using_pointers(int *ages, char **names, int count)
    {int i = 0;while (i < count) {printf("%s is %d years old.\n", *(names + i), *(ages + i));i++;}
    }// 使用数组方式打印
    void print_using_arrays(int *ages, char **names, int count)
    {int i = 0;while (i < count) {printf("%s has %d years alive.\n", names[i], ages[i]);i++;}
    }// 使用指针处理命令行参数
    void print_using_pointers_from_argv(char **argv, int argc)
    {int i = 0;while (i < argc) {printf("Argument %d: %s\n", i, *(argv + i));i++;}
    }// 打印指针所指向的地址
    void print_addresses(int *ages, char **names, int count)
    {int i = 0;while (i < count) {printf("Address of names[%d]: %p, Address of ages[%d]: %p\n", i, (void*)&names[i], i, (void*)&ages[i]);i++;}
    }
    code>
  • 将<code>forcode>循环改为<code>whilecode>循环࿰c;并且观察对于每种指针用法哪种循环更方便。

 

 


http://www.ppmy.cn/ops/144549.html

相关文章

Matlab个性化绘图第6期—带标记面的三维折线图

带标记面的三维折线图本质上就是多组折线图&#xff1a; Matlab论文插图绘制模板第92期—折线图&#xff08;Plot&#xff09; 或者三维折线图&#xff1a; Matlab论文插图绘制模板第37期—三维折线图(plot3) 不同之处在于带标记面的三维折线图把每一组数据单独放在一个三维平…

【多模态聚类】用于无标记视频自监督学习的多模态聚类网络

Multimodal Clustering Networks for Self-supervised Learning from Unlabeled Videos 用于无标记视频自监督学习的多模态聚类网络 0.论文摘要 多模态自监督学习越来越受到关注&#xff0c;因为它不仅允许在没有人工监督的情况下训练大型网络&#xff0c;还允许跨各种模态搜索…

致命错误: Class ‘ZipArchive‘ not found

银河麒麟V10处理 本人在安装过程遇到的坑&#xff0c;就是不要使用太低版本的 1、安装cmake 确认是否安装 cmake --version 如果没安装的话按照如下步骤处理下&#xff08;如果想要其他版本点击cmake官网下载&#xff09; wget https://github.com/Kitware/CMake/release…

基于DockerCompose搭建Redis主从哨兵模式

linux目录结构 内网配置 哨兵配置文件如下&#xff0c;创建3个哨兵配置文件 # sentinel26379.conf sentinel26380.conf sentinel26381.conf 内容如下 protected-mode no sentinel monitor mymaster redis-master 6379 2 sentinel down-after-milliseconds mymaster 60000 s…

利用Java爬虫获取苏宁易购商品详情

在数字化时代&#xff0c;电商平台的商品信息对于市场分析、价格监控和消费者决策至关重要。苏宁易购作为中国领先的电商平台之一&#xff0c;提供了丰富的商品信息。本文将介绍如何使用Java语言开发爬虫&#xff0c;获取苏宁易购商品的详细信息。 Java爬虫技术简介 Java作为一…

ECharts中通过饼图(type为pie)绘制出仪表盘进度条

在ECharts中&#xff0c;可以通过多个饼图系列&#xff08;series&#xff09;来实现仪表盘形式的进度条&#xff0c;如下图&#xff0c;需要通过以下几个饼图组合来完成。 一个饼图用于进度条背景底色&#xff08;未完成部分&#xff09;&#xff1b;一个饼图用于进度条颜色&…

基于JAVA_JSP电子书下载系统的设计与实现【源码+文档+部署讲解】

目 录 第1章 绪论 课题的研究背景、内容和意义 第2章 主要技术概述 2.1 B/S结构 2.2 JSP技术 2.2.1 JSP技术的强势 2.2.2 JSP技术的弱势 2.3 SQL Server 2000数据库 2.4 JDBC数据库连接 2.4.1 JDBC接口 2.4.2 JDBC的驱动程序 2.5 TOMCAT应用服务器 第3章 需求分…

SpringBoot如何实现缓存预热?

缓存预热是指在 Spring Boot 项目启动时&#xff0c;预先将数据加载到缓存系统&#xff08;如 Redis&#xff09;中的一种机制。 那么问题来了&#xff0c;在 Spring Boot 项目启动之后&#xff0c;在什么时候&#xff1f;在哪里可以将数据加载到缓存系统呢&#xff1f; 实现…