Linux和C语言(Day11)
一、学习内容
-
讲解有参函数
-
形参 和 实参
-
形参——定义时的参数,形式上的参数,没有实际意义,语法上必须带有数据类型 void fun(int a,int b); void fun(int a[],int n); void fun(char *s); 可以是:变量、数组、指针
-
实参——调用时的参数,实际上的参数,有实际意义,实参语法上不加数据类型,直接传参名字 fun(a,b) fun(1,2) fun(a+2,3) fun(a,strlen(a)) fun(3,fun(4,5)) 可以:常量、变量名、数组名、指针名、表达式
-
注意:实参必须和形参保持数据类型、顺序、数量一一对应。【名字无所谓】
-
eg: void fun(int a,float b,char c); fun(3 , 4 , 'A'); //正确 fun(3,4); //错误 int x=3; float y=3.5; char z='a'; fun(x,y,z); //正确
-
-
-
值传递 和 地址传递
-
值传递
-
形参和实参分处不同的存储单元形参变实参不变
-
实参【原件】拷贝出一份值给形参【复印件】,复印件修改原件不变
-
-
-
地址传递
-
形参和实参除处在相同的存储单元里面形参变、实参也变
-
实参【原件】直接给了形参
-
-
-
-
数组传参方式
-
整型一维数组传参
-
一维数组传参需要两个参数——数组、数组的长度
-
void FA(int a[10],int n);
-
void FA(int a[],int n);
-
void FA(int *a,int n);
-
-
-
整型二维数组传参
-
二维数组传参需要三个参数——数组、行数、列数
-
void FB(int b[2][3], int hang, int lie);
-
void FB(int b[][3], int hang, int lie);
-
void FB(int (*b)[3], int hang, int lie);
-
-
-
字符串传参
-
因为字符串可以通过strlen()或者条件判断!='\0'来控制循环。所以,字符串的传参最少有一个——字符串名
-
void my_strlen(char s[100]);
-
void my_strlen(char s[];)
-
void my_strlen(char *s);
-
-
-
-
-
讲解有返回值函数
-
问:函数必须有返回值吗?
-
答:可以没有,根据需求设置返回值,而且设置返回值之后,调用时可以接收返回值,也可以不接收返回值。
-
-
有返回值函数的定义语法
-
函数数据类型 函数名(参数){ return 返回值; //返回【一个】确定值 }
-
若返回值数据类型与函数数据类型不一致,以【函数数据类型】为准。
-
return有结束函数的作用,若有多个return,遇见第一个return就结束函数。
-
若省略函数数据类型不写,默认是【int】类型。
-
-
-
-
递归函数
-
递归思想
-
将大规模问题分解成相似的小规模问题,再将小规模问题分解成更小规模的相似的问题,最终通过解决小规模问题 把大规模问题就解决。
-
-
问:函数可以自己调用自己吗?若可以的话,会出现什么问题呢?
-
可以,但是不加条件的话,会类似于“死循环”
-
-
概念
-
自己调用自己的函数。
-
-
递归三要素
-
递归边界条件【结束条件】
-
递归返回段【边界条件满足,结束】
-
递归前进段【边界条件不满足,继续】
-
-
递归函数的应用
-
斐波那契数列、树的遍历、图的遍历等
-
-
递归实现0-n求和
-
问题规模是n
假设一个函数 int fun(int n);功能是对规模n求和
fun(n); 求0到n之和
fun(n-1); 求0到n-1之和
fun(n) === fun(n-1)+n; fun(n-2); 求0到n-2之和
……
fun(1); 求0-1之和
fun(0); 求0-0之和
-
-
递归实现求斐波那契数列第n项
-
问题规模是n
假设一个函数int fbnq(int n);功能求第n项
fun(n) n
fun(n-1) n-1
fun(n-2) n-2
……
fun(2) 第2项 1
fun(1) 第1项 1
-
-
-
指针函数
-
注意
-
不可以返回局部变量的地址
-
可以返回全局变量的地址
-
可以返回static局部变量的地址
-
可以返回形参接收的地址
-
-
语法格式
-
数据类型 *函数名(参数){ 代码块; }
-
-
-
函数指针
-
本质是一个指针,指向一个函数
-
语法格式
-
数据类型 (*指针名)(参数列表);
-
-
已学习的指针:
-
int *p; 指向整型变量的地址【一维数组的首地址】
char *p; 指向单字符变量的地址【字符串/字符数组的首地址】
int (*p)[3]; 指向二维数组【行指针】
void (*p)(int a,int b); 指向函数的地址
-
-
-
函数指针数组
-
本质是一个数组,存储的元素都是函数指针。
-
语法格式
-
数据类型 (*数组名[长度])(参数);
-
-
作用
-
转移表【C语言转移表(Jump Table)是一种优化技术,可以用来代替一系列的if-else语句或switch语句,从而提高代码的执行效率。】
-
-
-
变量的作用域——全局 和 局部

-
数组、函数、指针总结
-
数组能存储指针
-
函数能返回指针
-
指针能指向数组、指向函数
-
int *p; 【指向一个int变量/一维数组】
-
int *p[3]; 【指针数组,存储int指针】
-
int (*p)[3]; 【数组指针,指向二维数组,也就是行指针】
-
int *p(); 【指针函数,返回值是int指针】
-
int (*p)(); 【函数指针,指向返回值为int的函数】
-
int (*p[3])(); 【函数指针数组,存储指针,指针指向返回值为int的函数】
-
int (*(*p)[3])(); 【p是一个指针,指向函数指针数组】
-
int (*(*p)())[3]; 【p是一个指针,指向一个函数,函数的返回值是指针,这个指针指向长度为3的int数组】
-
-
脑图

二、作业
-
以下程序的正确运行结果是( )。(鲁科安全)
int f(int a);
int main(void)
{
int a = 2,i;
for(i = 0; i < 3; i++)
printf("%4d", f(a));
}
int f(int a)
{
int b = 0;
static int c = 3;
b++;
c++;
return (a+b+c);
}
A. 777 B. 7 10 13 C. 7 9 11 D. 7 8 9
解析:
第一次调用
f(a):
a = 2
b = 0(初始值),然后b++,因此b = 1
c = 3(初始值),然后c++,因此c = 4返回值:
a + b + c = 2 + 1 + 4 = 7第二次调用
f(a):
a = 2
b = 0,然后b++,因此b = 1
c上次调用后为 4,然后c++,因此c = 5返回值:
a + b + c = 2 + 1 + 5 = 8第三次调用
f(a):
a = 2
b = 0,然后b++,因此b = 1
c上次调用后为 5,然后c++,因此c = 6返回值:
a + b + c = 2 + 1 + 6 = 9程序的输出结果:
7 8 9
解答:
D
- 在一个被调用函数中,关于return语句使用的描述,( )是错误的 (软通动力)
A. 被调用函数中可以不用return语句
B. 被调用函数中可以使用多个return语句
C. 被调用函数中,如果有返回值,就一定要有return语句
D. 被调用函数中,一个return语句可以返回多个值给调用函数
解析:
A. 被调用函数中可以不用
return语句对于
void类型的函数,return语句是可选的,因为函数不需要返回值。如果函数有返回类型(如
int、float等),则必须使用return返回相应类型的值。结论:正确,可以不用
return,但这是针对void函数的情况。B. 被调用函数中可以使用多个
return语句函数中可以在不同的逻辑分支中使用多个
return语句,表示在不同条件下返回不同的结果。这是常见的编程模式。结论:正确,多个
return语句是合法的。C. 被调用函数中,如果有返回值,就一定要有
return语句对于有返回类型的函数(如
int、float等),必须有return语句来返回指定类型的值,否则编译器会产生错误。结论:正确,有返回值的函数必须有
return语句。D. 被调用函数中,一个
return语句可以返回多个值给调用函数一个
return语句只能返回一个值。要返回多个值,通常需要使用结构体、指针、数组等方式来间接返回多个值。结论:错误,
return语句一次只能返回一个值。
解答:
D
-
以下程序的运行结果为( ) (鲁科安全)
#include <stdio.h>
#include <string.h>
int SubCount(char *dest, int count)
{
strcpy(dest, "555");
count++;
return 0;
}
int main()
{
int count = 3;
char caBuf[8];
SubCount(caBuf, count);
printf("%d\n", count);
return 0;
}
A. 8 B. 4 C. 3 D. 5
解析:
在
main()函数中:
count初始化为 3。定义了一个字符数组
caBuf[8]。调用了函数
SubCount(caBuf, count)。在
SubCount()函数中:
strcpy(dest, "555")将字符串"555"复制到dest,即caBuf中。
count++增加了count的值,但这里的count是值传递,意味着main()函数中的count不会受到影响,SubCount()函数只是修改了自己的副本。函数返回 0,但没有影响到
main()函数中的count值。
SubCount()函数返回后,main()函数中的count仍然是原来的值 3。最后,
printf("%d\n", count)输出的是main()函数中的count的值,即 3。
解答:
C
-
请问运行Test函数会有什么样的结果?(华辰泰尔)
char *GetMemory(void)
{
char p[] = "hello world";
return p;
}
void Test(void)
{
char *str = NULL;
str = GetMemory();
printf(str);
}
解析:
GetMemory()函数中:
char p[] = "hello world";定义了一个局部字符数组p,并将字符串"hello world"存储在其中。该数组是局部变量,存储在函数的栈帧中。
函数返回
p的地址(指针),但是由于p是局部变量,函数返回后,该栈帧会被释放,p所指向的内存地址不再有效。
Test()函数中:调用了
GetMemory(),并将返回的地址赋值给str。然后调用
printf(str)试图打印字符串。运行结果:
由于
GetMemory()函数返回的是栈中局部变量p的地址,函数返回后该栈帧被释放,指向的内存不再有效,因此str指向的是一个无效的地址。printf(str)将尝试访问该无效地址,结果会有以下几种情况:未定义行为:因为访问了无效的内存区域,程序可能会崩溃(例如导致段错误
Segmentation fault)。在某些情况下,程序可能会打印随机数据或者乱码(如果栈内存尚未被覆盖)。
解答:
段错误
-
分析以下程序,写出运行结果并写出过程 (广域科技)
#include <stdio.h>
#include <stdlib.h>
void getalloc(char *p)
{
p = (char *)malloc(100);
strcpy(p, "hello world");
}
int main()
{
char *str = NULL;
getalloc(str);
printf("%s\n",str);
free(str);
return 0;
}
解析:
在
main()函数中:定义了一个字符指针
str并将其初始化为NULL。调用了
getalloc(str)函数,试图为str分配内存并赋值。在
getalloc()函数中:参数
p是值传递,意味着p是str的一个副本。
p = (char *)malloc(100);为局部指针p分配了 100 字节的内存空间。
strcpy(p, "hello world");将字符串"hello world"复制到p指向的内存区域。函数结束时,
p仅是局部变量的修改,str在main()函数中的值并没有被修改。回到
main()函数:
str仍然是NULL,因为在getalloc()函数中修改的是p的副本,而不是str的原始指针。当调用
printf("%s\n",str);时,str仍然指向NULL,这会导致未定义行为。最常见的结果是程序崩溃,出现 段错误(Segmentation fault),因为printf试图访问空指针。
free(str);也会导致未定义行为,因为free不能释放NULL指针或未分配的内存。运行结果:
由于
str没有被成功分配内存,程序会在printf调用处崩溃,最可能的结果是发生 段错误(Segmentation fault)。
解答:
段错误
-
下列程序的输出结果是________。(富士安全)
fun(int a, int b, int c)
{
c = a*b;
}
void main()
{
int c = 10;
fun(2,3,++c);
printf("%d\n", c);
}
解析:
main()函数中:
int c = 10;定义了一个整数变量c,初始值为 10。调用
fun(2, 3, ++c)之前,c被前置递增(++c),因此c的值从 10 变为 11。在
fun()函数中:
fun(2, 3, ++c)被调用时,参数传递的是a = 2,b = 3,c = 11(传递的是递增后的c值)。需要注意,C 语言中,参数是通过值传递的,意味着在
fun()函数中修改c(局部变量)并不会影响main()函数中的c。在
fun()函数中,执行c = a * b;,这只是修改了fun()函数内部的局部变量c,它的值变为2 * 3 = 6。但是,这并不会改变main()函数中的c。回到
main()函数:调用
fun()后,main()函数中的c仍然是 11(因为在fun()中修改的是局部变量,不会影响main()中的c)。
printf("%d\n", c);将输出c的值,即 11。
解答:
11
-
找错题,说明那里错了(恩易物联1,深圳元征信息科技)
void test1()
{
char string[10];
char *str1 = "0123456789";
strcpy( string, str1 );
}
解析:
数组
string的长度不足:
char string[10];定义了一个大小为 10 的字符数组。这意味着它最多能存储 10 个字符。但是,
str1是"0123456789",它有 10 个字符加上一个空字符\0,总共需要 11 个字节 来存储。因此,当使用
strcpy( string, str1 );复制字符串时,会试图将 11 个字符复制到一个只能容纳 10 个字符的数组中,导致 缓冲区溢出,这是一个严重的错误,可能会导致程序崩溃或不可预期的行为
解答:
段错误
-
下面的代码片段会输出__________ (飞音时代)
void test(void)
{
char *p = NULL;
strcpy(p, "hello");
printf("%s", p);
}
解析:
指针
p初始化为NULL:
char *p = NULL;将指针p初始化为NULL,即p不指向任何有效的内存地址。尝试将字符串复制到
NULL指针:
strcpy(p, "hello");试图将字符串"hello"复制到指针p指向的内存区域。然而,
p是NULL,并没有指向任何有效的内存地址,因此尝试写入会导致 段错误(Segmentation fault),程序崩溃。操作系统一般会保护
NULL地址,防止写入。
printf("%s", p);不会被执行:由于在
strcpy处程序会崩溃,printf语句不会被执行,因此程序没有输出。
解答:
段错误
-
sizeof(str); 的值是多少? (21年中航安为)
void Func(char str[100])
{
sizeof(str);
}
解析:
数组退化为指针:
在函数参数中,数组类型会自动退化为指向数组首元素的指针。因此,
char str[100]实际上等同于char *str。也就是说,
str在函数内部是一个指针,而不是一个数组。
sizeof(str)的结果:
sizeof(str)实际上是在计算指针的大小,而不是数组的大小。指针的大小依赖于系统架构:
在32位系统上,指针大小通常为 4 字节。
在64位系统上,指针大小通常为 8 字节。
解答:
4/8
-
递归函数最终会结束,那么这个函数一定( );(北京信果科技)
A. 使用了局部变量
B. 有一个分支不调用自身
C. 使用了全局变量或者使用了一个或多个参数
解析:
A. 使用了局部变量
不一定。递归函数使用局部变量有助于保存每次递归调用的状态,但递归函数的结束条件与是否使用局部变量无关。局部变量只是用来存储数据,并不直接影响递归的终止。
B. 有一个分支不调用自身
正确。为了使递归函数能够终止,它必须有一个基本情况或终止条件,使得递归调用停止。如果递归函数在所有情况下都调用自身,那么它将永远不会终止。因此,递归函数需要至少一个分支不调用自身,以确保递归能够结束。
C. 使用了全局变量或者使用了一个或多个参数
不一定。递归函数可以使用全局变量或者参数来控制递归,但这并不是递归函数必须具备的条件。全局变量和参数可以帮助控制递归的行为,但递归函数的最终结束依赖于其终止条件。
解答:
B
-
程序如下,程序执行后的输出结果是: (中科四平)
int f(int x, int y)
{
return (y-x)*x;
}
void main()
{
int a = 3,b=4,c=5,d;
d=f(f(3,4),f(3,5));
printf("%d\n", d);
}
解析:
f(int x, int y)函数:函数体:
return (y - x) * x;函数返回的是
(y - x) * x的值。
main()函数中的变量:
int a = 3, b = 4, c = 5, d;
d = f(f(3, 4), f(3, 5));我们需要先计算
f(3, 4)和f(3, 5),然后将这两个结果作为参数传递给f()。计算
f(3, 4):
f(3, 4) = (4 - 3) * 3
4 - 3 = 1
1 * 3 = 3所以
f(3, 4) = 3计算
f(3, 5):
f(3, 5) = (5 - 3) * 3
5 - 3 = 2
2 * 3 = 6所以
f(3, 5) = 6计算
d = f(f(3, 4), f(3, 5)):已知
f(3, 4) = 3和f(3, 5) = 6因此
d = f(3, 6)计算
f(3, 6):f(3, 6) = (6 - 3) * 3
6 - 3 = 3
3 * 3 = 9所以
f(3, 6) = 9输出结果:
printf("%d\n", d);将输出d的值,即9。
解答:
9
-
请判断下面程序输出的值是多少? (信雅达)
int func(int a)
{
static int b = 0;
b+=a;
return b;
}
int main(void)
{
printf("%d %d\n", func(1)+func(3), func(5));
}
解析:
func(int a)函数:
static int b = 0;:b是静态变量,它在函数调用间保持其值。
b += a;:每次调用func时,b的值会增加a。
return b;:函数返回b的值。
main()函数中的代码:
printf("%d %d\n", func(1) + func(3), func(5));计算
func(1):初始时,
b = 0。调用
func(1)时,b += 1,所以b = 1。
func(1)返回1。计算
func(3):之前
b = 1。调用
func(3)时,b += 3,所以b = 4。
func(3)返回4。计算
func(5):之前
b = 4。调用
func(5)时,b += 5,所以b = 9。
func(5)返回9.计算
func(1) + func(3):已经计算得出
func(1) = 1和func(3) = 4。
func(1) + func(3) = 1 + 4 = 5。最终的
printf语句:
printf("%d %d\n", func(1) + func(3), func(5));之前已经计算出
func(1) + func(3) = 5和func(5) = 9。
解答:
5 9
-
这段程序的输出是(________) (青岛汉泰)
void f1(int *, int);
void f2(int *, int);
void(*p[2]) (int *, int);
main()
{
int a;
int b;
p[0] = f1;
p[1] = f2;
a=3;
b=5;
p[0](&a, b);
printf("%d\t %d\t", a, b);
p[1](&a, b);
printf("%d\t %d\t", a, b);
}
void f1(int * p, int q)
{
int tmp;
tmp = *p;
*p = q;
q = tmp;
}
void f2(int *p, int q)
{
int tmp;
tmp = *p;
*p = q;
q = tmp;
}
A. 5 5 5 5 B. 3 5 3 5 C. 5 3 5 3 D. 3 3 3 3
解析:
函数指针数组
p的初始化:
p[0]被赋值为f1,p[1]被赋值为f2。这使得
p成为指向f1和f2函数的指针数组。变量初始化和函数调用:
a = 3;
b = 5;
p[0](&a, b);调用f1(&a, b)
p[1](&a, b);调用f2(&a, b)
f1函数的执行过程:
f1(int *p, int q)的功能:
tmp = *p;保存*p(即a的值)到tmp,此时tmp = 3。
*p = q;将q(即b的值 5)赋给*p,所以a被更新为 5。
q = tmp;将tmp的值(3)赋给q,但这只改变了q的值,q是局部变量,不影响b。执行
f1(&a, b)后:
a的值变为 5
b的值保持为 5结果:
a = 5,b = 5
f2函数的执行过程:
f2(int *p, int q)的功能:
tmp = *p;保存*p(即a的值)到tmp,此时tmp = 5。
*p = q;将q(即b的值 5)赋给*p,所以a保持为 5(没有变化)。
q = tmp;将tmp的值(5)赋给q,但这只改变了q的值,q是局部变量,不影响b。执行
f2(&a, b)后:
a的值保持为 5
b的值保持为 5结果:
a = 5,b = 5
解答:
A
-
有以下程序段, x=7执行后的值为 ( ) (杭州快越科技)
int fun(int x) {
int p;
if(x==0||x==1)
return(3);
p=x-fun(x-2);
return p;
}
A. 0 B. 2 C. 5 D. 6
解析:
逐步计算
fun(7):计算
fun(7):
fun(7) = 7 - fun(5)计算
fun(5):
fun(5) = 5 - fun(3)计算
fun(3):
fun(3) = 3 - fun(1)计算
fun(1):基本情况:
fun(1) = 3回到
fun(3):
fun(3) = 3 - fun(1) = 3 - 3 = 0回到
fun(5):
fun(5) = 5 - fun(3) = 5 - 0 = 5回到
fun(7):
fun(7) = 7 - fun(5) = 7 - 5 = 2
解答:
B
-
有以下函数,该函数的返回值是:( ) (山东信通电子)
char *fun(char *p)
{
return p;
}
A. 无确切的值 B. 形参 p 中存放的地址值
C. 一个临时存储单元的地址 D. 形参 p 自身的地址值
解析:
A. 无确切的值:
不正确。函数
fun返回的是传递给它的指针p,并且返回值是确定的。B. 形参 p 中存放的地址值:
正确。函数
fun返回的是传递给它的char *p的值,即p存放的地址值。C. 一个临时存储单元的地址:
不正确。
p是传递给函数的实际指针值,而不是临时存储单元的地址。D. 形参 p 自身的地址值:
不正确。
p是指向char的指针,返回的是指针的值,而不是形参p在内存中的地址。
解答:
B
-
编写strcpy函数 (山东山大电力技有限公司)
已知strcpy 函数的原型是
char *strcpy(char *strDest,const char *strSrc);其中 strDest 是目的字符串,strSrc 是源字符串。
(1)、不调用 C 的字符串库函数,请编写函数 strcpy。
(2)、strcpy 能把 strSr 的内容复制到 strDest,为什么还有 char"类型的返回值?
(3)、strcpy 和 memcpy 的区别。
代码解答:
#include <stdio.h>char *strcpy(char *strDest, const char *strSrc) {char *dest = strDest; // 保存指向目标字符串的指针while (*strSrc != '\0') { // 遍历源字符串直到遇到终止符*dest = *strSrc; // 将源字符串的字符复制到目标字符串dest++; // 移动目标指针strSrc++; // 移动源指针}*dest = '\0'; // 添加字符串终止符到目标字符串的末尾return strDest; // 返回目标字符串的起始地址
}int main() {char src[] = "Hello, World!";char dest[50];strcpy(dest, src);printf("%s\n", dest); return 0;
}
结果展示:

解答:
问题2:
链式调用:可以通过返回指针实现函数的链式调用,例如 strcpy(dest, src) == dest,这使得代码更加灵活和简洁。
方便使用:在某些情况下,用户可能需要直接获取目标字符串的起始地址来进行进一步操作,而不仅仅是复制字符串。
问题3:
功能:
strcpy:用于复制以 null 终止的字符串。它会复制源字符串的所有字符,包括终止符'\0'到目标缓冲区。
memcpy:用于复制任意类型的内存块,不考虑数据的内容或类型。它只是按照字节进行复制,不处理字符串终止符。参数:
strcpy:char *strcpy(char *strDest, const char *strSrc),接受两个char *类型的参数,表示字符串的源和目标。
memcpy:void *memcpy(void *dest, const void *src, size_t n),接受void *类型的源和目标指针,以及一个size_t类型的字节数,表示要复制的字节数。安全性:
strcpy:在源字符串的末尾假设有一个终止符'\0',如果源字符串没有终止符或者目标缓冲区不够大,可能导致缓冲区溢出。
memcpy:没有终止符的概念,因此不会自动处理字符串的结束,必须确保目标缓冲区足够大以容纳源数据。
-
请实现一个函数,输入一个整数,输出该数二进制表示中的1的个数。例如:把9表示成二进制是1001,有2位是1。因此如果输入9,该函数输出2。(矩阵软件)
代码解答:
#include <stdio.h>int countOnes(int num) {int count = 0;while (num) {count += num & 1; // 检查最低位是否为 1num >>= 1; // 右移一位,继续检查下一位}return count;
}int main() {int num;printf("输入一个十进制数字: ");scanf("%d", &num);int result = countOnes(num);printf("%d\n", result);return 0;
}
成果展示:

思路:
位操作计算1的个数:
int count = 0;:初始化计数器count,用于记录1的个数。
while (num) { ... }:循环直到num变为0。
num & 1:位与操作检查num的最低位。如果最低位是1,结果为1;否则为0。
count += num & 1;:将检查结果加到count中。
num >>= 1;:将num右移一位,丢弃最低位,并将下一位移到最低位。
return count;:返回1的总数。
例如:你输入的是
9:二进制表示:
9的二进制表示是1001。执行步骤:
初始
num = 9(1001)
num & 1:最低位为1,所以count增加1,count = 1。
num >>= 1:右移一位后,num = 4(100)。
num & 1:最低位为0,所以count不变,count = 1。
num >>= 1:右移一位后,num = 2(10)。
num & 1:最低位为0,所以count不变,count = 1。
num >>= 1:右移一位后,num = 1(1)。
num & 1:最低位为1,所以count增加1,count = 2。
num >>= 1:右移一位后,num = 0。退出循环,
count最终值为2。
-
请用编写递归算法计算fibinacci数列第1000位的值。斐波拉契数列为1,1,2,3,5,8,13,21,…… (北京凝思软件)
代码解答:
#include <stdio.h>// 递归计算斐波那契数列第 n 位的值
unsigned long long fibonacci(int n) {if (n <= 1) {return n;}return fibonacci(n - 1) + fibonacci(n - 2);
}int main() {int n = 1000;// 计算第 1000 位的斐波那契数,注意这会非常耗时和可能会导致栈溢出printf("斐波那契(%d) = %llu\n", n, fibonacci(n));return 0;
}
-
用 C 语言写一个递归算法求 N!(华辰泰尔)
代码解答:
#include <stdio.h>// 递归计算阶乘函数
unsigned long long factorial(int n) {if (n <= 1) {return 1; // 基本情况:0! 和 1! 都是 1}return n * factorial(n - 1); // 递归调用
}int main() {int n;printf("请输入一个数字:");scanf("%d", &n);if (n < 0) {printf("输入的数字不合理\n");} else {printf("%d! = %llu\n", n, factorial(n));}return 0;
}
成果展示:

三、总结
1. 学习内容概述
指针的使用和传递
学习了如何在函数中使用指针并传递变量地址,包括传递一维数组和二维数组的指针,使函数可以直接操作数组内容。
函数参数的传递方式
重点理解了C语言中函数参数的两种传递方式:**值传递**和**地址传递**。值传递会传递变量的副本,而地址传递可以通过传递指针修改原始变量的值。
递归函数设计
学习了递归函数的设计与实现,递归是函数调用自身的过程,常用于解决具有重复结构的问题,如计算阶乘、斐波那契数列等。
指针函数和函数指针
了解了指针函数和函数指针的区别与用法,函数指针可以指向一个函数,通过指针调用函数;而指针函数则是返回指针的函数。
2. 学习难点
函数指针的使用
虽然已经理解了函数指针的概念,但在实际应用中,如何将函数指针与复杂的函数调用机制结合使用,尤其是在函数作为参数传递时,仍然是一个难点。
指针与数组的区别
指针和数组在C语言中关系密切,但它们的区别和使用场景不同,特别是多维数组指针的传递和解引用,容易让初学者混淆。
递归函数的调试
递归函数虽然简单但不易调试,特别是在递归终止条件不明确或递归深度过大时,容易导致栈溢出或程序崩溃。
3. 注意事项
值传递与地址传递的选择
在编写函数时,需要根据实际需求选择合适的参数传递方式。如果函数需要修改调用者的变量,应该使用地址传递(即传递指针);如果只需要处理变量的副本,则可以使用值传递。
指针函数与函数指针的区别
指针函数是返回指针的函数,而函数指针是指向函数的指针,虽然概念接近,但要注意区分使用场景。例如,函数指针通常用于回调函数,而指针函数则多用于动态分配内存后返回地址。
递归函数的边界条件
在编写递归函数时,一定要明确递归的终止条件,否则会导致无限递归,程序会因栈溢出而崩溃。可以通过测试递归的中间结果来验证逻辑的正确性。
数组指针传递时的边界检查
传递数组指针时,要确保数组在函数内操作时没有越界,尤其是传递多维数组时,要明确数组的大小和结构。
4. 未来学习的重点
深入研究函数指针的应用场景
函数指针在回调机制和动态函数调用中有着重要作用。未来的学习可以深入研究函数指针在操作系统、事件驱动编程和动态库中的应用。
递归与迭代的比较
递归虽然简洁,但在某些情况下性能不佳,可能导致栈溢出。未来学习中可以探索递归与迭代的性能对比,并掌握如何将递归转换为迭代。
结构体与动态内存管理结合使用
指针和结构体的结合非常强大,尤其是在处理复杂数据结构时。未来可以深入学习如何使用指针动态创建结构体,并结合`malloc`和`free`进行内存管理。
相关文章:
Linux和C语言(Day11)
一、学习内容 讲解有参函数 形参 和 实参 形参——定义时的参数,形式上的参数,没有实际意义,语法上必须带有数据类型 void fun(int a,int b); void fun(int a[],int n); void fun(char *s); 可以是:变量、数组、指针 实参——调用…...
使用Zlib库进行多文件或者多文件夹的压缩解压缩
zlib库可在git上自己clone下来然后使用cmake工具生成解决方案,编译、生成zlib二进制文件。然后将zlib库引入项目: //zlib库支持 #include "../zlib/include/zlib.h" #ifdef _DEBUG #pragma comment(lib, "../zlib/lib/zlibd.lib") …...
CSGHub携手Nvidia NIM、阿里计算巢打造企业级私有化部署解决方案
强强联合 人工智能与大数据的迅速发展,大模型的推理应用和资产管理已成为企业数字化转型的重要组成部分,企业正寻求高效、安全的AI模型部署解决方案。为应对日益增长的计算需求和复杂的数据管理挑战,CSGHub、Nvidia和阿里云计算巢强强联手&a…...
opencv的球面投影
cv::detail::SphericalProjector 在全景图像拼接任务中,可能需要对多个图像进行球面投影以实现无缝拼接。每个cv::detail::SphericalProjector可以负责一个图像的球面投影操作。通过将多个这样的投影器存储在std::vector中,可以对一组图像依次进行投影处…...
5. 去中心化应用(dApp)
去中心化应用(dApp) 去中心化应用(dApp)是基于区块链技术构建的应用程序,其核心特性是去中心化、透明和开放。dApp与传统应用有许多显著的区别,它们在实现和功能上都带来了新的变革。以下是对dApp的详细介…...
k8s服务发布Ingress
Kubernetes暴露服务的方式目前只有三种:LoadBlancer Service、NodePort Service、Ingress,通俗来讲,ingress和之前提到的Service、Deployment,也是一个k8s的资源类型,ingress用于实现用域名的方式访问k8s内部应用。 In…...
区块链学习笔记1--比特币
区块链是分布式数据存储、点对点传输、共识机制、加密算法等计算机技术的新型应用模式。 从狭义上来说:区块链是一种按照时间顺序将数据区块以顺序相连的方式组合成的一种链式数据结构,并以密码学的方式保证的不可篡改和不可伪造的分布式账本。 意思就是…...
在 Vite 项目中自动为每个 Vue 文件导入 base.less
在 Vue.js 项目中,使用 Less 作为 CSS 预处理器时,我们通常会创建一个全局的样式文件(如 base.less),用于存放一些全局变量、混合、通用样式等。为了避免在每个 Vue 组件中手动导入这个文件,我们可以通过配…...
RUST 学习之全局变量
RUST 全局变量 rust 全局变量编译期初始化的全局变量静态常量静态变量原子类型的静态变量 运行期初始化的全局变量lazy_staticBox::leakOnceCell & OnceLock 参考文档 rust 全局变量 编译期初始化的全局变量 静态常量 在编译期初始化,所以其赋值只能是表达式…...
代码随想录八股训练营第三十九天| C++
前言 一、说一下 lambda函数? 1.1.Lambda 函数的一般语法如下: 1.2.捕获子句: 二、C 怎么实现一个单例模式? 2.1.懒汉式(线程不安全): 2.2.饿汉式(线程安全): 2.3.双重检查锁定ÿ…...
服务网关工作原理,如何获取用户真实IP?
文章目录 一、什么是网关二、网关工作原理 (★)三、SpringCloud Gateway3.1 Gateway 简介3.2 Gateway 环境搭建3.3 自定义路由规则 (★)3.4 局部过滤器3.5 全局过滤器(案例:获取用户真实IP地址) (★) 补充1:不同类型的客户端如何设…...
单链表的实现(C语言)
目录 1.单链表 1.1 实现单链表 1.1.1 文件创建 1.1.2 链表功能了解 1.1.3 链表的结点 1.1.4 链表的函数声明 1.1.5 链表功能的实现 链表是一种链式结构,物理结构不连续,逻辑结构是连续的,在计算机中链表的实际存储是按照一个结点内存放…...
sql语句的训练2024/9/9
1题 需要看清思路:不是将数据库中的device_id的名字改为user_infors_example,而是在查找的时候,需要将device_id看成user_infors_example来进行查找。 答案 select device_id AS user_infos_example FROM user_profile limit 2 2 当固定查找…...
【QT】常用控件-下
欢迎来到Cefler的博客😁 🕌博客主页:折纸花满衣 🏠个人专栏:QT 目录 👉🏻QComboBox👉🏻 QSpinBox👉🏻QDateTimeEdit👉🏻QD…...
828华为云征文|华为云Flexus X实例docker部署Jitsi构建属于自己的音视频会议系统
828华为云征文|华为云Flexus X实例docker部署Jitsi构建属于自己的音视频会议系统 华为云最近正在举办828 B2B企业节,Flexus X实例的促销力度非常大,特别适合那些对算力性能有高要求的小伙伴。如果你有自建MySQL、Redis、Nginx等服务的需求&a…...
25虾皮笔试shopee笔试测评sea笔试测评题型
虾皮笔试shopee笔试测评用的自己的笔试系统,全英文笔试: 1.Numerical Reasoning Test:10题,言语推断和数学计算 2. Verbal Reasoning Test:10题,言语理解,每题一段英文材料,选对错…...
启明云端乐鑫代理商,乐鑫ESP32无线芯片方案,物联网设备WiFi联动控制
随着智能和远程技术的飞速发展,物联网(IoT)逐渐出现在我们生活的每一个角落。乐鑫以其创新的无线通信技术,正成为智能家居、工业自动化和医疗设备等领域的推动者。 无线WiFi芯片模组不仅提供了强大的数据处理能力,还赋予了设备以直观的交互方…...
希尔排序/选择排序
前言: 本篇主要对常见的排序算法进行简要分析,代码中均以数组 arr[] { 5, 3, 9, 6, 2, 4, 7, 1, 8 } 为例,进行升序排列。 常见的排序算法有如下: 选择排序中,直接选择排序没有任何实际与教育意义,而堆排…...
漫谈设计模式 [16]:中介者模式
引导性开场 菜鸟:老鸟,我最近在开发一个聊天应用的时候遇到了点问题。每个用户都需要与其他用户直接通信,这让我在代码中写了很多复杂的逻辑来管理这些联系。这样下去,代码越来越难维护了。你有什么建议吗? 老鸟&…...
深度学习-物体检测YOLO(You only look once)
目录 一:YOLO算法的网络结构 流程 1.图像分割 2.图片在网格中的处理 3.非极大值抑制 二:训练 三:分类误差 四:与Faster R-CNN对比 一:YOLO算法的网络结构 GooleNet4个卷积2个全连接层 流程 输入原始图片resize到…...
铭豹扩展坞 USB转网口 突然无法识别解决方法
当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…...
多模态2025:技术路线“神仙打架”,视频生成冲上云霄
文|魏琳华 编|王一粟 一场大会,聚集了中国多模态大模型的“半壁江山”。 智源大会2025为期两天的论坛中,汇集了学界、创业公司和大厂等三方的热门选手,关于多模态的集中讨论达到了前所未有的热度。其中,…...
iOS 26 携众系统重磅更新,但“苹果智能”仍与国行无缘
美国西海岸的夏天,再次被苹果点燃。一年一度的全球开发者大会 WWDC25 如期而至,这不仅是开发者的盛宴,更是全球数亿苹果用户翘首以盼的科技春晚。今年,苹果依旧为我们带来了全家桶式的系统更新,包括 iOS 26、iPadOS 26…...
React Native 导航系统实战(React Navigation)
导航系统实战(React Navigation) React Navigation 是 React Native 应用中最常用的导航库之一,它提供了多种导航模式,如堆栈导航(Stack Navigator)、标签导航(Tab Navigator)和抽屉…...
在HarmonyOS ArkTS ArkUI-X 5.0及以上版本中,手势开发全攻略:
在 HarmonyOS 应用开发中,手势交互是连接用户与设备的核心纽带。ArkTS 框架提供了丰富的手势处理能力,既支持点击、长按、拖拽等基础单一手势的精细控制,也能通过多种绑定策略解决父子组件的手势竞争问题。本文将结合官方开发文档,…...
UDP(Echoserver)
网络命令 Ping 命令 检测网络是否连通 使用方法: ping -c 次数 网址ping -c 3 www.baidu.comnetstat 命令 netstat 是一个用来查看网络状态的重要工具. 语法:netstat [选项] 功能:查看网络状态 常用选项: n 拒绝显示别名&#…...
前端开发面试题总结-JavaScript篇(一)
文章目录 JavaScript高频问答一、作用域与闭包1.什么是闭包(Closure)?闭包有什么应用场景和潜在问题?2.解释 JavaScript 的作用域链(Scope Chain) 二、原型与继承3.原型链是什么?如何实现继承&a…...
A2A JS SDK 完整教程:快速入门指南
目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库ÿ…...
Webpack性能优化:构建速度与体积优化策略
一、构建速度优化 1、升级Webpack和Node.js 优化效果:Webpack 4比Webpack 3构建时间降低60%-98%。原因: V8引擎优化(for of替代forEach、Map/Set替代Object)。默认使用更快的md4哈希算法。AST直接从Loa…...
从 GreenPlum 到镜舟数据库:杭银消费金融湖仓一体转型实践
作者:吴岐诗,杭银消费金融大数据应用开发工程师 本文整理自杭银消费金融大数据应用开发工程师在StarRocks Summit Asia 2024的分享 引言:融合数据湖与数仓的创新之路 在数字金融时代,数据已成为金融机构的核心竞争力。杭银消费金…...
