迈向熟悉C语言:C 语言指针和数组进阶(学习笔记)

 

目录

1.前言

2.数组进阶

2.1 数组名含义

2.2 数组下标运算符[ ]

2.3 字符串常量

2.4 特殊数组

2.4.1 零长数组

2.4.2 变长数组

3.指针进阶 

3.1 指针语法剖析

3.2 特殊指针

3.2.1 char型指针

3.2.2 多级指针

3.2.3 void型指针

3.2.4 const型指针

3.2.5 函数指针

4.练习题 

低难度

题目 1:指针基础操作

题目 2:数组元素求和

题目 3:字符串长度计算

中难度

题目 4:交换数组元素

题目 5:二维数组指针操作

题目 6:动态数组创建与操作

高难度

题目 7:指针数组排序

题目 8:多级指针与动态二维数组

题目 9:函数指针应用

5.总结


数组和指针初阶的文章可查看编程小白进阶秘籍:C 语言指针和数组初阶全解析(值得反复观看)

1.前言

在讲 C 语言指针和数组进阶内容前,首先强调一遍,C 语言的核心内容是指针、数组和内存管理,它们之间的联系才是 C 语言真正的灵魂,而要真正做到从 “了解 C” 迈向 “熟悉 C”,便得掌握数组和指针的内容。对于这部分内容而言,我们之前所学的只是基础,在实际的开发场景中,这些基础内容很多时候难以直接派上用场,那些只适合做些小程序,效率不高而且代码量大。

那么我们该如何学习这块内容呢?首先,我们必须得多虚拟画图,从手绘到脑绘是一个过程,尤其是遇到一些一时间不好口述的情况,这时画图往往可以让你找到答案(本人在学习过程中也是跟着我的老师一步步进行图文结合来学习的)。然后是做一些相关的题目,提升你对这块内容的印象,以及深度去思考指针、数组和内存之间的联系。

接下来,我们便正式进入本章关于数组和指针的学习,成为一名 “熟悉 C” 的程序员。

2.数组进阶

2.1 数组名含义

数组名在C语言程序中出现时,有两种含义

1) 代表整个数组

2) 代表数组首元素的地址

以下数组名出现时,代表整个数组的情况有:

1) 在数组定义中

2) 在 sizeof 运算表达式中

3) 在取址符&中(前面讲过)

示例代码:

#include <stdio.h>
int main(int argc, char const *argv[])
{
    // (1)、以下数组名出现时,代表整个数组的情况有
    // 1、在数组定义中
    int buf1[5] = {0};    
    // 数组在内存中不能随便移动位置(数组名不能进行加减操作:buf1=buf+1)(数组名相当于一个常量指针)

    // 2、在 sizeof 运算表达式中
    printf("数组buf1的大小 == %lu\n", sizeof(buf1));
    /*
        解析:
            sizeof是将其当成一个指针来对待吗??
            如果将其当成一个指针来对待的话,那么sizeof(buf1)的大小就应该为系统位数的大小
            所以,sizeof(buf1)其实是算这个buf1数组的大小,而非指针的大小
    */

    // 3、在取址符&中
    printf("数组buf1的首元素的地址   == %p\n", buf1) ;
    printf("数组buf1的首元素的地址+1 == %p\n", buf1+1);
    /*
        解析:
            数组buf1的首元素的地址   == 0x7ffe387f2510
            数组buf1的首元素的地址+1 == 0x7ffe387f2514

        地址移动了4个位置,所以是4个字节,所以其buf1的作用
        范围是4个字节(数组元素的作用范围)
    */

    printf("整个数组buf1的地址   == %p\n", &buf1) ;
    printf("整个数组buf1的地址+1 == %p\n", &buf1+1);
    /*
        解析:
            整个数组buf1的地址   == 0x7ffc202c2650
            整个数组buf1的地址+1 == 0x7ffc202c2664

        地址移动了20个位置,所以是20个字节,所以其&buf1的
        作用范围是20个字节(整个数组的作用范围)
    */
    
    return 0;

}

以下数组名出现时,代表数组首元素的地址的情况有:

1) 函数传参时

2) 指针指向该数组首元素的地址时

3) 使用scanf函数

4) 直接使用数组名(前面讲过)

示例代码:

#include <stdio.h>

// 函数传参时,数组代表其首元素地址(相当于一个指针)
// 传参,不论是指针还是数组,都会将其当成指针来对待
void upper_case(char *p)    // char *p = str;   
{
    printf("sizeof(p)    == %lu\n", sizeof(p));
    printf("sizeof(p[0]) == %lu\n", sizeof(p[0]));

    int step = 'a' - 'A';                           // 32    a-32 == 'A'
       
// sizeof(p)    == sizeof(指针):         由系统位数决定(32位:4个字节 64位:8个字节)
// sizeof(p[0]) == sizeof(数组里面的元素): 1个字节(char型数组元素)
    for(int i = 0; i<sizeof(p)/sizeof(p[0]); i++)
    {
        if((p[i] >='a') && (p[i]<='z') )            // 判断其是否是小写字符
            p[i] -= step;                           // 转换小写变为大写
    }
}

int main()
{
    // (2)- 当出现其他情形时,数组代表其首元素地址。
    // 1、指针指向该数组首元素的地址时
    int buf2[128] = {0};
    int *p1       = buf2;
    int *p2       = &buf2[0];

    printf("数组buf2的首元素地址      == %p\n", buf2);
    printf("数组buf2的首元素地址+1    == %p\n", buf2+1);

    printf("p1的变量里面存放的地址     == %p\n", p1);
    printf("p1的变量+1后里面存放的地址 == %p\n", p1+1);

    printf("p2的变量里面存放的地址     == %p\n", p2);
    printf("p2的变量+1后里面存放的地址 == %p\n", p2+1);
    /*
        解析:
            数组buf2的首元素地址      == 0x7ffda56858d0
            数组buf2的首元素地址+1    == 0x7ffda56858d4

            p1的变量里面存放的地址     == 0x7ffda56858d0
            p1的变量+1后里面存放的地址 == 0x7ffda56858d4

            p2的变量里面存放的地址     == 0x7ffda56858d0
            p2的变量+1后里面存放的地址 == 0x7ffda56858d4
        
        地址一样,且+1后移动的地址也相同,所以其作用范围都一致

    */

    // 2、函数传参时
    char str[] = "abcdefghijklnmopqrstuvwxyz";
    printf("原数组:%s\n", str);
    upper_case(str);         
    // 传进去的数组,在另一个函数中,由一个相同类型的指针变量,将其数组的首元素地址获取了,后续计算的只是指针变量,而非数组。
    printf("转换后:%s\n", str);   
    
    /*
        解析:
            原数组:abcdefghijklnmopqrstuvwxyz
            转换后:ABCDEFGHijklnmopqrstuvwxyz  

        此处打印出来的数据是只有前8个字符做了改变,而其它字符没有改
        说明函数里的:sizeof(p)/sizeof(p[0]),算的p变量不是整个数组的大小,而是指针的大小
    */


    // 3、使用scanf函数
    char buf3[128] = {0};
    printf("请输入你要输入的数据:\n");
    scanf("%s", buf3);                  
    // buf3表示的是数组的首元素的地址(&buf3表示就是整个数组的地址,切记不要写错了)
    // 由于buf3是首地址,所以不用加&,直接使用buf3

    // 4、直接使用数组名
    char buf4[128] = "来一根超级棒棒糖";
    printf("数组buf4的首地址      == %p\n", buf4);      // 将数组的首元素的地址给打印出来
    printf("数组buf4的首地址+1    == %p\n", buf4+1);

    printf("buf4数组里面的字符串  == %s\n", buf4);      // 把数组里面的内存给打印出来
    
    return 0;
}
  • 重点信息:
  • 指针的大小取决于系统位数
  • 传参时,传的是数组的首地址,函数可以通过这个地
内容概要:本文档详细介绍了在三台CentOS 7服务器(IP地址分别为192.168.0.157、192.168.0.158192.168.0.159)上安装配置Hadoop、Flink及其他大数据组件(如Hive、MySQL、Sqoop、Kafka、Zookeeper、HBase、Spark、Scala)的具体步骤。首先,文档说明了环境准备,包括配置主机名映射、SSH免密登录、JDK安装等。接着,详细描述了Hadoop集群的安装配置,包括SSH免密登录、JDK配置、Hadoop环境变量设置、HDFSYARN配置文件修改、集群启动与测试。随后,依次介绍了MySQL、Hive、Sqoop、Kafka、Zookeeper、HBase、Spark、ScalaFlink的安装配置过程,包括解压、环境变量配置、配置文件修改、服务启动等关键步骤。最后,文档提供了每个组件的基本测试方法,确保安装成功。 适合人群:具备一定Linux基础大数据组件基础知识的运维人员、大数据开发工程师以及系统管理员。 使用场景及目标:①为大数据平台搭建提供详细的安装指南,确保各组件能够顺利安装配置;②帮助技术人员快速掌握Hadoop、Flink等大数据组件的安装与配置,提升工作效率;③适用于企业级大数据平台的搭建与维护,确保集群稳定运行。 其他说明:本文档不仅提供了详细的安装步骤,还涵盖了常见的配置项解释故障排查建议。建议读者在安装过程中仔细阅读每一步骤,并根据实际情况调整配置参数。此外,文档中的命令配置文件路径均为示例,实际操作时需根据具体环境进行适当修改。
在无线通信领域,天线阵列设计对于信号传播方向覆盖范围的优化至关重要。本题要求设计一个广播电台的天线布局,形成特定的水平面波瓣图,即在东北方向实现最大辐射强度,在正东到正北的90°范围内辐射衰减最小且无零点;而在其余270°范围内允许出现零点,且正西西南方向必须为零。为此,设计了一个由4个铅垂铁塔组成的阵列,各铁塔上的电流幅度相等,相位关系可自由调整,几何布置间距不受限制。设计过程如下: 第一步:构建初级波瓣图 选取南北方向上的两个点源,间距为0.2λ(λ为电磁波波长),形成一个端射阵。通过调整相位差,使正南方向的辐射为零,计算得到初始相位差δ=252°。为了满足西南方向零辐射的要求,整体相位再偏移45°,得到初级波瓣图的表达式为E1=cos(36°cos(φ+45°)+126°)。 第二步:构建次级波瓣图 再选取一个点源位于正北方向,另一个点源位于西南方向,间距为0.4λ。调整相位差使西南方向的辐射为零,计算得到相位差δ=280°。同样整体偏移45°,得到次级波瓣图的表达式为E2=cos(72°cos(φ+45°)+140°)。 最终组合: 将初级波瓣图E1次级波瓣图E2相乘,得到总阵的波瓣图E=E1×E2=cos(36°cos(φ+45°)+126°)×cos(72°cos(φ+45°)+140°)。通过编程实现计算并绘制波瓣图,可以看到三个阶段的波瓣图分别对应初级波瓣、次级波瓣总波瓣,最终得到满足广播电台需求的总波瓣图。实验代码使用MATLAB编写,利用polar函数在极坐标下绘制波瓣图,并通过subplot分块显示不同阶段的波瓣图。这种设计方法体现了天线阵列设计的基本原理,即通过调整天线间的相对位置相位关系,控制电磁波的辐射方向强度,以满足特定的覆盖需求。这种设计在雷达、卫星通信移动通信基站等无线通信系统中得到了广泛应用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值