三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

c语言 第五章下:函数(持续更新中……)

c语言 第五章下:函数(持续更新中……)

7.函数与一维整型数组

传参方式:

定义:

返回值 函数名(int数组名[], int len
{
// sizeof(数组名)----sizeof(int *) ---->8字节
//外面数组怎么使用,函数内数组就怎么使用
}
调用:
函数名(数组名, 数组长度);
void print_array(int a[], int len)
{
}
print_array(a, len);
数组的数组名是数组的首元素地址。
【注意】
1.数组作为函数参数传递时,形参必须要是数组类型:int a[], []中的常量可以有,也可以省略。
2.数组作为函数参数传递时,本质传递的是数组的首元素地址,所以形参本质是一个地址,是数组的
起始地址。
3.由于数组名传参本质传递的是首地址,所以必须要传递整形数组的长度。
4.数组作为函数参数传递给被调函数后,函数内部的使用方法和外面使用一样,除了sizeof(数组
名);
【练习】封装函数,计算所有元素之和。实现数组的冒泡排序,选择排序并输出。
【练习】封装函数实现数组的逆序
【练习】封装函数将整形数组中的数据做如下转换后输出: 0,1,2,3,4,5,6,7,8,9。

n = 1: 9, 0,1,2,3,4,5,6,7,8 ; n = 2:8, 9, 0,1,2,3,4,5,6,7 ;n = 3:7, 8, 9, 0,1,2,3,4,5,6

或者

8.函数和一维字符型数组

传参:

char数组名[100];
定义:
返回值 函数名(char数组名[ ])
{
函数体;
}
调用:
函数名(数组名);
void print_str(char str[ ])
{
}
print_str(str);
【注意】
1.一维字符型数组作为函数参数传递时,只需要传递数组即可,不需要传递长度,因为字符型型数组
保存的字符串,末尾有'\0'结束标志。
2.数组作为函数参数传递时,本质传递的是数组的首元素地址(在被调函数中:sizeof(数组名形参) ——> 本质求地址在计算机内存中的大小)。
【练习】封装mystrlen函数,返回数组中有效字符的个数。

【练习】封装函数,将源字符型数组的元素拷贝/拼接到目标字符型数组,比较大小。

9.函数和二维整型数组

定义:

返回值 函数名(int数组名[][列数]int行数)
{
}
调用:
函数名(数组名,行数)
int a[3][4] = {0};
void print_2D_array(int a[][4], int hang)
{
}
printf_2D_array(a, sizeof(a)/sizeof(a[0]));
【注意】
1.二维整形数组传参时,形参必须说明是二维数组,行数可以省略,列数不能省略。
形参:int a[ ][列数]
2.在自定义的函数中,行数无法计算,所以可以通过参数的方式传递
3.在自定义的函数中,列数可以计算:sizeof(a[0]) / sizeof(a[0][0]);
【练习】封装一个函数,打印二维整型数组并求求边行列之和。

10.函数和二维字符型数组

传参:
定义:
返回值 函数名(char数组名[ ][列数], int行数)
{
}
调用:
函数名(数组名, 行数);
char str[3][100] = {0};
void print_2DC_array(char str[][100], int hang)
{
}
print_2DC_array(str, 3);
【练习】封装函数,打印二维字符型数组,返回最大的字符串的行号。
或者

【练习】封装一个函数myatoi

【练习】封装函数,实现myitoa

考虑负数和0:

11.变量的作用域和生命周期

标识符作用域(空间角度)

作用域:标识符/变量可以被使用的范围
局部作用域:在{}内部被定义的变量的作用范围
局部变量:定义在局部作用域中的变量(函数内部定义的变量、函数形参)
全局作用域:可使用范围在全局,{}外面。比如:定义函数
全局变量:定义在全局作用域的变量。
【注意】
1.标识符必须先定义再使用。
2.在相同作用域中,不能定义同名标识符(变量、函数)。
3.在具有包含关系的作用域中,出现了同名标识符,外层标识符在内层不可见(就近原则)

变量的生命周期(时间角度)

变量的生命周期:从变量的空间开辟到空间回收对应的生存期。
动态生命周期:
局部变量:在被定义的时候开辟空间,超出其作用域范围空间被回收。
静态生命周期:和程序运行的生命周期一样长
全局变量:从程序运行之前空间开辟,程序运行结束空间被回收。
static修饰的静态变量:从程序运行之前空间开辟,程序运行结束空间被回收。
a.作用域没发生变化,局部作用域。
b.生命周期变为静态生命周期。
【总结】
局部变量:
1.作用域在局部,超出局部作用域无法访问;
2.动态生命周期,定义时开辟空间,超出作用范围空间被回收;
3.局部变量保存在栈区,未被初始化为随机值。
全局变量/static静态变量:
1.全局变量作用域在全局,static静态变量作用域在局部;
2.静态生命周期:程序运行之前空间开辟,程序运行结束空间被回收
3.全局变量/static静态变量保存在数据区。
4.全局变量/static静态变量未被初始化时,默认为0
5.未初始化的全局变量/static静态变量存储在.bss段,.bss段系统默认清0.
6.已初始化的全局变量/static静态变量存储在.data段。

③变量的存储类型

int a;

存储类型 数据类型 变量名;
变量的存储类型:
auto默认存储类型
static
1. static修饰局部变量,静态变量,具备静态生命周期(延长局部变量的生命周期)。
2. static修饰函数:该函数只允许在当前原文件中被调用。
3. static修饰全局变量:该变量只允许在当前文件中使用。
register
寄存器变量。
register修饰变量时,建议编译器将该变量存储在寄存器中。
register int i = 0;
register仅仅是一个建议。
int *p;
p = &i; //x i是寄存器变量,无法取址
extern
修饰全局变量/函数声明。
1. extern修饰全局变量,用来声明全局变量,表示允许被外部的.c使用。
2. extern声明函数,表示该函数允许被外部的.c使用。

12.预处理命令

gcc的编译的四个步骤:

gcc main.c -----> a. out

a.预处理

展开和#相关的代码:展开头文件、展开宏、处理预处理命令(条件编译)
#include <stdio.h>
#define PI 3.1415
#if 0
#endif
gcc -E main.c -o main.i

b.编译

c语言程序转换成汇编指令
gcc -S main.i -o main.s

c.汇编

将汇编指令转换成二进制可执行程序
gcc -c main.s -o main.o

d.链接

负责链接系统的函数/库函数的实现(链接函数)。
gcc main.o -o main

预处理命令

a.宏定义

不带参宏:——>符号常量
#define标识符 值
#define PI 3.1415
带参宏(宏函数)
#define标识符(参数1, 参数2,参数3)宏的实现
#define PI (3.14)
#define ADD(a, b) ((a)+(b))
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define AREA(r) ((PI) * (r) * (r))
【注意】
1.宏作为预处理命令,负责在预处理阶段展开,不做运算,只是代码的替换
2.宏标识符最好大写,定义宏时不能加分号
3.宏只是代码的替换,能加()就加括号。
4.宏和函数的区别
宏是预处理阶段的代码替换、会使得代码体积增大
函数是在运行时的调用,需要保护现场和恢复现场

b.头文件包含

#include <stdio.h>
#include "head.h"
头文件放:
1.可以有包含的其他头文件
2.声明的数据类型
3.宏定义
4.函数和全局变量的声明
【注意】
1. <>表示包含的是系统中的头文件,让编译器从系统路径下找:/usr/include
2. ""表示包含的头文件是用户自己定义的头文件,让编译器从当前路径下寻找。

c.条件编译

=>头文件中防止头文件重复包含
#ifndef
#define
//头文件内容
#endif
根据有没有被定义出来决定该头文件中的内容要不要加入到编译中。
=>条件编译
#if逻辑结果表达式
//代码
#endif
根据逻辑结果表达式的结果,决定中间的代码要不要加入到编译中
//代码屏蔽
#if 0
//代码
#endif
=>条件编译
#ifdef
//代码1
#else
//代码2
#endif
根据有没有被定义决定是否编译代码1或者代码2
#ifndef
//代码1
#else
//代码2
#endif
← 返回列表