一、shell中函数的语法格式
function 函数名()
{
函数体
}
注意:
1>shell中的函数使用function进行修饰,function可写可不写,建议写;
2>shell中的函数没有返回类型,如果接受shell函数的返回值呢?
2.1>shell 中定义的变量默认就是全局的变量,可以直接使用
2.2>如果shell中的变量使用local 定义局部的变量
可以使用return 进行返回,return返回只能返回0-255之间的数。
接受返回值使用$?,$?可以获取上一条指令的执行结果。
2.3>在函数中可以使用echo命令输出函数的返回值,接收函数的返回值时,使用命令置换符``,获取函数的返回结果。
3>函数的形参
shell的函数不需要形参列表,但是可以给函数传递实参。
在shell函数内如何获取给shell函数传递的实参呢?使用位置变量
$0----->执行shell 脚本文件的名字
$1----->第一个实参
$2----->第二个实参
.......
${n}---> 第n个实参 (n>10,需要加{ } )
$*或¥@----> 获取所有的实参
4>shell 中的函数依然要向C中的函数遵循先声明,后使用的原则
但是shell 中的函数都是声明和定义写到一起的,及shell 中的函数的定义应该写到函数调用的前边。
5>shell中的函数的调用
函数名 实参列表
例:将打印⼆进制的代码封装为函数
#!/bin/bash
# 函数的封装: 功能 参数 返回值 (函数的三要素)
# 调用库函数: 研究函数的功能 参数 返回值
function convert() {
local ret=''
local num=$1
# seq 用来输出序列化 seq 开始 间隔 结束 默认是间隔1
for p in `seq 1 32`
do
if (($num & 0x80000000))
then
ret="${ret}1"
else
ret="${ret}0"
fi
num=$(($num<<1))
done
echo $ret
}
read -p "请输入一个无符号的整数 > " num
value=`convert $num`
echo "0b${value}"
二、C高级------宏定义
1.常量宏
#define DEBUG 3.14
#define M 3
#define N 4
#define ADD ((M)+(N))
1>宏定义在预处理阶段进行替换
2>宏定义中有复杂的表达式,多多使用()
3>进行宏定义时,默认不允许出现换行,如果使用换行,必须在每行的结尾加一个换行符\
2.使用宏函数,最后一个表达式的结果作为宏函数的返回值
#define MAX(a,b) ({a>b?a:b;})
1>宏函数在底层封装时经常使用。
#include MAX(a,b) ({ (a) > (b) ? (a) : (b) ;})
#include MIN(a,b) ({int ret;\
if(a>b)\
ret = b;\
else ret =a;\
ret; })
3.宏定义和do { }while(0);结合使用 ——>在底层的封装经常使用,宏定义多条语句时
#include <stdio.h>
// 宏定义和do{}while(0)结合
#define print() do{ \
printf("input error"); \
printf("please try again input > ") \
} while(0)
4.宏定义中使用#----->将宏定义的参数转换成一个字符串
#include <stdio.h>
// 将n转换成一个字符串 #n等价于"n"
#define str(n) #n
#define string "helloworld"
int main(int argc, const char *argv[])
{
// 等价于 printf("helloworld\n")
printf(str(helloworld\n));
// 等价于printf("%s\n", "zhoukai")
printf("%s\n", str(zhoukai));
printf("%s\n", string);
return 0;
}
5.宏定义中使用两个##------>实现宏定义中的参数的拼接
#include <stdio.h>
// 实现宏中两个参数的拼接
#define name(a,b) a##b
#define uint32_t unsigned int
#define uint16_t unsigned short
// 使用宏定义编写一个模板函数
#define MAX(T) T max_##T(T a, T b) { \
return (a>b?a:b); \
}
// 调用宏函数,定义两个函数
MAX(double)
MAX(int)
#define max(T) max_##T
int main(int argc, const char *argv[])
{
name(uint, 32_t) a = 100;
name(uint, 16_t) b = 200;
printf("max double = %f\n" , max_double(3.14, 5.67));
printf("max int = %d\n" , max_int(3, 1));
printf("max double = %f\n" , max(double)(3.14, 5.67));
printf("max int = %d\n" , max(int)(3, 1));
return 0;
}
6.宏定义的预定义
// 当行注释
/**/ 多行注释, 注不可以嵌套
#if 0/1 #else #endif 多行注释
5.1> #ifndef
#define 宏名 // 宏定义
#ifndef 宏名 ===》 宏不可以进行逻辑运算
// 如果宏没有定义,则这段代码有效
#else
// 如果宏定义了,则这段代码有效
#endif
5.2> #ifdef
#ifdef 宏名 ===》 宏不可以进行逻辑运算
// 如果宏定义了,则这段代码有效
#else
// 如果宏没有定义,则这段代码有效
#endif
5.3> defined()
#if defined(宏)
// 如果宏定义了,则这段代码有效
#else
// 如果宏没有定义,则这段代码有效
#endif
#if !defined(宏)
// 如果宏没有定义,则这段代码有效
#else
// 如果宏定义了,则这段代码有效
#endif
#if defined(宏1) || defined(宏2) ....
// 只要有一个宏定义了,则这段代码有效
#else
// 两个宏都没有定义,则这段代码有效
#endif
#if defined(宏1) && defined(宏2) ....
// 两个宏都有定义,则这段代码有效
#else
// 只要有一个宏没有定义了,则这段代码有效
#endif
#if !defined(宏1) || defined(宏2) ....
// 第一个宏没有定义或者第二个宏定义了则这段代码有效
#else
// 上边无效的时候,下边就有效
#endif
#include <stdio.h>
#define DEBUG
#define DEBUG2
int main(int argc, const char *argv[])
{
#ifndef DEBUG
printf("这是一段调试代码\n");
#else
printf("这不是一个调试代码\n");
#endif
#ifdef DEBUG2
printf("这是一条调试代码\n");
#else
printf("这不是一个调试代码\n");
#endif
#if defined(DEBUG) || defined(DEBUG2)
printf("两个中一定有一个定义了\n");
#else
printf("两个中一定都没有一个定义了\n");
#endif
return 0;
}
三、多文件的编程
将具有相同功能的代码放到一个.c文件,不同功能的代码放到不同.c文件中,多文件变量目的是为了工程的方便管理,阅读代码方便。
一般是一个.c文件对应一个.h文件。
.c文件中一般用于函数定义,变量的定义,
.h文件中一般书写函数声明,变量的声明。
tree 命令 sudo apt-get install tree
modules --> 一个工程一个文件夹
├── include ---> 放所有的头文件
├── main.c ---> 主函数
└── src ----> 放所有的源文件
头文件的重复包含机制
#ifndef __***_H__
#define __***_H__
#endif // __***_H__
四、动态内存分配------> malloc/free
1.malloc函数的分析------>man malloc
#include <stdlib.h>
void *malloc(size_t size);
/*
函数的功能: 在堆区动态的分配内存空间
函数的参数:
@ size : 在堆区分配多少字节的空间,单位为字节
函数的返回值: void *
@ 成功: 返回分配堆区空间的首地址
@ 失败: 返回NULL
*/
2.free函数的分析-----> man free
#include <stdlib.h>
void free(void *ptr);
/*
函数的功能: 手动释放堆区的空间
函数的参数:
@ ptr : 释放堆区空间的首地址
函数的返回值:
@ 无
*/
3.总结:
1.在程序中可以动态指定本次分配内存的⼤⼩
2.malloc分配的内存在堆区
3.malloc分配的内存是连续的,可以通过数组的格式访问内存中的数据
p = (int *)malloc(sizeof(int)*10); 在堆区分配40个字节的空间,并且是连续的。
4.malloc申请的内存需要free释放,如果在⼀个⼀直运⾏的程序中,
反复调⽤malloc,但是没有调⽤free就会导致"内存泄漏"。
如果malloc申请的内存没有调⽤free,
但是程序执⾏结束了,内存会被操作系统释放掉。
5.malloc申请的内存在释放的时候,⼀定使⽤的是从⾸地开始的内存。
free函数调⽤之后⼀定要将指针执向NULL,
否则在使⽤这个指针的时候就是在使⽤"野指针"
如果释放堆区的空间没有执向NULL,则这个指针已让可以使用,
并且编译不会报错,当时使用了非法的内存空间,别人再次malloc时
这块空间又可能会被重新分配,执行也不会报错,但是程序的运行结果为止。
如果释放堆区的空间没有指向NULL,就会出现野指针。
如果释放堆区的空间,并且释放之后将指针指向NULL,
此时这个指针依然可以使用,编译器不会报错,
但是指向时会报段错误(核心已转储).
4.
1> 分配空间
2> 使用分配空间
3> 释放空间
4> 让指针指向NULL,防止野指针的出现
在实际的开发中杠修改bug大多数都是野指针,段错误相关问题。