c语言简单编程练习9

1、输入一个整数,判断它是否为回文数

例:输入:12321 输出:该数是回文数 输入:12323 输出:该数不是回文数

cpp 复制代码
#include <stdio.h>
int huiwenshu(int num)
{
    int a[100];
    int i, n, j, k;
    i = 0;
    n = 0;
    while(num)   //将整数存入数组 1234 -> a[100]={4,3,2,1};
    {
        n = num%10;
        a[i] = n;
        num /= 10;
        n = 0;
        i++;
    }
    k = i-1;  //找到最后一个数的下标
    for(j=0;j<(i-1)/2;j++)  //第一个与最后一个比较,从两边向中间依次比较
    {
        if(a[j]==a[k])
            k--;
        else
            return 0;
    }
    return 1;
}
int main(int argc, char *argv[])
{
    int n, m;
    printf("请输入一个整数:");
    scanf("%d",&n);
    m = huiwenshu(n);
    if(m==1)
    {
        printf("该数是回文数\n");
    }
    else
        printf("该数不是回文数\n");
    return 0;
}

解析: 在之前我们判断的是回文字符串,字符串我们是可以通过指针指向这个字符串,然后直接把最后一个字符与第一个字符比较,依次比较即可;但是这里需要判断的是回文数,这是一个整数我们并不能直接使用指针来比较它,所以我先使用了一个数组将整数的每一位数都存入了数组,然后通过调用数组里的值进行比较判断;

这里存整数是通过除以十取余数的方式来存储,将整数除以十,得到的余数存入数组第一个元素的位置,然后得到这个数除以十后的商,用商再除以十取余得到整数十位上的数,存入数组第二个元素的位置,依次存储,最后在数组中得到的就是整数反过来读的一组数字,例如1234,在数组中就是4321;虽然是倒过来的但是并不影响我们判断它是否为回文数;

得到数组后我们直接使用一个循环来进行比较,循环的条件跟整数的位数相关,也就是跟数组中的元素个数相关,j=0;j<(i-1)/2;j++;j从0开始,到小于元素个数的一半即可,这样就能保证比较完;在这里我把判断回文数写为了一个函数,当比较到数字不相同就返回0(表示输入的整数不是回文数),要是循环能完成就返回1;这样通过判断函数的返回值就能知道输入的整数是不是回文数。

2、函数的多文件封装

多文件封装的优点:

  1. 模块化
    • 将代码拆分成多个文件,每个文件负责特定的功能或模块,可以使代码结构更加清晰和易于管理。
    • 模块化有助于减少代码之间的依赖,使得代码更易于维护和扩展。
  2. 可读性
    • 将函数和类分散到多个文件中,每个文件专注于一个特定的任务或功能,可以提高代码的可读性。
    • 开发者可以更容易地找到和理解所需的代码部分,而不必在大量代码中搜索。
  3. 可重用性
    • 通过将常用功能封装到独立的文件中,可以更容易地在多个项目或模块中重用这些功能。
    • 这有助于减少代码重复,提高开发效率。
  4. 代码维护
    • 当需要修改或更新代码时,模块化结构使得修改更加局部化,减少了引入错误的风险。
    • 独立的文件也更便于版本控制和代码审查。
  5. 编译和加载效率
    • 在一些编程语言中,如C或C++,将代码拆分成多个文件可以优化编译过程,因为编译器可以只重新编译那些发生变化的文件。
    • 对于大型项目,这可以显著减少编译时间。
    • 在一些动态语言(如Python)中,将代码拆分成多个模块也可以提高模块的加载效率,因为模块只会在需要时被加载。
  6. 团队协作
    • 模块化结构使得多个开发者可以并行工作在不同的模块上,减少了代码冲突和合并问题的可能性。
    • 这有助于加快开发进度,提高团队协作效率。
  7. 命名空间管理
    • 将函数和类封装到不同的文件中,可以避免命名冲突,特别是当使用全局变量或函数时。
    • 通过命名空间(如Python中的模块、Java中的包)来组织代码,可以提高代码的安全性和可维护性。
  8. 代码封装和隐藏实现细节
    • 将实现细节封装在单独的文件中,并通过公共接口(如API)暴露必要的功能,可以隐藏内部实现细节,提高代码的封装性和安全性。
    • 这有助于减少外部代码对内部实现的依赖,使得内部实现更容易进行更改和优化。

函数的多文件封装通常分为三个文件:自定义头文件、功能函数文件、测试文件(主函数文件)

自定义头文件里面放的是库头文件(用<>引用)和我们自定义的头文件(用双引号引用)

< >和" "的区别:

尖括号< >

  • 用途:主要用于引用标准库头文件或安装在系统级别的第三方库头文件。
  • 查找路径:编译器会直接在它的默认包含目录中查找头文件。这些目录通常由编译器预定义或通过编译选项

双引号"

  • 用途:主要用于引用项目特定或自定义的头文件,例如你自己写的头文件或者与项目紧密相关的第三方库头文件。
  • 查找路径:编译器会首先在源文件所在的目录进行查找,然后才会去编译器默认的包含目录中查找。这种方式允许开发者更灵活地管理文件位置,特别是在项目结构复杂时。

下面我将题1的代码来做多文件封装,便于宝子们对照

自定义头文件:

cpp 复制代码
#ifndef _HUIWEN_H
#define _HUIWEN_H

#include <stdio.h>
int huiwenshu(int num);

#endif

头文件的格式就是头两行为**#ifndef _HUIWEN_H** (如果没有定义huiwen.h这个头文件)和**#define _HUIWEN_H** (定义huiwen.h这个头文件),最后加一行**#endif**;中间要写的就是所需要头文件的引用和我们自己功能函数的声明

功能函数文件:

cpp 复制代码
#include "huiwen.h"
int huiwenshu(int num)
{
    int a[100];
    int i, n, j, k;
    i = 0;
    n = 0;
    while(num)   //将整数存入数组 1234 -> a[100]={4,3,2,1};
    {
        n = num%10;
        a[i] = n;
        num /= 10;
        n = 0;
        i++;
    }
    k = i-1;  //找到最后一个数的下标
    for(j=0;j<(i-1)/2;j++)  //第一个与最后一个比较,从两边向中间依次比较
    {
        if(a[j]==a[k])
            k--;
        else
            return 0;
    }
    return 1;
}

在功能函数文件里面我们首先需要引用我们自定义的头文件 #include "huiwen.h",之后就是需要实现功能的函数,这里面可以放多个功能函数,同时就需要在我们自定义的头文件里面声明所有的功能函数。

测试文件(主函数文件):

cpp 复制代码
#include "huiwen.h"
int main(int argc, char *argv[])
{ 
    int n, m;
    printf("请输入一个整数:");
    scanf("%d",&n);
    m = huiwenshu(n);
    if(m==1)
    {
        printf("该数是回文数\n");
    }
    else
        printf("该数不是回文数\n");
    return 0;
} 

在测试文件里面我们也需要先引用我们自定义的头文件,之后就是通过调用函数来实现相应的功能。

注意:除了我们的测试文件(主函数文件) 之外,其他两个文件都不能有主函数int main()。

3、 静态存储(static)

作用:

  1. 在函数体内定义静态变量

    • 记忆功能:被声明为static的局部变量在这一函数被调用的过程中其值维持不变,即函数返回后,该变量的值不会丢失,下次该函数被调用时,该变量将保持上次函数执行结束时的值。
    • 存储位置:静态局部变量存储在全局数据区(静态区),而非栈区。
    • 生命周期:静态局部变量的生命周期贯穿整个程序运行期间,但其作用域仍然是局部的,即只能在定义它的函数内部访问。
  2. 在模块内(函数体外)定义静态变量

    • 作用域限制:静态全局变量只能被本模块内的函数访问,而不能被模块外的其他函数访问。它相当于一个本地的全局变量。
    • 存储位置:同样存储在全局数据区。
    • 初始化:静态全局变量只初始化一次,防止在其他文件单元中被引用。
  3. 定义静态函数

    • 作用域限制:静态函数只能在本源文件中使用,不能被其他源文件调用。这有助于实现信息隐藏和模块化编程。

记忆功能,我也把它称为延长生命周期(不是延长作用域!!!),下面我用一个例子来说明:

cpp 复制代码
#include <stdio.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
p:{
    int a;
    a = 10;
    a++;
    printf("%d\n",a);
    sleep(2);
    goto p;
  }
    return 0;
}

在代码中我使用goto做了一个循环,相信宝子们都知道上面代码会每2秒打印a的值,但是由于每次goto都回到了a定义前,因此每次打印a的值都为11,如下图:

但是我如果使用static去修饰变量a,那么goto回去之后并不会将它初始化,a任然会保持上次执行的值不变,在上次值的基础上++,执行结果如上面右图,代码如下:

cpp 复制代码
#include <stdio.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
p:{
    static int a =10;
    a++;
    printf("%d\n",a);
    sleep(2);
    goto p;
  }
    return 0;
}

4、printf的缓冲行为

printf将数据输出到标准输出时,这些数据的缓冲行为遵循标准输出的缓冲机制。

缓冲的一些关键点:

  1. 行缓冲 :当输出流是交互式设备(如终端或控制台)时,标准输出通常是行缓冲的。这意味着输出会存储在缓冲区中,直到遇到换行符(\n)或缓冲区满时,缓冲区的内容才会被刷新到输出设备。

  2. 全缓冲 :当输出流是文件时,标准输出通常是全缓冲的。这意味着输出会存储在缓冲区中,直到缓冲区满或显式调用fflush函数时,缓冲区的内容才会被写入文件。

  3. 无缓冲 :标准错误(stderr)通常是无缓冲的,意味着每次调用如fprintf(stderr, ...)都会立即将输出刷新到设备,而不会存储在缓冲区中。

cpp 复制代码
#include <stdio.h>
int main(int argc, char *argv[])
{ 
    printf("1111");
    while(1)
    {
        //printf("2222");
        //usleep(10000);
    }

    return 0;
} 

当printf没有加换行符'\n'时,代码会全部执行完才会将1111打印出来,我在下面加了一个死循环之后,光标会在终端一直闪烁,并不会输出1111,运行结果如下:

把上述代码被注释掉的两行加入,就能得到当缓冲区被放满后,缓冲区的内容被刷新输出到终端的结果,如下图:

相关推荐
2401_84653595几秒前
Scala项目(图书管理系统)
开发语言·后端·scala
锅包肉的九珍2 分钟前
Scala图书管理系统
开发语言·后端·scala
itas1094 分钟前
Rust调用C动态库
c语言·rust·bindgen·bindings·rust c绑定
SomeB1oody9 分钟前
【Rust自学】5.1. 定义并实例化struct
开发语言·后端·rust
云空31 分钟前
《解锁 Python 数据挖掘的奥秘》
开发语言·python·数据挖掘
青莳吖41 分钟前
Java通过Map实现与SQL中的group by相同的逻辑
java·开发语言·sql
Buleall1 小时前
期末考学C
java·开发语言
重生之绝世牛码1 小时前
Java设计模式 —— 【结构型模式】外观模式详解
java·大数据·开发语言·设计模式·设计原则·外观模式
小蜗牛慢慢爬行1 小时前
有关异步场景的 10 大 Spring Boot 面试问题
java·开发语言·网络·spring boot·后端·spring·面试
荒古前1 小时前
龟兔赛跑 PTA
c语言·算法