【C语言航路】第三站:函数(二)

简介: 【C语言航路】第三站:函数(二)

五、函数返回类型,参数未写导致产生的一些后果

我们在写函数的时候,总有一些初学者,会将函数写的乱七八糟。比如返回类型不写,无返回类型的函数却强行让他返回,导致产生了各种各样的后果,那么今天我们就故意来试错,看看到底会产生什么结果。

1.函数未写返回类型,则默认返回int类型

有些人在些函数的时候,就不写返回类型,因为他们认为不写返回类型就是void返回类型,这其实是一个经典的错误,标准的零分。函数不写返回类型,则默认返回int类型。如下代码所示,test返回int类型。这是c语言所规定的。

test()
{
  printf("hehe");
}
int main()
{
  test();
  return 0;
}

2.函数不写形参,但传了实参。

如下代码所示,函数中不写形参,但是非要传一个参数,这样的操作是不会报错的,无非就是给传一个参数,但是这个函数不接收罢了,也就是说,传不传这个100对代码毫无影响。但是不推荐这样写,容易误导人。降低可读性。同时这也是c语言不严谨的一个小地方。

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int test()
{
  printf("hehe");
}
int main()
{
  test(100);
  return 0;
}

3.函数形参写了void,但非要传实参

如下代码所示,函数已经明确规定不要传参了,但是非要传100,那编译器就会报警告。

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int test(void)
{
  printf("hehe");
}
int main()
{
  test(100);
  return 0;
}

运行结果如下所示

4.函数内没有return,但是却强行让他返回一个值,则大多数编译器返回最后一条语句的返回值

如下代码所示,test内没有return,反而强行让他返回,那大多数编译器就会返回最后一条语句的结果。

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int test(void)
{
  printf("hehe\n");
}
int main()
{
  int ret = test();
  printf("%d", ret);
  return 0;
}

运行结果为,因为printf的返回值为他打印出来的字符数。显然打印了五个字符(hehe\n)。具体printf这个库函数如何学习,请大家看这篇文章,详细讲解了库函数如何学习,因为授之以鱼,不如授之以渔。一定要自己学会如何学习库函数。第三站:函数(第一幕),如何学习库函数

当然,最好不要写出这种摸棱两可的函数,因为这个只是一般情况,不代表全部编译器都是这样的,这是一种很危险的行为。

5.声明函数时省略函数形参,但不省略类型。

如下代码所示

int Add(int ,int);

这样的代码是没有问题的, 在声明函数时候,可以省略函数的形参的变量,但是不可以省略类型,但是定义的时候不可以省略。同样也不建议这样操作。因为很多人定义和声明极其混淆。不过在下文我们会详细讲解声明和定义的区别。

六、函数的嵌套调用和链式访问

1.嵌套调用

我们通过一个例子来详细解读一下,如下所示代码,three_line中调用了new_line这个函数,一个函数调用另外一个函数,这就叫做嵌套调用。但是有一点需要注意,函数可以嵌套调用,但是绝对不可以嵌套定义。

#include <stdio.h>
void new_line()
{
  printf("hehe\n");
}
void three_line()
{
  int i = 0;
  for (i = 0; i < 3; i++)
  {
    new_line();
  }
}
int main()
{
  three_line();
  return 0;
}

2.链式访问

我们通过一段代码来了解一下链式访问

#include<stdio.h>
int main()
{
  printf("%d", strlen("abcdef"));
  return 0;
}

顾名思义,就是一个像一根链子一样串起来,把strlen的返回值作为另一个函数的参数,就叫做链式访问

我们在看一段代码,这是一个经典的例子。

#include<stdio.h>
int main()
{
  printf("%d", printf("%d", printf("43")));
  return 0;
}

运行结果为

因为printf的返回值为它打印出的字符个数,具体了解printf这个函数在上文中已经提到。第三个printf先打印出43,43是两个字符,返回2,打印出2,2又是一个字符,又返回1,1在打印出来。所以最终结果为4321

七、函数的声明和定义

当我们在代码中这样写函数时候,将函数放在了调用它的后面,那么就会产生报错,这是因为我们的计算机编译时候是从上往下按照顺序的。

#include<stdio.h>
int main()
{
  int a = 0;
  int b = 0;
  scanf("%d %d", &a, &b);
  int ret = Add(a, b);
  printf("%d", ret);
  return 0;
}
int Add(int x,int y)
{
  return x + y;
}

这时候,我们为了防止报错,在前面加上一个函数的声明,就可以防止报错了。此时上面的Add称作声明,下面的Add称作定义。

#include<stdio.h>
int Add(int x,int  y);
int main()
{
  int a = 0;
  int b = 0;
  scanf("%d %d", &a, &b);
  int ret = Add(a, b);
  printf("%d", ret);
  return 0;
}
int Add(int x,int  y)
{
  return x + y;
}

当然我们也可以按照我们之前的方式直接将Add放在调用它之前,此时Add就是定义,而此时定义本身就是一种特殊的声明。

当然,大家肯定觉得这样很麻烦,为什么要这样做呢,为什么就不能把这个永远定义在最前面呢?其实,这种定义方式在工程中不是这样使用的,而是通过分文件使用的。我们将这个内容放在后面的一篇外传中,为大家详细讲解,为什么要这样定义,多文件使用。有什么好处,一个完整的程序是如何不让别人看到源码的使用的?这些都将放在后面的一篇文章中详细讲解,后续也会在该文章中做出一定的修改,在这里附上链接。

当然在我们之前提到变量的声明和定义时候,提到了函数的声明和定义,在这里我们在回顾一下变量的声明和定义,其实和函数是基本相同的

如下代码所示,当我们只在下面具有一个g_val时候,毫无疑问编译器会报错误,因为定义在使用之后了。

#include<stdio.h>
int main()
{
  printf("%d", g_val);
  return 0;
}
int g_val = 2022;//定义

因此我们在前面补上一个声明,此时上面的这个就叫做声明了,下面那个是定义。

#include<stdio.h>
int g_val;//声明
int main()
{
  printf("%d", g_val);
  return 0;
}
int g_val = 2022;//定义

而且不能在声明的时候赋值,然后下面还继续定义。此时上面的叫做定义,下面的也叫做定义,此时会报一个错误,叫做重定义

#include<stdio.h>
int g_val = 2022;//定义
int main()
{
  printf("%d", g_val);
  return 0;
}
int g_val = 2022;//定义

所以此时我们就应该去掉下面的定义,这样就不会叫做重定义了,并且上面的定义也是一种特殊的声明。

#include<stdio.h>
int g_val = 2022;//定义
int main()
{
  printf("%d", g_val);
  return 0;
}

上面的这些例子这也同时告诉着我们,函数的声明是是不需要赋值的,定义的时候在赋值就可以了。但是定义一定要赋值吗?我们说,是不一定的。请看下面的代码

#include<stdio.h>
int g_val;//定义
int main()
{
  printf("%d", g_val);
  return 0;
}

这段代码中,g_val就属于定义,虽然它没有赋值,但他仍然属于定义。总的来说就是有定义的时候,它就是声明,没有定义的时候,它就是定义。

并且我们运行后,结果是0.

为什么是0呢?我们说这是因为全局变量不初始化默认是0

八、函数递归

1.什么是递归?以及基本定义,基本概念

程序调用自身的编程技巧称为递归(recursion)。

递归做为一种算法在程序设计语言中广泛应用。 一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解。

递归策略

只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。

递归的主要思考方式在于:把大事化小

我们来看一个史上最简单的递归

#include<stdio.h>
int main()
{
  printf("hehe\n");
  main();
  return 0;
}

这个递归下去效果是这样的:最终程序挂了

我们调试,会发现报错信息:stack overflow 意思为栈溢出。

这是什么意思呢?这又是为什么呢?我们得好好研究一下,我们在前面的文章中提到过内存的分区使用,如有忘记可以查看该链接去跳转前文:第一站:初识C语言(第四幕)。内存我们会粗略的分为栈区,堆区和静态区。如下图所示

其中栈区存放着函数的局部变量、函数参数、临时的变量。每一次调用递归,都会为本次函数,在内存的栈区上开辟一块空间。而我们上面的调用递归,会使得main函数不断占用栈区,这个递归相当于一个无限递归,而我们的栈区是有限的。所以最终就会使得栈区内存被完全占用,也就是所谓的栈溢出了。

也即是说,栈被耗干了,程序最终就会崩溃。上面的递归虽然是一个最简单的递归,但是却是一个错误的递归。

当然提起栈溢出,这里也为大家分享一个网站:https://stackoverflow.com/,可以看到这个网站的名字就叫做栈溢出。这相当于知乎一样的问答社区,只不过上面的都是程序员在交流,所以他是一个程序员的问答社区。也被人调侃为:同性交友网站。

当然我们国内也有类似的网站:它的名字是思否,网站是:https://segmentfault.com/

2.递归的两个必要条件

1.存在限制条件,当满足这个限制条件时,递归不在继续。

2.每次递归调用之后越来越接近这个限制条件。

我们看两个例子来详细解释一下这两条:

(1)接受一个无符号整型值,按照顺序打印它的每一位。

例如:输入1234  输出 1 2 3 4

这道题大家第一眼看过去,当然觉得简单,我们只需要利用好/和%这两个操作符就可以很轻松的打印出来。唯一需要注意的就是无符号整型用%u来输入。代码实现如下.

#include<stdio.h>
void Print(unsigned int num)
{
  while(num != 0)
  {
    printf("%d ", num % 10);
    num = num / 10;
  }
}
int main()
{
  unsigned int num = 0;
  scanf("%u",&num);
  Print(num);
  return 0;
}

可是需求却有一点不一样啊,这个打印出来的是逆序。而我们需要的是顺序,那还有其他办法吗?我们说,是有的。

我们可以试一下递归的思想,因为递归的思想就是大事化小。

我们想要1234按顺序打印出来.

那是不是可以分为先打印将123给按顺序打印出来,然后在打印出4.

想把123按顺序打印出来可以分为先把12给按顺序打印出来,然后打印出3

想把12按顺序打印出来可以分为先把1打印出来,然后在打印出来2

也就是说

Print(1234)可分为

Print(123)   4   可分为

Print(12)   3    4   可分为

Print(1) 2    3     4    

然后就打印出来1   2    3    4了

所以我们的递归函数表达式应该是这样写的

也即是,如果num大于9,则两条都执行,如果num不大于9,则只执行后一条语句

我们按照上面的思路实现一下代码

#include<stdio.h>
void Print(unsigned int num)
{
  if (num > 9)
  {
    Print(num / 10);
  }
  printf("%d ", num % 10);
}
int main()
{
  unsigned int num;
  scanf("%d", &num);
  Print(num);
  return 0;
}

当然呢,肯定还是有些读者看到这个递归代码是很懵逼的,但是没关系,我们详细解剖一下这个代码的运行过程。

递归=递推+回归,先递推,在回归。

我们把主函数和Print函数先分开

然后当我主函数传入1234后,我就进入Print函数,如下图所示,1234进入函数以后,满足if语句,继续往下走,又遇到Print函数,此时这个Print函数传入一个123

如下图所示,我们第二个继续传入123,然后我们继续进入下一个Print函数,和之前一样完全满足之前的情况

同样的,我们继续深入递归

注意看,这里我们的递归就要开始回归了,此时num为1了,不满足递归条件,所以先打印出1%10,也就是1

打印出1以后,就返回到上一层递归中,执行上一层递归剩余的语句,也就是num%10,而在这一层中num为12,所以打印出2

然后继续退回上一层递归,打印出3

继续退回第一层递归,打印出4

最后回到主函数,程序结束

我们回想一下之前的两个必要条件。

1.存在限制条件,当满足这个限制条件时,递归不在继续。

2.每次递归调用之后越来越接近这个限制条件。

我们会发现这个题中的函数完全符合这两个条件。

(2)编写函数,不允许创建临时变量,求字符串长度

我们已经知道,我们库函数本身就有一个strlen函数可以求字符串长度,我们先演示一遍库函数求字符串长度的代码

#include<stdio.h>
#include<string.h>
int main()
{
  char a[] = "abc";
  int len = strlen(a);
  printf("%d", len);
  return 0;
}

而这道题的意思其实翻译一下也很简单,就是自己模拟实现一个strlen函数

那我们先把这个函数的类型,参数都给定义好。如下代码所示,值得注意的是,数组名就是首元素的地址,也就是a的地址,所以我们传参的时候需要使用一个指针来传参。

#include<stdio.h>
#include<string.h>
int my_strlen(char* str)
{
}
int main()
{
  char a[] = "abc";
  int len = my_strlen(a);
  printf("%d", len);
  return 0;
}

如何实现呢?我们可以这样想,这是我们的数组中的元素,我们想要求他的长度,那不就是只要*str不等于\0的时候,弄一个计数器,进行++一下,然后str这个指针也往后走一步,利用一个循环就可以完美的解决问题吗?

我们代码实践一下,注意str++,字符指针+1,往后跳一个字符,举一反三,整型指针+1,向后跳一个整型,也就是4个字节。

#include<stdio.h>
#include<string.h>
int my_strlen(char* str)
{
  int count = 0;
  while (*str != '\0')
  {
    count++;
    str++;
  }
  return count;
}
int main()
{
  char a[] = "abc";
  int len = my_strlen(a);
  printf("%d", len);
  return 0;
}

可是这个好像创建了一个新变量count啊,我们题目的要求是不创建新变量啊,这违背了我们题目的要求,那么还有什么好的方法可以避免吗?我们说,是有的。

我们刚刚学习了递归,那我们就可以使用分而治之的思想啊。那我们分析一下,我们想要求my_strlen("abc")的长度,那是不是就是1+my_strlen("bc")的长度啊,那不就是1+1+my_strlen("c")的长度啊,进而就是1+1+1+my_strlen("\0")的长度啊(因为字符串的结尾是\0,即便不写也是一个默认的,这在之前的文章中提到过),最终就是1+1+1+0,最后返回一个3就结束了。所以说我们的递推函数应该怎么写呢?应该这样写。

有了这个递归函数,那么写出这个递归就轻而易举了

#include<stdio.h>
#include<string.h>
int my_strlen(char* str)
{
  if (*str != '\0')
  {
    return 1 + my_strlen(str+1);
  }
  else
  {
    return 0;
  }
}
int main()
{
  char a[] = "abc";
  int len = my_strlen(a);
  printf("%d", len);
  return 0;
}

但是呢,注意一点,在这里很多人都会犯一个经典的错误,标准的零分,它把my_strlen(str+1)改成了my_strlen(str++)。这个错误很不容易看出来,要知道str++是先引用str,在加加的,可是我们这个递归中先引用了str后,这个str就进入了下一层递归,然后悲剧来了,你会发现这个str一直不会被改变,因为str一直处于递推状态,它只有在回归的时候才会++,这就造成了递归层数太深,栈溢出,程序崩溃了。但同时修改方法很简单,把str++改为++str就可以了。

而且我们也可以看到,这个递归中也同样满足两个必要条件。

这个递归也没有创建临时变量,也完美的实现了我们的需求。

3.递归和迭代

所谓迭代就是指用非递归的方式解决问题

我们看两个题

(1)求n的阶乘

非递归的方式,也就是迭代的方式相信大家都很容易能够写出来,这里就不再赘述了,直接给出代码

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int Factorial(int n)
{
  int i;
  int s=1;
  for (i = 1; i <= n; i++)
  {
    s = s * i;
  }
  return s;
}
int main()
{
  int n;
  scanf("%d", &n);
  int ret = Factorial(n);
  printf("%d", ret);
  return 0;
}

那么对于递归的思想其实也不难,因为n的阶乘等于n乘以n-1的阶乘

有了递归公式,写出递归函数就轻而易举了,代码如下

#include<stdio.h>
int Factorial(int n)
{
  if(n>1)
  {
    return n * Factorial(n - 1);
  }
  else
  {
    return 1;
  }
}
int main()
{
  int n;
  scanf("%d", &n);
  int ret = Factorial(n);
  printf("%d", ret);
  return 0;
}

我们画一下该递归的图解

迭代与递归的方法各有优缺,因为递归可能会造成栈溢出,程序崩溃,所以此题其实迭代的方法更好一些。

(2)求第n个斐波那契数

这是斐波那契数列的递推式

有了递推式写出代码那简直轻而易举

#include<stdio.h>
int Fib(int n)
{
  if (n <= 2)
  {
    return 1;
  }
  else 
  {
    return Fib(n - 1) + Fib(n - 2);
  }
}
int main()
{
  int n;
  scanf("%d", &n);
  int ret = Fib(n);
  printf("%d", ret);
  return 0;
}

可是这代码,我们可以试一下,会发现效率特别低,如果输入50,可能得跑很长时间,里面具有大量的重复计算。而且还有栈溢出,程序崩溃的风险。

我们可以试一下,使用一个全局变量count,计算一下光Fib(3)被重复计算了多少次,

#include<stdio.h>
int count = 0;
int Fib(int n)
{
  if (n == 3)
  {
    count++;
  }
  if (n <= 2)
  {
    return 1;
  }
  else 
  {
    return Fib(n - 1) + Fib(n - 2);
  }
}
int main()
{
  int n;
  scanf("%d", &n);
  int ret = Fib(n);
  printf("%d\n", ret);
  printf("%d", count);
  return 0;
}

运行结果如下

可见单单是Fib(3)就重复计算了将近4千多万次

我们还可以画个图可以更加直观的看出这计算次数之大

数目之大难以想象。可见递归并不是解决这道题的最优方案。

那么还有什么方法能好一点呢?其实就是循环,循环也是迭代的一种

如图所示,我们其实可以用循环的思想,按照顺序进行计算第n个斐波那契数

我们的思考是这样的,第一个和第二个是已知的,都是1,第三个数是前两个相加,那我就把后两个数给放到前两个数的位置上,然后就循环下去。判断条件也很简单,就是当n>2时候才需要执行,n小于2直接返回即可,另外要给c初始化为1,因为我们返回的是c。

代码实现如下

#include<stdio.h>
int Fib(int n)
{
  int a = 1;
  int b = 1;
  int c = 1;
  while (n > 2)
  {
    c = a + b;
    a = b;
    b = c;
    n--;
  }
  return c;
}
int main()
{
  int n;
  scanf("%d", &n);
  int ret = Fib(n);
  printf("%d\n", ret);
  return 0;
}

在调试 factorial 函数的时候,如果你的参数比较大,那就会报错: stack overflow(栈溢出) 这样的信息。系统分配给程序的栈空间是有限的,但是如果出现了死循环,或者(死递归),这样有可能导致一直开辟栈空间,最终产生栈空间耗尽的情况,这样的现象我们称为栈溢出

如何解决呢?

1. 将递归改写成非递归。

2. 使用static对象替代 nonstatic 局部对象。在递归函数设计中,可以使用 static 对象替代

nonstatic 局部对象(即栈对象),这不仅可以减少每次递归调用和返回时产生和释放 nonstatic 对象的开销,而且 static 对象还可以保存递归调用的中间状态,并且可为各个调用层所访问

1. 许多问题是以递归的形式进行解释的,这只是因为它比非递归的形式更为清晰。

2. 但是这些问题的迭代实现往往比递归实现效率更高,虽然代码的可读性稍微差些。

3. 当一个问题相当复杂,难以用迭代实现时,此时递归实现的简洁性便可以补偿它所带来的运行时开销。

4.函数栈帧的创建与销毁.

在上面这么多的递归的例子以后,大家有没有思考过一个问题,就是为什么递归在回归的时候,他原先的n值还在呢?这个n不会发生变化呢?这个阶乘递归的图解,我们以这个为例子进行讲解

函数栈帧

每一次函数调用都会为本次函数调用分配内存空间(是在内存的栈区),为本次函数调用分配的内存空间叫被成为这次函数调用的栈帧空间,这也就是我们将要说的的函数栈帧的创建与销毁

我们在之前已经多次提到过,函数的内存大致分为三个区域,注意是大致,实际上的分区要比这个还要复杂,也就是下图中的栈区,堆区,静态区。

由于都是在内存的栈区上进行创建的。所以我们将栈区单独拿出来

而我们第一次调用主函数,就会为主函数分配一块内存空间,然后在主函数中定义n,这个n是在主函数栈帧空间内的,如下图所示

紧接着,我们进入第一层递归,就要调用函数,调用函数继续占用栈区空间。

然后继续进入第二层,第三层。

每个空间的具体详情如下,在这里你也能感受到,当递归层次太深的话,栈区就会爆满了,也就是所谓的栈溢出。程序就会崩溃

然后当我们一层递归使用完毕之后就会销毁这个空间,从内层到直到程序结束释放了main函数

当然呢,这里只是简单了谈了一下栈帧的创建与销毁,或许你听了之后还是摸棱两可,但是不要紧,我们后续会专门拿出一节内容来好好探讨一下函数栈帧的创建与销毁,从汇编代码的角度一步一步看懂函数栈帧的创建与销毁


总结

本节内容较多,需要大家好好消化理解一下,主要讲解了一些函数返回类型乱写导致的现象,函数嵌套调用与链式访问,声明和定义的区别,函数递归的思想,如何理解递归,递归的必要条件以及迭代,并且简单了解了函数栈帧的创建与销毁。

本站未完,欲知后事,请看下节

相关文章
|
5月前
|
存储 算法 C语言
【C语言程序设计——函数】素数判定(头歌实践教学平台习题)【合集】
本内容介绍了编写一个判断素数的子函数的任务,涵盖循环控制与跳转语句、算术运算符(%)、以及素数的概念。任务要求在主函数中输入整数并输出是否为素数的信息。相关知识包括 `for` 和 `while` 循环、`break` 和 `continue` 语句、取余运算符 `%` 的使用及素数定义、分布规律和应用场景。编程要求根据提示补充代码,测试说明提供了输入输出示例,最后给出通关代码和测试结果。 任务核心:编写判断素数的子函数并在主函数中调用,涉及循环结构和条件判断。
296 23
|
4月前
|
人工智能 Java 程序员
一文彻底搞清楚C语言的函数
本文介绍C语言函数:函数是程序模块化的工具,由函数头和函数体组成,涵盖定义、调用、参数传递及声明等内容。值传递确保实参不受影响,函数声明增强代码可读性。君志所向,一往无前!
87 1
一文彻底搞清楚C语言的函数
|
5月前
|
C语言
【C语言程序设计——函数】亲密数判定(头歌实践教学平台习题)【合集】
本文介绍了通过编程实现打印3000以内的全部亲密数的任务。主要内容包括: 1. **任务描述**:实现函数打印3000以内的全部亲密数。 2. **相关知识**: - 循环控制和跳转语句(for、while循环,break、continue语句)的使用。 - 亲密数的概念及历史背景。 - 判断亲密数的方法:计算数A的因子和存于B,再计算B的因子和存于sum,最后比较sum与A是否相等。 3. **编程要求**:根据提示在指定区域内补充代码。 4. **测试说明**:平台对代码进行测试,预期输出如220和284是一组亲密数。 5. **通关代码**:提供了完整的C语言代码实现
110 24
|
5月前
|
存储 C语言
【C语言程序设计——函数】递归求斐波那契数列的前n项(头歌实践教学平台习题)【合集】
本关任务是编写递归函数求斐波那契数列的前n项。主要内容包括: 1. **递归的概念**:递归是一种函数直接或间接调用自身的编程技巧,通过“俄罗斯套娃”的方式解决问题。 2. **边界条件的确定**:边界条件是递归停止的条件,确保递归不会无限进行。例如,计算阶乘时,当n为0或1时返回1。 3. **循环控制与跳转语句**:介绍`for`、`while`循环及`break`、`continue`语句的使用方法。 编程要求是在右侧编辑器Begin--End之间补充代码,测试输入分别为3和5,预期输出为斐波那契数列的前几项。通关代码已给出,需确保正确实现递归逻辑并处理好边界条件,以避免栈溢出或结果
290 16
|
5月前
|
存储 编译器 C语言
【C语言程序设计——函数】分数数列求和2(头歌实践教学平台习题)【合集】
函数首部:按照 C 语言语法,函数的定义首部表明这是一个自定义函数,函数名为fun,它接收一个整型参数n,用于指定要求阶乘的那个数,并且函数的返回值类型为float(在实际中如果阶乘结果数值较大,用float可能会有精度损失,也可以考虑使用double等更合适的数据类型,这里以float为例)。例如:// 函数体代码将放在这里函数体内部变量定义:在函数体中,首先需要定义一些变量来辅助完成阶乘的计算。比如需要定义一个变量(通常为float或double类型,这里假设用float。
145 3
|
5月前
|
存储 算法 安全
【C语言程序设计——函数】分数数列求和1(头歌实践教学平台习题)【合集】
if 语句是最基础的形式,当条件为真时执行其内部的语句块;switch 语句则适用于针对一个表达式的多个固定值进行判断,根据表达式的值与各个 case 后的常量值匹配情况,执行相应 case 分支下的语句,直到遇到 break 语句跳出 switch 结构,若没有匹配值则执行 default 分支(可选)。例如,在判断一个数是否大于 10 的场景中,条件表达式为 “num> 10”,这里的 “num” 是程序中的变量,通过比较其值与 10 的大小关系来确定条件的真假。常量的值必须是唯一的,且在同一个。
138 2
|
5月前
|
存储 编译器 C语言
【C语言程序设计——函数】回文数判定(头歌实践教学平台习题)【合集】
算术运算于 C 语言仿若精密 “齿轮组”,驱动着数值处理流程。编写函数求区间[100,500]中所有的回文数,要求每行打印10个数。根据提示在右侧编辑器Begin--End之间的区域内补充必要的代码。如果操作数是浮点数,在 C 语言中是不允许直接进行。的结果是 -1,因为 -7 除以 3 商为 -2,余数为 -1;注意:每一个数据输出格式为 printf("%4d", i);的结果是 1,因为 7 除以 -3 商为 -2,余数为 1。取余运算要求两个操作数必须是整数类型,包括。开始你的任务吧,祝你成功!
117 1
|
5月前
|
算法 C语言
【C语言程序设计——函数】利用函数求解最大公约数和最小公倍数(头歌实践教学平台习题)【合集】
本文档介绍了如何编写两个子函数,分别求任意两个整数的最大公约数和最小公倍数。内容涵盖循环控制与跳转语句的使用、最大公约数的求法(包括辗转相除法和更相减损术),以及基于最大公约数求最小公倍数的方法。通过示例代码和测试说明,帮助读者理解和实现相关算法。最终提供了完整的通关代码及测试结果,确保编程任务的成功完成。
249 15
【C语言程序设计——函数】利用函数求解最大公约数和最小公倍数(头歌实践教学平台习题)【合集】
|
6月前
|
存储 C语言 开发者
【C语言】字符串操作函数详解
这些字符串操作函数在C语言中提供了强大的功能,帮助开发者有效地处理字符串数据。通过对每个函数的详细讲解、示例代码和表格说明,可以更好地理解如何使用这些函数进行各种字符串操作。如果在实际编程中遇到特定的字符串处理需求,可以参考这些函数和示例,灵活运用。
241 10
|
6月前
|
存储 程序员 C语言
【C语言】文件操作函数详解
C语言提供了一组标准库函数来处理文件操作,这些函数定义在 `<stdio.h>` 头文件中。文件操作包括文件的打开、读写、关闭以及文件属性的查询等。以下是常用文件操作函数的详细讲解,包括函数原型、参数说明、返回值说明、示例代码和表格汇总。
157 9