C语法Day1-函数
# 小试牛刀
开始实操!
# 1.1、程序入口函数
所有的软件程序通常需要一个入口函数,就像大门一样,而C语言的大门就是 main 函数
一个空房子的C软件最基本的代码长这样:
#include <stdio.h> // 环境中自带标准库文件,直接引用即可
int main() {
return 0;
}
不过上面这个程序啥也没干,直接返回了。
# 1.2第一行代码:打印信息
#include <stdio.h> // 环境中自带标准库文件,直接引用即可
int main() {
/* 我的第一个 C 程序 */
printf("Hello, World! \n");
return 0;
}
# 1.3运行:在线演示
随便找一个可以在线编译的网站即可验证效果:
https://www.nhooo.com/tool/c/
# 1.4运行:终端命令
1、需要安装gcc环境,xcode环境自带, 新建文件名 main.c ,保存上述代码。
$ ls
main.c
2、终端命令窗口查看本地gcc 环境:which gcc
$ which gcc
/usr/bin/gcc
3、编译代码,生成可执行文件 a.out
# 编译代码, -o 表示产物名称
$ gcc main.c -o main.out
# 产看结果
$ ls
main.out main.c
# 执行可执行文件a.out
$ ./main.out
Hello, World!
我们看到最后打印了Hello, World!, 自此,一个简单的程序完成
# 1.5、简单运算:数据和字符
数据类型
常用基本数据类型占用空间(64位机器为例)
char : 1个字节
int :4个字节
float:4个字节
double:8个字节
基本类型书写
【整数】
a,默认为10进制 ,10 ,20。
b,以0开头为8进制,045,021。
c.,以0b开头为2进制,0b11101101。
d,以0x开头为16进制,0x21458adf。
int a = 3;
【小数】
单精度常量:2.3f 。
双精度常量:2.3,默认为双精度。
float e = 3.12578; // 定义双精度浮点数
字符型常量:
用英文单引号括起来,只保存一个字符'a'、'b' 、'*'
还有转义字符; 换行'\n' 、制表符'\t'
char name = 'A';
printf("name = %c\n", name); // 打印字符
字符串常量:
用英文的双引号引起来 可以保存多个字符:"abc"。
⚠️ c语言中没有 String 字符串类型,而是通过char类型的数组代替,但是数组的最后需要结尾符号 '\0'
方式一
char name[] = "张三";
方式二
char name[] = {'A', 'B', '\0'};
方式三
⚠️ 也可以指定长度,但是不能比最大长度短,否则会越界错误
char name[2] = {'A', 'B'}; // 长度设置1就会报错
printf("name = %s\n", name); // 打印字符串
修改main.c 文件:
#include <stdio.h>
int main()
{
int a = 3, b = 5; // 定义并初始化a和b
int c = a + b;
printf("c = %d\n",c);
float e = 3.12578; // 定义浮点数
printf("f = %.2f\n",e); // 保留2位小数
return 0;
}
运行结果:
将main.c文件编译为main可执行文件,并执行:
$gcc -o main main.c -lm
$./main
c = 8
f = 3.12
# 1.6、函数声明和调用
三步骤:函数声明 - 函数实现 - 函数的调用
#include <stdio.h>
/* 函数声明 */
int max(int num1, int num2);
int main()
{
int result;
result = max(3, 6); // 函数的调用
printf("最大值为: %d\n",result);
return 0;
}
/* 函数实现:求最大值 */
int max(int num1, int num2)
{
return num1 > num2 ? num1 : num2;
}
打印结果:最大值为: 6
# 文件引用
前面介绍了单个文件的编译执行,现在介绍多个文件相互 依赖
# 示例1:引用外部文件变量
文件 class.c 获取 文件 student.c 中学生的年龄、班级和姓名,然后打印出来
class.c
#include <stdio.h>
int main()
{
// 仅声明
extern int s_age;
extern char s_name[], class_name[];
printf("%s, 学生:%s, 年龄:%d岁 \n",s_name, class_name, s_age);
return 0;
}
student.c
#include <stdio.h>
// 定义并初始化
int s_age = 6;
char class_name[] = "一年级三班", s_name[] = "张三";
# 运行结果:
将class.c文件编译为class可执行文件,并执行:
$ gcc class.c student.c -o class1
$ ./main
c = 8
f = 3.12
# 变量声明
extern 存储类用于提供一个全局变量的引用,全局变量对所有的程序文件都是可见的。当您使用 extern 时,对于无法初始化的变量,会把变量名指向一个之前定义过的存储位置。
变量的声明有两种情况:
- 1、一种是需要建立存储空间的。例如:int a 在声明的时候就已经建立了存储空间。
- 2、另一种是不需要建立存储空间的,通过使用extern关键字声明变量名而不定义它。 例如:extern int a 其中变量 a 可以在别的文件中定义的。
- 除非有extern关键字,否则都是变量的定义。
extern int i; //声明,不是定义
int i; //声明,也是定义
extern int d = 3, f = 5; // d 和 f 的外部声明与初始化?
int a = 3, b = 5; // 定义并初始化 d 和 f
byte z = 22; // 定义并初始化 z
char x = 'x'; // 变量 x 的值为 'x'
没有extern 就是简单的变量定义,加上没有extern 的定义就是外部声明的,也就是说如果当前文件内没有定义和赋值的话,会去外部文件搜索定义实现,不会报错,除非找不到。
# 示例2:引用外部文件函数
入口文件 test_add.c
#include <stdio.h>
/*定义两个全局变量*/
int x=1;
int y=2;
/*
当您在一个源文件中定义函数且在另一个文件中调用函数时,函数声明是必需的。在这种情况下,您应该在调用函数的文件顶部声明函数。
这一行不能漏,否则在下面赋值时会报错,error: implicit declaration of function 'addtwonum' is invalid in C99 [-Werror,-Wimplicit-function-declaration]
*/
int addtwonum(); // 函数声明
int main(void)
{
int result;
result = addtwonum();
printf("result 为: %d\n",result);
return 0;
}
addtwonum.c 文件
#include <stdio.h>
/*外部变量声明,当前文件没有定义时就会去外部寻找值*/
extern int x ;
extern int y ;
int addtwonum()
{
return x+y;
}
# 运行结果:
// 编译
mac$ gcc addtwonum.c test_add.c -o main_add
// 执行文件
mac$ ./main_add
// 结果输出
result 为: 3
# 附:外部声明可以在当前实现吗?
1、当前文件如果修改为
extern int x = 4;
extern int y = 7;
会报错,外部test_add.c 定义和实现了这个变量值,这里初始化有问题:
addtwonum.c:3:12: warning: 'extern' variable has an initializer [-Wextern-initializer]
extern int x = 4;
^
2、如果当前在定义一下变量:
extern int x ;
extern int y ;
int x=4;
int y=2;
也是会报错,提示重复定义,因为外部test_add.c 已经定义
duplicate symbol '_y' in:
/var/folders/84/yp82d91d5jvdwmx6kd1td9640000gn/T/addtwonum-f59002.o
/var/folders/84/yp82d91d5jvdwmx6kd1td9640000gn/T/test_add-a2b257.o
duplicate symbol '_x' in:
/var/folders/84/yp82d91d5jvdwmx6kd1td9640000gn/T/addtwonum-f59002.o
/var/folders/84/yp82d91d5jvdwmx6kd1td9640000gn/T/test_add-a2b257.o
3、删掉入口处定义,改在addtwonum.c 定义 也不行
addtwonum.c:3:12: warning: 'extern' variable has an initializer [-Wextern-initializer]
extern int x = 4;
^
# 常量
在 C 中,有两种简单的定义常量的方式:
1、使用 #define 预处理器(宏定义)
2、使用 const 关键字
#include <stdio.h>
// 1、使用 **#define** 预处理器 定义常量
#define LENGTH 10
#define WIDTH 5
#define NEWLINE '\n'
int main() {
//2、const语法格式 定义常量 : const type variable = value;
const int LENGTH = 10;
const int WIDTH = 5;
const char NEWLINE = '\n';
int area;
area = LENGTH * WIDTH;
printf("value of area : %d", area);
printf("%c", NEWLINE);
return 0;
}
# 判断语句
# if-else
// if-elseif-else
int a = 100;
if( a < 20 )
{
printf("a 小于 20\n" );
return; // 条件控制,结束当前函数,不再执行 if 语句
}
else if( a < 80 )
{
printf("a 小于 80\n" );
}
else
{
printf("a 大于等于 80\n" );
}
# switch
// 等级分数
char grade = 'A';
switch(grade)
{
case 'A' :
printf("优秀!\n" );
break; // 条件控制,结束 switch,继续后面代码
case 'B' :
case 'C' :
printf("很好\n" );
break;
default :
printf("加油\n" );
}
# 循环控制语句
# while
int a = 5;
/* while 循环:先判断括号内条件是否成立,再执行 */
while( a > 0 ) // 当 a <= 0时不满足循环条件,则循环结束
{
printf("a 的值: %d\n", a);
a--;
}
# for 循环
/* for 循环 */
for( int a = 0; a < 5; a++ )
{
printf("a 的值: %d\n", a);
}
# do...while 循环
int a = 5;
/* 和while 循环的区别是:
do-while 循环:先至少执行一次,后面再判断条件
while 循环:先判断条件,条件成立再执行
*/
do {
printf("a 的值: %d\n", a);
a--;
}while(a > 0);
# 循环控制语句:break、continue
break: 终止循环或 switch 语句 ,继续后面代码
continue:表示当前循环内的后续代码不执行,直接判断下一次循环条件
/* for 循环 */
for( int a = 100; a > 0; a-- )
{
printf("a 的值: %d\n", a);
if (a > 80) {
continue;
}
if (a >= 60) {
break;
}
}
# 循环控制语句:goto
在代码前面加上 “ 标签名:” 格式即可通过名称跳转
⚠️:不建议 c 大量使用goto语句,不好调试。但是在汇编中大量使用类似逻辑
int a = 50;
printf("a 的值: %d\n", a);
if (a > 40) {
goto ENDIF; // 直接跳转到 ENDIF 执行后面的代码
}
a += 50;
printf("a 的值: %d\n", a);
ENDIF: printf("if 条件被强制跳出结束 \n");
# 函数
# 函数定义格式
C 语言中的函数定义的一般形式如下:
// 返回类型 方法名称 参数
return_type function_name( parameter list )
{
// 方法主体
body of the function
}
# 示例:
// 1、没有参数
int zero()
{
return 0;
}
// 2、有参数(参数是值传递),有返回值
int max(int num1, int num2)
{
return num1 > num2 ? num1 : num2;
}
// 3、有参数(参数是值传递),没有返回值
void p_max(int num1, int num2)
{
printf("value of max : %d", num1 > num2 ? num1 : num2);
}
// 4、没有参数,没有返回值
void ssyhello()
{
printf("hello");
}
// 5、没有函数主体
{
printf("hello");
}
// 6、有参数(参数是 指针传递)
void swap(int *x, int *y)
{
// ...
}
# 函数声明
函数调用前需要先进行函数声明,函数声明会告诉编译器函数名称及如何调用函数。
比如求最大值函数:
int max(int num1, int num2)
{
return num1 > num2 ? num1 : num2;
}
针对上面定义的函数 max(),以下是函数声明:
int max(int num1, int num2);
在函数声明中,参数的名称并不重要,只有参数的类型是必需的,因此下面也是有效的声明:
int max(int, int);
# 函数参数
方式一、值传递 (int a)
将变量(或对象)的值传递给其它函数,函数内拿到的参数对象只能a取值,不能修改a
方式二、指针传递 (int *a)
将变量(或对象)的地址传递给其它函数,函数内拿到的参数对象不仅能取a值,也能修改a
#include <stdio.h>
/* 函数声明 */
void swap(int *x, int *y);
int main ()
{
/* 局部变量定义 */
int a = 100;
int b = 200;
/* 调用函数来交换值
* &a 表示指向 a 的指针,即变量 a 的地址
* &b 表示指向 b 的指针,即变量 b 的地址
*/
swap(&a, &b); //通过指针传递方式
return 0;
}
/* 函数实现 */
void swap(int *x, int *y)
{
int tmp = *x;
x = y;
y = &tmp;
printf("x: %d; y: %d\n",*x, *y);
}
# 可变数量的参数
有时,您可能会碰到这样的情况,您希望函数带有可变数量的参数,而不是预定义数量的参数。
C 语言为这种情况提供了一个解决方案,它允许您定义一个函数,能根据具体的需求接受可变数量的参数。
下面的实例演示了这种函数的定义。
int func(int, ... ) // 可变数量参数
{
...
}
int main()
{
func(2, 2, 3);
func(3, 2, 3, 4);
}
请注意,函数 func() 最后一个参数写成省略号,即三个点号(...),省略号之前的那个参数是 int,代表了要传递的可变参数的总数。
为了使用这个功能,您需要使用 stdarg.h 头文件,该文件提供了实现可变参数功能的函数和宏。具体步骤如下:
- 定义一个函数,最后一个参数为省略号,省略号前面可以设置自定义参数。
- 在函数定义中创建一个 va_list 类型变量,该类型是在 stdarg.h 头文件中定义的。
- 使用 int 参数和 va_start 宏来初始化 va_list 变量为一个参数列表。宏 va_start 是在 stdarg.h 头文件中定义的。
- 使用 va_arg 宏和 va_list 变量来访问参数列表中的每个项。
- 使用宏 va_end 来清理赋予 va_list 变量的内存。
现在让我们按照上面的步骤,来编写一个带有可变数量参数的函数,并返回它们的平均值:
# 实例
#include <stdio.h>
#include <stdarg.h>
double average(int num,...) // num 表示参数的个数
{
va_list valist;
double sum = 0.0;
int i;
/* 为 num 个参数初始化 valist */
va_start(valist, num);
/* 访问所有赋给 valist 的参数 */
for (i = 0; i < num; i++)
{
sum += va_arg(valist, int);
}
/* 清理为 valist 保留的内存 */
va_end(valist);
return sum/num;
}
int main()
{
printf("Average of 2, 3, 4, 5 = %f\n", average(4, 2,3,4,5));
printf("Average of 5, 10, 15 = %f\n", average(3, 5,10,15));
}
当上面的代码被编译和执行时,它会产生下列结果。应该指出的是,函数 average() 被调用两次,每次第一个参数都是表示被传的可变参数的总数。省略号被用来传递可变数量的参数。
Average of 2, 3, 4, 5 = 3.500000
Average of 5, 10, 15 = 10.000000
# 递归
递归指的是在函数的定义中使用函数自身的方法,并在指定条件下退出。
求数的阶乘:
#include <stdio.h>
double factorial(unsigned int i)
{
if(i <= 1)
{
return 1;
}
return i * factorial(i - 1);
}
int main()
{
int i = 10;
// 求:10*9....*2*1
printf("%d 的阶乘为 %.f\n", i, factorial(i));
return 0;
}
运行结果: 10 的阶乘为 3628800