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

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

 说明:

         这是个人对该在Linux平台上的C语言学习网站笨办法学C上的每一个练习章节附加题的解析和回答

ex14:

  • 重新编写这些函数,使它们的数量减少。比如,你真的需要can_print_it吗?
     
    if(isalpha(ch) || isblank(ch)) {printf("'%c' == %d ", ch, ch);
    }
  • 使用strlen函数,让print_arguments知道每个字符串参数都有多长,之后将长度传入print_letters。然后重写print_letters,让它只处理固定的长度,不按照'\0'终止符。你需要#include <string.h>来实现它。
    #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;
    }
    
  • 使用man来查询isalphaisblank的信息。使用其它相似的函数来只打印出数字或者其它字符。


    其他类似函数:
    isdigit: 检查字符是否为数字(0-9)。
    ispunct: 检查字符是否为标点符号。
    isxdigit: 检查字符是否为十六进制数字。
    isalnum: 检查字符是否为字母或数字。
    iscntrl: 检查字符是否为控制字符。
    #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;
    }
    
  • 上网浏览不同的人喜欢什么样的函数格式。永远不要使用“K&R”语法,因为它过时了,而且容易使人混乱,但是当你碰到一些人使用这种格式时,要理解代码做了什么。

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

    ex15:

  • 一些指针的疑问总结:

    指针的类型和不同级数的异同点?
    所有的指针(无论几级指针都是一个存储指针地址的变量,都是只有操作系统的位数来决定的,要么是64位要么是32位),而指针的类型(例如:int * ,char *)决定了指针每次读取数据的内存地址偏移量
    例子:
    #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
    */

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

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

    当我们解引用 p2 时,首先得到 p1 的值(即 &x),然后通过 p1 访问 x

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

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

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

    char_ptr[0]: 4
    char_ptr[1]: 3

    指针词库

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

    type *ptr

    type类型的指针,名为ptr

    *ptr

    ptr所指向位置的值。

    *(ptr + i)

    ptr所指向位置加上i)的值。

    注:以字节为单位的话,应该是ptr所指向的位置再加上sizeof(type) * i

    &thing

    thing的地址。

    type *ptr = &thing

    名为ptrtype类型的指针,值设置为thing的地址。

    ptr++

    自增ptr指向的位置。
     

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

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

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


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

     
  • 在程序末尾添加一个for循环,打印出这些指针所指向的地址。你需要在printf中使用%p
     
    // 打印指针所指向的地址
    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++;}
    }
  • 对于每一种打印数组的方法,使用函数来重写程序。试着向函数传递指针来处理数据。记住你可以声明接受指针的函数,但是可以像数组那样用它。
     
    #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++;}
    }
    
  • for循环改为while循环,并且观察对于每种指针用法哪种循环更方便。

 

 


http://www.mrgr.cn/news/80620.html

相关文章:

  • 第十五章 Linux Shell 编程
  • stable diffusion学习01
  • 【jpa】springboot使用jpa示例
  • 天天 AI-241215:今日热点-OpenAI发布ChatGPT Projects,万能工具箱上线!
  • 无人机故障安全模式设计逻辑与技术!
  • qemu源码解析【02】qom基本概念
  • vue3前端组件库的搭建与发布(一)
  • 什么是动态网站 ,有哪些特点
  • abc 384 D(子数组->前缀和) +E(bfs 扩展的时候 按照数值去扩展)
  • 程序的基本结构
  • Android 10.0 adb install执行安装过程分析二
  • Linux(一次性和周期性任务cron)
  • 51c嵌入式~合集3
  • unique_ptr 智能指针
  • 【C++】抽象之神:类和对象(中)万字详解
  • 【深入了解MySQL】优化查询性能与数据库设计的深度总结
  • SCAU期末笔记 - Linux系统应用与开发教程样卷解析(2024版)
  • java全栈day16--Web后端实战(数据库)
  • BGP协议
  • SimAI万卡集群模拟器,LLM大模型训练通信计算模拟
  • C++ __attribute__((constructor))使用介绍
  • LearnOpenGL学习(高级OpenGL - - 实例化,抗锯齿)
  • 计算机网络-网络层
  • c++:STL:string
  • Pytorch | 从零构建GoogleNet对CIFAR10进行分类
  • Eureka学习笔记-服务端