站长工具怎么关闭好看的免费的小说网站模板

当前位置: 首页 > news >正文

站长工具怎么关闭,好看的免费的小说网站模板,和京东一样做电子产品的网站,网页设计制作思路C语言编程指南 ■ C语言基础■ 数据类型■ 数据类型■ 数据类型占位符■ 数字后面带个U#xff0c;L#xff0c;F的含义■ 自动类型转换 ■ 异或■ 关键字■ const■ extern■ typedef■ static■ 左值和右值■ 位域■ float数据存储■ .h文件 ■ 条件编译■ #define■ #ifde… C语言编程指南 ■ C语言基础■ 数据类型■ 数据类型■ 数据类型占位符■ 数字后面带个ULF的含义■ 自动类型转换 ■ 异或■ 关键字■ const■ extern■ typedef■ static■ 左值和右值■ 位域■ float数据存储■ .h文件 ■ 条件编译■ #define■ #ifdef#ifndef#else与#endif■ #if#elif#else与#endif■ #if defined 和 #if !defined■ #和## ■ attribute■ 属性声明section■ 属性声明aligned■ aligned(4)-地址对齐■ 结构体对齐 ■ 属性声明packed■ 属性声明format■ 属性声明weak■ 属性声明alias■ 属性声明noinline■ 属性声明always_inline■ 属性声明 attribute((used))■ 属性声明 attribute((unused)) ■ 字符串■ C指针■ 指针介绍■ 指针的类型■ 指针所指向的类型■ 指针的值或者叫指针所指向的内存区或地址■ 指针本身所占据的内存区■ 指针的算术运算■ 运算符和*■ 指针表达式 ■ 函数传递二维数组■ 形参给出第二维的长度。■ 形参声明为指向数组的指针。■ 形参声明为指针的指针。 ■ 二级指针传参数■ 二级指针传参数没有改变原始值■ 二级指针传参数改变原始值 ■ 数组和指针■ 数组指针也称行指针■ 指针数组■ 指针变量■ 函数指针指针函数■ 指针函数■ 函数指针 ■ 可变参数■ 三维数组的传递■ 计算公式最大值最小值和中间值■ memcpy■ offsetof(TYPE, MEMBER) 计算结构体变量中元素的位置■ 结构体数组初始化■ 结构体数组传参■ str 函数■ _strlen函数实现■ strstr函数使用■ strcasestr函数■ char *strdup(const char *s); ■ C库函数■ ftell(FILE *stream)■ printf■ snprintf■ getopt() 函数■ bzero■ sscanf 利用 sscanf和正则表达式提取数据 ■ C语言基础 推荐网页 https://www.cnblogs.com/shuopython/p/15270897.html ■ 数据类型 ■ 数据类型 ■ 数据类型占位符 %d——整型 %f——浮点数 输入%lf输出%f——高精度浮点数比较特殊 %s——字符串 %c——字符 %p——指针 %e——科学计数法 %g——小数或科学计数法 %o 以八进制输出 %x 以16进制输出 %i 结构体输出 %d 整型int %ld 长整型long %lld 长长整型long long int %hd 短整型short int %u 无符号整型unsigned int %hu 无符号短整型unsigned short int %lu 无符号长整形unsigned long int %llu 无符号长长整型unsigned long long %f 浮点型float 输入%lf输出%f 高精度浮点型double %eE) 以指数形式表示的浮点型 %m.nf 可控制输出小数位数 如何控制输出的格式%m.nf %.2f输出两位小数左对齐输出位数不够自动补0 %02d 右对齐输出位数不够补0 %2d右对齐输出位数不够补/0 #include stdio.hint main() {printf(%09d\n,2345);printf(%9d\n,2345);printf(%.2f\n,2345.235);printf(%.2d\n,2345.235);return 0; } 输出结果 0000023452345 2345.24 1537761952 //这个printf的函数我丢给他一个浮点数却要他输出一个%.2d是不是输出错误了所以平时要保持这优秀而严谨的习惯。■ 数字后面带个ULF的含义 U 表示该常数用无符号整型方式存储相当于 unsigned int L 表示该常数用长整型方式存储相当于 long F 表示该常数用浮点方式存储相当于 float三、自动类型转换 (“L”和“l” 数值后面加“L”和“l”小写的l的意义是该数值是long型。) 数值后面加“”H“、“h”的意义是该数值是用16进制表示的。 数值后面加“”B“、“b”的意义是该数值是用2进制表示的。 后面什么也不加代表10进制。 例如 5L 的数据类型为long int。 5.12L 的数据类型为long double。 #include stdio.h int main() {printf(%d %d %d\n, sizeof(1.1), sizeof(1.1f), sizeof(1.1l));printf(%d %d %d\n, sizeof(1), sizeof(1l), sizeof(1ll));printf(%d %d\n, sizeof(0xFF), sizeof(0xFFu));printf(%d %d\n, sizeof(0xFFFFFFFFFFFFFFFFLL), sizeof(0xFFFFFFFFFFFFFFFF));if (-2 1u 0)printf(???\n);elseprintf(…\n);if (-2 1 0)printf(???\n);elseprintf(…\n);return 0; }结果 8 4 12 4 4 8 4 4 8 8 ??? … ■ 自动类型转换 ■ 异或 原则1和1取00和0 取01和0 取1. 作用交换两个整数的值而不必用第三个参数 a 9; b 11; aa^b; 1001^10110010 bb^a; 1011^00101001 aa^b; 0010^10011011■ 关键字 设置表格对齐方式 关键字描述asm用于内嵌汇编代码和直接访问底层硬件inline用于内联函数以减少函数调用的开销attribute用于定义变量或函数的属性例如对齐方式、可见性、优化等级等packed用于取消结构体成员的对齐以节省内存空间irq, fiq用于定义中断服务函数的类型以区分普通函数和中断函数register用于将变量声明为寄存器变量以提高访问速度volatile告诉编译器该变量可能会被意外修改从而避免编译器对该变量进行优化weak, strong用于声明弱引用和强引用变量在链接时进行符号重定向packed, attribute((aligned(n)))用于控制结构体和变量的对齐方式ARM_ARCH_XX用于条件编译根据处理器的不同选择不同的代码路径 ■ const 区分常量指针和指针常量的关键就在于星号的位置 我们以星号为分界线如果const在星号的左边则为常量指针如果const在星号的右边则为指针常量。 如果我们将星号读作‘指针’将const读作‘常量’的话内容正好符合。 int const * n是常量指针 const int * n是常量指针 int *const n 是指针常量。 属性描述书写注意点常量指针是指针指向的内容是常量const int * n; int const * n;int a5; int b6; const int* na; nb;// 不能通过n改变a的值但是可以改变n的指向。指针常量是指指针本身是个常量不能在指向其他的地址int *const n;int a5; int *pa; int * const na; *p8; 属性描述const char *p;const 修饰的是 *p 所以是*p值不能更改。 p[1] ‘W’; 错误char const *p;const 修饰的是 *p 所以是*p值不能更改。 p[1] ‘W’; 错误char * const p;const 修饰的是 p 所以是 p指针不能更改*p值可变。 pstr[1]; 错误。 char str[10]Hello; char buf[10]World; //const char *pstr; //常量指针 P buf; 正确, p[1] W; 错误 //char const *pstr; //常量指针 P buf; 正确, p[1] W; 错误 char *const pstr; //指针常量 p[0]X; 正确, p buf; 错误 , pstr[1]; 错误。■ extern ■ typedef 作用是为一种数据类型定义一个新的名字。对于以上两种结构体定义形式 typedef都可以为其创建别名。

  1. 先定义结构体类型再定义结构体变量 struct student{char no[20]; //学号char name[20]; //姓名char sex[5]; //性别int age; //年龄 };
    struct student stu1,stu2; //此时stu1,stu2为student结构体变量2. 定义结构体类型的同时定义结构体变量。 struct student{char no[20]; //学号char name[20]; //姓名char sex[5]; //性别int age; //年龄 } stu1,stu2;
    此时还可以继续定义student结构体变量如 struct student stu3;1. 先定义结构体类型再定义结构体变量 struct{char no[20]; //学号char name[20]; //姓名char sex[5]; //性别int age; //年龄 } stu1,stu2; ■ static ■ 左值和右值 ■ 位域 C 语言的位域bit-field是一种特殊的结构体成员允许我们按位对成员进行定义指定其占用的位数。 用 1 位二进位存放一个开关量时只有 0 和 1 两种状态。 实例 1 带有预定义宽度的变量被称为位域 struct bs{int a:8;int b:2;int c:6; }data; struct bs 的结构体data 为 bs 的结构体变量共占四个字节 对于位域来说它们的宽度不能超过其数据类型的大小实例 2 位数 #include stdio.hstruct packed_struct {unsigned int f1 : 1; // 1位的位域unsigned int f2 : 1; // 1位的位域unsigned int f3 : 1; // 1位的位域unsigned int f4 : 1; // 1位的位域unsigned int type : 4; // 4位的位域unsigned int my_int : 9; // 9位的位域 };int main() {struct packed_struct pack;pack.f1 1;pack.f2 0;pack.f3 1;pack.f4 0;pack.type 7;pack.my_int 255;printf(f1: %u\n, pack.f1);printf(f2: %u\n, pack.f2);printf(f3: %u\n, pack.f3);printf(f4: %u\n, pack.f4);printf(type: %u\n, pack.type);printf(my_int: %u\n, pack.my_int);return 0; } 输出结果为 f1: 1 f2: 0 f3: 1 f4: 0 type: 7 my_int: 255实例 3 空域 struct bs{unsigned a:4;unsigned :4; /* 空域 /unsigned b:4; / 从下一单元开始存放 /unsigned c:4 }实例 3 超过位数赋值 #include stdio.h #include string.h struct {unsigned int age : 3; } Age;int main( ) {Age.age 4;printf( Sizeof( Age ) : %d\n, sizeof(Age) );printf( Age.age : %d\n, Age.age );Age.age 7;printf( Age.age : %d\n, Age.age );Age.age 8; // 二进制表示为 1000 有四位超出printf( Age.age : %d\n, Age.age );return 0; } 当上面的代码被编译时它会带有警告当上面的代码被执行时它会产生下列结果 Sizeof( Age ) : 4 Age.age : 4 Age.age : 7 Age.age : 0实例 4 结构体和位域对比 #include stdio.h #include string.h/ 定义简单的结构 / struct {unsigned int widthValidated;unsigned int heightValidated; } status1;/ 定义位域结构 */ struct {unsigned int widthValidated : 1;unsigned int heightValidated : 1; } status2;int main( ) {printf( Memory size occupied by status1 : %d\n, sizeof(status1));printf( Memory size occupied by status2 : %d\n, sizeof(status2));return 0; }它会产生下列结果 Memory size occupied by status1 : 8 Memory size occupied by status2 : 4实例 5 可以对 u16Bit;数据类型中的每个位进行操作赋值u16Bit.b1 1; typedef struct{ union{struct {uint8_t b0:1; uint8_t b1:1; uint8_t b2:1; uint8_t b3:1; uint8_t b4:1; uint8_t b5:1; uint8_t b6:1; uint8_t b7:1;uint8_t b8:1; uint8_t b9:1; uint8_t b10:1; uint8_t b11:1; uint8_t b12:1; uint8_t b13:1; uint8_t b14:1; uint8_t b15:1; };uint16_t byte;}; }u16Bit;/******************************定义uint32_t*************************/ typedef struct {uint32_t b0:1; }u200Bit;u200Bit u200bit_t; u200bit_t.b01;//赋值 qDebug() sizeof(u200Bit) sizeof(u200Bit) u200bit_t.b0 endl; //sizeof(u200Bit) 4 1 /***************************定义uint16_t*************************/ typedef struct {uint16_t b0:1; }u200Bit;u200Bit u200bit_t; u200bit_t.b01;//赋值 qDebug() sizeof(u200Bit) sizeof(u200Bit) u200bit_t.b0 endl; //sizeof(u200Bit) 2 1 /***************************定义uint8_t**************************/ typedef struct {uint8_t b0:1; }u200Bit;u200Bit u200bit_t; u200bit_t.b01; //赋值 qDebug() sizeof(u200Bit) sizeof(u200Bit) u200bit_t.b0 endl;//sizeof(u200Bit) 1 1 ■ float数据存储 float类型数字在计算机中用4个字节存储。遵循IEEE-754格式标准一个浮点数有2部分组成底数m和指数e底数部分 使用二进制数来表示此浮点数的实际值 指数部分 占用8bit的二进制数可表示数值范围为0-255但是指数可正可负所以IEEE规定此处算出的次方必须减去127才是真正的指数。所以float类型的指数可从-126到128底数部分实际是占用24bit的一个值但是最高位始终为1所以最高位省去不存储在存储中占23bit科学计数法。格式 SEEE EEEE EMMM MMMM MMMM MMMM MMMM MMMM S表示浮点数正负
    E指数加上127后的值得二进制数据 M底数举例 17.625在内存中的存储首先要把17.625换算成二进制10001.101整数部分除以2直到商为0余数反转。小数部分乘以2直到乘位0进位顺序取。在将10001.101右移直到小数点前只剩1位1.0001101 * 2^4 因为右移动了四位这个时候我们的底数和指数就出来了 底数因为小数点前必为1所以IEEE规定只记录小数点后的就好。所以此处的底数为0001101 指数实际为4必须加上127(转出的时候减去127)所以为131。也就是10000011 符号部分是整数所以是0 综上所述17.625在内存中的存储格式是 01000001 10001101 00000000 00000000 ■ .h文件 #ifndef _VERSIONH #define _VERSIONH …. #endif■ 条件编译 1、#if如果条件为真则执行相应的操作。 2、#elif类似于 elseif 的用法当前面条件为假再判断该条件是否为真如果是真则执行相应操作。 3、#else如果前面所有条件均为假则执行相应操作。 4、#ifdef如果该宏已定义则执行相应操作。 5、#ifndef如果该宏没有定义则执行相应操作。 6、#endif 结束对应的条件编译指令。(不能省略) ■ #define //——————————————————– #define CFDA #ifdef CFDA //代码区 有定义CFDA此处高亮没有定义灰显。 #endif 不带替换体 //——————————————————– #define VERSION_GHK 2 #define ARMCLIB_VERSION 5060037 带替换体 //——————————————————– #define SQUARE(x) ((x)*(x)) 带参数 输出结果 printf(%d\n,SQUARE(8)); 8*8 64//——————————————————– #define DEBUG_PRINT printf(file:%s\tline:%d\t \date:%s\ttime:%s\n ,__FILE,LINE , __DATE,TIME__ ) 加换行符 //——————————————————– #define XVALUE(n) x##n //将符号 x 和 n 合并为一个记号 #define YVALUE(n) y##n //将符号 y 和 n 合并为一个记号 #define ZVALUE(n) z##n //将符号 z 和 n 合并为一个记号 int XVALUE(1) 10; //等价 int x1 10 int YVALUE(1) 10; //等价 int y1 10 int ZVALUE(1) 10; //等价 int z1 10#define NUM(a,b,c) a##b##c printf(NUM(1,2,3) %d\n,NUM(1,2,3)); //NUM(1,2,3) 123
    添加拼接 //——————————————————–//——————————————————–拼接和define
    ■ #ifdef#ifndef#else与#endif #ifdef 用于判断某个宏是否定义 #ifndef 功能正好相反二者仅支持判断单个宏是否已经定义 语法 #ifdef 标识符 #ifndef 标识符 ifdef _DEBUG // … do some operations #endif#ifdef _WIN32 // … use Win32 API #endif//———————————#ifdef ABC // … codes while definded ABC #elif (CODE_VERSION 2) // … codes while CODE_VERSION 2 #else // … remained cases #endif // #ifdef ABC//——————————— ■ #if#elif#else与#endif #if 常量表达式1 // … some codes #elif 常量表达式2 // … other codes #elif 常量表达式3 // … … #else // … statement #endif//——————————— #if 0 //代码1 #endif 相当于#if 0 和 #endif之间的代码被注释掉了一般在IDE环境中会显示灰色。 //——————————— #define FUNCTION 0#if FUNCTION //代码1 #endif 即FUNCTION宏定义为0或者1决定了#if FUNCTION和#endif之间的代码是否参与编译。 //——————————— #define FUNCTION 0#if (FUNCTION 0) //代码1 #elif (FUNCTION 1) //代码2 #elif (FUNCTION 2) //代码3 #endif FUNCTION宏定义为0或者1或者2FUNCTION为0则代码1参与编译FUNCTION为1则代码2参与编译FUNCTION为2则代码3参与编译。 //——————————— #define FUN_A 1 #define FUN_B 1#if (FUN_A FUN_B) //代码1 #endif 只有当FUN_A和FUN_B同时定义为1时代码1才参与编译否则代码1不参与编译。 //———————————■ #if defined 和 #if !defined defined用来测试某个宏是否被定义。 defined(name): 若宏被定义则返回1否则返回0。 它与#if、#elif、#else结合使用来判断宏是否被定义乍一看好像它显得多余, 因为已经有了#ifdef和#ifndef。defined可用于在一条判断语句中声明多个判别条件#ifdef和#ifndef则仅支持判断一个宏是否定义。 //——————————— 判断多个条件 #if defined(VAX) defined(UNIX) !defined(DEBUG) //——————————— #if defined(VAX) 和 #ifdef VAX 如果是判断单个条件#if defined和#ifdef没有啥差别。 //——————————— #if !defined(VAX) 和 #ifndef VAX 说明 #if !defined的本质还是 #if defined所以它们可以组合。■ #和## #把一个宏参数变成对应的字符串。 ##运算符 ;与 #运算符类似 ##运算符可用于类函数宏带参宏的替换部分。 ##运算符可以把两个记号组合成一个记号。 #define XVALUE(n) x##n //将符号 x 和 n 合并为一个记号 #define YVALUE(n) y##n //将符号 y 和 n 合并为一个记号 #define ZVALUE(n) z##n //将符号 z 和 n 合并为一个记号 int XVALUE(1) 10; //等价 int x1 10 int YVALUE(1) 10; //等价 int y1 10 int ZVALUE(1) 10; //等价 int z1 10#define NUM(a,b,c) a##b##c printf(NUM(1,2,3) %d\n,NUM(1,2,3)); //NUM(1,2,3) 123 #示例
    ■ attribute attribute 关键字用来声明一个函数、变量或类型的特殊属性。 申明这些属性主要用途就是指导编译程序进行特定方面的优化或代码检查。 attribute((ATTRIBUTE)) attribute 后面是两对小括号不能图方便只写一对否则会编译报错。 ATTRIUBTE 表示要声明的属性目前支持十几种属性声明 section自定义段aligned对齐packed对齐format检查函数变参格式weak弱声明alias函数起别名noinline无内联always_inline内联函数总是展开 对一个变量也可以同时添加多个属性。在定义变量前各个属性之间用逗号隔开。 例如 char c attribute((packed,algined(4))); char c attribute((packed,algined(4)))4; attribute((packed,algined(4)))charc4; ■ 属性声明section section 属性的主要作用是在程序编译时将一个函数或者变量放到指定的段即指定的section 中。 一个可执行文件注意由代码段数据段、BSS 段构成。 代码段主要用来存放编译生成的可执行指令代码、数据段和BSS段用来存放全局变量和未初始化的全局变量。 除了这三个段可执行文件还包含一些其他的段。我们可以用 readelf 去查看一个可执行文件各个section信息。
    attribute ((section(“xxx”)))修改段的属性。 示例一 intglobal_val0; int unint_val attribute((section(.data))); //unint_val 这个变量已经被编译器放在数据段中。当然也可以自定义段的名称。intmain() {return0; }■ 属性声明aligned GNU C 通过 attribute 来声明 aligned 和 packed 属性指定一个变量或类型的对齐方式。 ■ aligned(4)-地址对齐 aligned 属性我们可以显示地指定变量 a 在内存中的地址对齐方式。 aligned 有一个参数表示要按几个字节对齐使用时要注意地址对齐的字节数必须是 2 的幂次方否则编译就会报错。 #include stdioint a1; int b2; char c13; char c24; //替换成 char c2 attribute((aligned(4)))4; int main() {printf(a%p,a);printf(b%p,b);printf(c1%p,c1);printf(c2%p,c2);return0; }可以看到char 占一个字节c2的地址紧挨着 c1a0x404030 b0x404034 c10x404038 c20x404039使用 aligned 地址对齐a0x404030 b0x404034 c10x404038 c20x40403c 可以看到c2 的地址是按照4字节对齐■ 结构体对齐 结构体作为一种复杂的数据类型编译器在给一个结构体变量分配存储空间时不仅要考虑结构体内各个成员的对齐还要考虑结构体整体的对齐。 编译器一定会按照 aligend 指定的方式对齐吗 我们通过这个属性声明其实只是建议编译器按照这种大小地址对齐但不能超过编译器允许的最大值。一个编译器对每个基本的数据类型都有默认的最大边界对齐字节数如果超过了则编译器只能按照它规定的最大对齐字节数来对变量分配地址。 #include stdiostruct data{char a;int b;short c; };intmain() {struct data s;printf(size%d,sizeof(s));printf(a%p,s.a);printf(b%p,s.b);printf(c%p,s.c); return0; }四字节对齐占12字节size12 a0xffb6c374 b0xffb6c378 c0xffb6c37c ——————————————————————————————————— 结构体成员顺序不同所占大小有可能不同 structdata{char a;short b;int c; }; 四字节对齐占8字节 size8 a0xffa2d9f8 b0xffa2d9fa c0xffa2d9fc ——————————————————————————————————— 显示的指定成员的对齐方式#include stdio struct data{char a;short b attribute((aligned(4)));int c; };int main() {struct data s;printf(size%d ,sizeof(s));printf(a%p ,s.a);printf(b%p ,s.b);printf(c%p ,s.c);return0; }四字节对齐占12字节size12 a0xffb6c374 b0xffb6c378 c0xffb6c37c ——————————————————————————————————— 显示指定结构体对齐方式 #include struct data{char a;short b;int c; }attribute((aligned(16)));int main() {struct data s;printf(size%d,sizeof(s));printf(a%p,s.a);printf(b%p,s.b);printf(c%p,s.c);return0; } 16字节对齐末尾填充8字节占16字节size16 a0xffa2d9f8 b0xffa2d9fa c0xffa2d9fc■ 属性声明packed aligned 属性一般用来增大变量的地址对齐元素之间地址对齐会造成一定的内存空洞而packed属性则正好相反一般用来减少地址对齐指定变量或类型使用最可能小的地址对齐方式。 #include struct data{char a;short b attribute((packed));int c attribute((packed)); }; intmain() {struct data s;printf(size%d,sizeof(s));printf(a%p,s.a);printf(b%p,s.b);printf(c%p,s.c); return 0; } 使用最小一字节对齐 size7 a0xfff38fb9 b0xfff38fba c0xfff38fbc ——————————————————————————————————— 对整个结构体添加packed属性 structdata{char a;short b;int c; }attribute((packed));内核中的packed、aligned 声明 在内核源码中我们经常看到aligned 和 packed 一起使用即对一个变量或者类型同时使用packed 和 aligned 属性声明。 这样做的好处是即避免了结构体各成员间地址对齐产生的内存空洞又指定了整个结构体的对齐方式。 struct data{char a;short b;int c; }attribute((packed,aligned(8))); ■ 属性声明format format 属性用来指定变参函数的参数格式检查。 使用方法 attribute((format (archetype, string-index, frist-to-check))) 示例 void LOG(const char fmt, …) attribute((format(printf,1,2))); LOG(“hello world ,i am %d ages \n”, age); / 前者表示格式字符串后者表示所有的参数*/ 属性format(printf,1,2) 有3个参数 第一个参数pritnf 是告诉编译器按照printf的标准来检查 第二个参数表示LOG()函数所有的参数列表中格式字符串的位置索引即第一个参数hello world ,i am %d ages \n 第三个参数是告诉编译器要检查的参数的起始位置即从第二个参数age开始检查。 ■ 属性声明weak GNU C 通过 weak 属性声明将一个强符号转换为弱符号。 使用方法如下 void attribute((weak)) func(void); int num attribute((weak));强符号函数名初始化的全局变量名 弱符号未初始化的全局变量名 ■ 属性声明alias alias 属性主要用来给函数定义一个别名 示例 void f(void) {printf(f\n); }void f(void) attribute((alias(__f)));int main(void) {f();return 0; }在Linux 内核中你会发现alias有时候会和weak属性一起使用。如有些接口随着内核版本升级函数接口发生了变化我们可以通过alias属性对旧的接口名字进行封装重新起一个接口名字。 //f.c void f(void) {printf(f\n); }void f() attribute((weak, alias(__f)));//main.c void attribute((weak)) f(void); void f(void) {printf(f\n); }int main() {f();return 0; } 如果main.c 中定义了f()函数那么main 函数调用f()会调用新定义的函数强符号否则调用__f()函数弱符号■ 属性声明noinline static inline attribute((noinline)) void f() ■ 属性声明always_inline static inline attribute((always_inline)) void f() ■ 属性声明 attribute((used)) 表示该函数或变量可能不使用这个属性可以避免编译器产生警告信息。 ■ 属性声明 attribute((unused)) 向编译器说明这段代码有用即使在没有用到的情况下编译器也不会警告 ■ 字符串 字符串实际上是使用空字符 \0 结尾的一维字符数组。 \0 是用于标记字符串的结束。 空字符Null character又称结束符 缩写 NULL是一个数值为 0 的控制字符 \0 是转义字符意思是告诉编译器这不是字符 0而是空字符。 ‘\0’ NULL 0数字0 两个char 数组 第一个没有\0情况下后printf会打印第二个数组内容。 #include stdio.h int main() {char site[6] {R,u,n,o,b,9};char sizzz[7]{l,t,d,s,\0};printf(%s\n,site); //Runob9ltds site数组没有\0 输出了sizzz内容。site[3]0;printf(%s\n,site); //Run 遇见0就停止printf(%d,%d\n,site[4],site[5]); //98,57 }pengyctengmai:~/DockPro/demo$ ./a.out Runob9ltds Run 98,57char sizeee[] “abcde”; 大小 char sizeee[] abcde; printf(%ld\n,sizeof(sizeee)); //6 说明大小会在后面加\0;■ C指针 指针是一个特殊的变量它里面存储的数值被解释成为内存里的一个地址。 ■ 指针介绍 ■ 指针的类型 从语法的角度看你只要把指针声明语句里的指针名字去掉剩下的部分就是这个指针的类型。 (1) intptr; //指针的类型是int
    (2) charptr; //指针的类型是char
    (3) int
    ptr; //指针的类型是int**
    (4) int(ptr)[3]; //指针的类型是int()3 int*(ptr)[4];//指针的类型是int(*)[4] ■ 指针所指向的类型 当你通过指针来访问指针所指向的内存区时指针所指向的类型决定了编译器将把那片内存区里的内容当做什么来看待。 你只须把指针声明语句中的指针名字和名字左边的指针声明符*去掉剩下的就是指针所指向的类型。 (1) int*ptr;//指针所指向的类型是int
    (2) charptr;//指针所指向的的类型是char
    (3) int**ptr;//指针所指向的的类型是int

    (4) int(ptr)[3];//指针所指向的的类型是int()3 int(ptr)[4];//指针所指向的的类型是int()[4] ■ 指针的值或者叫指针所指向的内存区或地址 指针的值是指针本身存储的数值这个值将被编译器当作一个地址而不是一个一般的数值。 在32位程序里所有类型的指针的值都是一个32位整数因为32位程序里内存地址全都是32位长。 指针的值是这块内存区域的首地址. 指针所指向的类型已经有了但由于指针还未初始化所以它所指向的内存区是不存在的或者说是无意义的。 ■ 指针本身所占据的内存区 指针本身占了多大的内存你只要用函数sizeof(指针的类型)测一下就知道了。在32位平台里指针本身占据了4个字节的长度。 指针本身占据的内存这个概念在判断一个指针表达式是否是左值时很有用。 ■ 指针的算术运算 指针可以加上或减去一个整数。指针的这种运算的意义和通常的数值的加减运算的意义是不一样的 ———————————————————— 例二 char a[20];
    intptra; … …
    ptr; 指针ptr的类型是int
    ,它指向的类型是int它被初始化为指向整形变量a。 接下来的第3句中指针ptr被加了1编译器是这样处理的它把指针ptr的值加上了sizeof(int)在32位程序中是被加上了4。 由于地址是用字节做单位的故ptr所指向的地址由原来的变量a的地址向高地址方向增加了4个字节。
    由于char类型的长度是一个字节所以原来ptr是指向数组a的第0号单元开始的四个字节此时指向了数组a中从第4号单元开始的四个字节。 ———————————————————— 例三 int array[20];
    int*ptrarray; for(i0;i 20;i)
    { (*ptr); ptr
    } 这个例子将整型数组中各个单元的值加1。 由于每次循环都将指针ptr加1所以每次循环都能访问数组的下一个单元。 ———————————————————— 例四 chara[20];
    intptra; … …
    ptr5; ptr被加上了5编译器是这样处理的将指针ptr的值加上5乘sizeof(int)在32位程序中就是加上了5乘420。由于地址的单位是字节故现在的ptr所指向的地址比起加5后的ptr所指向的地址来说向高地址方向移动了20个字节。在这个例子中没加5前的ptr指向数组a的第0号单元开始的四个字节加5后ptr已经指向了数组a的合法范围之外了。虽然这种情况在应用上会出问题但在语法上却是可以的。这也体现出了指针的灵活性。ptr是被减去5那么处理过程大同小异只不过ptr的值是被减去5乘sizeof(int)新的ptr指向的地址将比原来的ptr所指向的地址向低地址方向移动了20个字节。■ 运算符和
    这里是取地址运算符是…书上叫做 间接运算符 。  a的运算结果是一个指针指针的类型是a的类型加个指针所指向的类型是a的类型指针所指向的地址嘛那就是a的地址。  p的运算结果就五花八门了。总之p的结果是p所指向的东西这个东西有这些特点它的类型是p指向的类型它所占用的地址是p所指向的地址。 inta12;
    intb;
    int*p; int**ptr;
    pa; //a的结果是一个指针类型是int*指向的类型是int指向的地址是a的地址。
    *p24; //*p的结果在这里它的类型是int它所占用的地址是p所指向的地址显然*p就是变量a。
    ptrp; //p的结果是个指针该指针的类型是p的类型加个在这里是int **。该指针所指向的类型是p的类型这里是int。该指针所指向的地址就是指针p自己的地址。
    *ptrb; //*ptr是个指针b的结果也是个指针且这两个指针的类型和所指向的类型是一样的所以用b来给*ptr赋值就是毫无问题的了。
    ptr34; //*ptr的结果是ptr所指向的东西在这里是一个指针对这个指针再做一次*运算结果就是一个int类型的变量。
    ■ 指针表达式 一个表达式的最后结果如果是一个指针那么这个表达式就叫指针表式。 例六 int a,b; intarray[10];
    int*pa;
    paa; //a是一个指针表达式。
    int
    ptrpa; //pa也是一个指针表达式。
    *ptrb; //*ptr和b都是指针表达式。
    paarray;
    pa; //这也是指针表达式。 在例六中a不是一个左值因为它还没有占据明确的内存。 *ptr是一个左值因为*ptr这个指针已经占据了内存其实*ptr就是指针pa既然pa已经在内存中有了自己的位置那么*ptr当然也有了自己的位置。 例七
    char*arr[20];
    char**parrarr;//如果把arr看作指针的话arr也是指针表达式
    char*str;
    str*parr;//parr是指针表达式
    str
    (parr1);//(parr1)是指针表达式
    str
    (parr2);//*(parr2)是指针表达式 当一个指针表达式的结果指针已经明确地具有了指针自身占据的内存的话这个指针表达式就是一个左值否则就不是一个左值。 例八 main() {int a[5]{1,2,3,4,5};int *ptr(int )(a1);printf(%d,%d,(a1),*(ptr-1)); }a 1: 取数组a 的首地址该地址的值加上sizeof(a) 的值即a 5*sizeof(int)也就是下一个数组的首地址显然当前指针已经越过了数组的界限。 (int *)(a1): 则是把上一步计算出来的地址强制转换为int * 类型赋值给ptr。 *(a1): a,a 的值是一样的但意思不一样a 是数组首元素的首地址也就是a[0]的首地址a 是数组的首地址a1 是数组下一元素的首地址即a[1]的首地址,a1 是下一个数组的首地址。所以输出 (ptr-1): 因为ptr 是指向a[5]并且ptr 是int * 类型所以(ptr-1) 是指向a[4] 输出5。 ■ 函数传递二维数组 ■ 形参给出第二维的长度。 ■ 形参声明为指向数组的指针。 #include stdio.h void func(int n, char str[5] ) {int i;for(i 0; i n; i)printf(\nstr[%d] %s\n, i, str[i]); } void main() {char p[3];char str[][5] {abc,def,ghi};func(3, str); }■ 形参声明为指针的指针。 #include stdio.h void func(int n, char str) {int i;for(i 0; i n; i)printf(\nstr[%d] %s\n, i, str[i]); } void main() {char* p[3];char str[][5] {abc,def,ghi};p[0] str[0][0];p[1] str[1];p[2] str[2];func(3, p); }char* arg[] {abc,cde,efg,}; //这种写法和上面的等价 char* b[3]; b[0] arg[0]; b[1] arg[1]; b[2] arg[2];int array[][3] {{1, 2, 3},{2, 3, 4},{3, 4, 5},};int* a[3];a[0] array[0]; a[1] array[1]; a[2] array[2];■ 二级指针传参数 ■ 二级指针传参数没有改变原始值 uisubuntu:/text$ gcc zhizheng.c uisubuntu:/text\( uisubuntu:~/text\) ./a.out a0x7ffe43789e08,p0x7ffe43789e0c pp0x7ffe43789e08,kk0x7ffe43789e0c—pp0x7ffe43789e10,kk0x7ffe43789e18 x0x7ffe43789e10,y0x7ffe43789e18—x0x7ffe43789dd8,y0x7ffe43789dd0,— *x0x7ffe43789e08,*y0x7ffe43789e0c
    x0x7ffe43789e10,y0x7ffe43789e18—x0x7ffe43789dd8,y0x7ffe43789dd0,— *x0x7ffe43789e08,*y0x7ffe43789e0c
    x0x7ffe43789e10,y0x7ffe43789e18—x0x7ffe43789dd8,y0x7ffe43789dd0,— *x0x7ffe43789e0c,*y0x7ffe43789e0c
    x0x7ffe43789e10,y0x7ffe43789e18—x0x7ffe43789dd8,y0x7ffe43789dd0,— *x0x7ffe43789e0c,*y0x7ffe43789e08
    a 20 b 10, a10,b20■ 二级指针传参数改变原始值 uisubuntu:/text$ gcc zhizheng2.c uisubuntu:/text\( uisubuntu:~/text\) ./a.out a0x7ffc22703778,p0x7ffc2270377c pp0x7ffc22703778,kk0x7ffc2270377c—pp0x7ffc22703780,kk0x7ffc22703788 x0x7ffc22703780,y0x7ffc22703788—x0x7ffc22703748,y0x7ffc22703740,— *x0x7ffc22703778,*y0x7ffc2270377c
    x0x7ffc22703780,y0x7ffc22703788—x0x7ffc22703748,y0x7ffc22703740,— *x0x7ffc22703778,*y0x7ffc2270377c
    x0x7ffc22703780,y0x7ffc22703788—x0x7ffc22703748,y0x7ffc22703740,— *x0x7ffc22703778,*y0x7ffc2270377c
    x0x7ffc22703780,y0x7ffc22703788—x0x7ffc22703748,y0x7ffc22703740,— *x0x7ffc22703778,*y0x7ffc2270377c
    a 20 b 10, a20,b10■ 数组和指针 例八 int array[10]{0,1,2,3,4,5,6,7,8,9},value;


    valuearray[0];//也可写成valuearray;
    valuearray[3];//也可写成value
    (array3);
    valuearray[4];//也可写成value*(array4); 一般而言数组名array代表数组本身类型是int[10]但如果把array看做指针的话它指向数组的第0个单元类型是int*所指向的类型是数组单元的类型即int。 因此array等于0就一点也不奇怪了。同理array3是一个指向数组第3个单元的指针所以(array3)等于3。 其它依此类推。 例九 char* str[3]{ Hello,thisisasample! , Hi,goodmorning. , Helloworld
    };
    char s[80]
    strcpy(s,str[0]);//也可写成strcpy(s,str);
    strcpy(s,str[1]);//也可写成strcpy(s,
    (str1));
    strcpy(s,str[2]);//也可写成strcpy(s,*(str2)); str是一个三单元的数组该数组的每个单元都是一个指针这些指针各指向一个字符串。把指针数组名str当作一个指针的话它指向数组的第0号单元它的类型是char
    它指向的类型是char*。
    *str也是一个指针它的类型是char它所指向的类型是char它指向的地址是字符串 Hello,thisisasample! 的第一个字符的地址即 H 的地址。 str1也是一个指针它指向数组的第1号单元它的类型是char**它指向的类型是char
    *(str1)也是一个指针它的类型是char*它所指向的类型是char它指向 Hi,goodmorning. 的第一个字符 H 等等 ■ 数组指针也称行指针 定义 int (*p)[n]; ()优先级高首先说明p是一个指针指向一个整型的一维数组这个一维数组的长度是n也可以说是p的步长。也就是说执行p1时p要跨过n个整型数据的长度。 int a[3][4]; int (*p)[4]; //该语句是定义一个数组指针指向含4个元素的一维数组。pa; //将该二维数组的首地址赋给p也就是a[0]或a[0][0]p; //该语句执行过后也就是pp1;p跨过行a[0][]指向了行a[1][]所以数组指针也称指向一维数组的指针亦称行指针。■ 指针数组 定义 int *p[n]; //这里int *p[3] 表示一个一维数组内存放着三个指针变量分别是p[0]、p[1]、p[2] []优先级高先与p结合成为一个数组再由int*说明这是一个整型指针数组它有n个指针类型的数组元素。这里执行p1时则p指向下一个数组元素这样赋值是错误的pa因为p是个不可知的表示只存在p[0]、p[1]、p[2]…p[n-1],而且它们分别是指针变量可以用来存放变量地址。但可以这样 *pa; 这里*p表示指针数组第一个元素的值a的首地址的值。 如要将二维数组赋给一指针数组: int *p[3]; int a[3][4]; p; //该语句表示p数组指向下一个数组元素。注此数组每一个元素都是一个指针 for(i0;i3;i) p[i]a[i] 这里int *p[3] 表示一个一维数组内存放着三个指针变量分别是p[0]、p[1]、p[2] 所以要分别赋值。■ 指针变量 int *p; *p NULL; 同样我们可以在编译器上调试这两行代码。第一行代码定义了一个指针变量 p其指向 的内存里面保存的是 int 类型的数据但是这时候变量 p 本身的值是多少不得而知也就是 说现在变量 p 保存的有可能是一个非法的地址。第二行代码给*p 赋值为 NULL即给 p 指向的内存赋值为 NULL但是由于 p 指向的内存可能是非法的所以调试的时候编译器可 能会报告一个内存访问错误。这样的话我们可以把上面的代码改写改写使 p 指向一块合 法的内存 int i 10; int *p i; p NULL; 在编译器上调试一下我们发现 p 指向的内存由原来的 10 变为 0 了而 p 本身的值 即内 存地址并没有改变■ 函数指针指针函数 ■ 指针函数 ■ 函数指针 ■ 可变参数 int func_name(int arg1, …); //省略号 … 表示可变参数列表。 函数 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 变量的内存。 常用的宏有 va_start(ap, last_arg)初始化可变参数列表。 ap 是一个 va_list 类型的变量 last_arg 是最后一个固定参数的名称也就是可变参数列表之前的参数。 该宏将 ap 指向可变参数列表中的第一个参数。 va_arg(ap, type)获取可变参数列表中的下一个参数。 ap 是一个 va_list 类型的变量 type 是下一个参数的类型。 该宏返回类型为 type 的值并将 ap 指向下一个参数。 va_end(ap)结束可变参数列表的访问。 ap 是一个 va_list 类型的变量。 该宏将 ap 置为 NULL。
    int func(int, … ) {… }int main() {func(2, 2, 3);func(3, 2, 3, 4); }#include stdio.h #include stdarg.hdouble average(int num,…) {va_list valist; //函数内部使用 va_list 类型的变量 va_list 来访问可变参数列表。double sum 0.0;int i;/
    为 num 个参数初始化 valist /va_start(valist, num);/// 访问所有赋给 valist 的参数 /for (i 0; i num; i){sum va_arg(valist, int);// va_arg() 宏获取下一个整数参数}/ 清理为 valist 保留的内存 */va_end(valist); //va_end() 宏结束可变参数列表的访问。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)); }■ 三维数组的传递 ■ 计算公式最大值最小值和中间值 最大值 #define MAX(a, b) (((a) (b) ) ? (a) : (b)) 最小值 #define MIN(a, b) (((a) (b) ) ? (a) : (b)) 中间值 #define MID(a,b,c) ab?(ac?(bc?b:c):(a)):(ac?(a):(bc?c:b))■ memcpy #define MIN(a, b) (((a) (b)) ? (a) : (b)) #define MAX(a, b) (((a) (b)) ? (a) : (b))#define MEMCPY(a, b, c) memcpy((a),(b),©)■ offsetof(TYPE, MEMBER) 计算结构体变量中元素的位置 #define offsetof(TYPE, MEMBER) ((size_t) ((TYPE *)0)-MEMBER)■ 结构体数组初始化 typedef enum { FILE_DIR 0,FILE_VIDEO,FILE_MUSIC,FILE_IMAGE,FILE_TXT,FILE_OTHER,FILE_ALL, }file_type_t; //valid types arranged be alphabettypedef enum {MEDIA_TYPE_VIDEO, // 电影MEDIA_TYPE_MUSIC, // 音乐MEDIA_TYPE_PHOTO, // 照片MEDIA_TYPE_TXT, // 电子书MEDIA_TYPE_COUNT, } media_type_t;#define MEDIA_FILE_DEF(x,y)
    { .ext_name x, .ext_count HC_ARRAY_SIZE(x), .file_type y,
    }#define EXT_NAME_LEN 12typedef struct{char (*ext_name)[EXT_NAME_LEN];int ext_count;file_type_t file_type; }file_ext_t;static file_ext_t m_meida_ext[] {[MEDIA_TYPE_VIDEO] MEDIA_FILE_DEF(m_movie_support_ext, FILE_VIDEO), [MEDIA_TYPE_MUSIC] MEDIA_FILE_DEF(m_music_support_ext, FILE_MUSIC), [MEDIA_TYPE_PHOTO] MEDIA_FILE_DEF(m_photo_support_ext, FILE_IMAGE), [MEDIA_TYPE_TXT] MEDIA_FILE_DEF(m_txt_support_ext, FILE_TXT), }; ■ 结构体数组传参 typedef struct {int Area; // 区域代码int DBNumber; // DB号int Start; // 起始地址int Amount; // 元素数量int WordLen; // 数据类型void *pData; // 数据指针int DataSize; // 数据大小(字节个数)const char *Desc; // 条目描述 } TS7DataItem, PS7DataItem;——main函数初始化——————————–TS7DataItem items[MAX_ITEMS] {// Area, DB, Start, Amount, WordLen, pData, DataSize, Desc{S7_AREA_DB, 10, 0, 4, S7_TS_BYTE, NULL, 4, DB10.DBB0-DBB3}, // DB10.DBB0-DBB3 4字节{S7_AREA_PE, 0, 0, 3, S7_TS_BIT, NULL, 1, I0.0-I0.2} // 输入位I0.0-I0.2// {S7_AREA_MK, 0, 100, 1, S7WLWord, NULL, 2, MW100}, // MW100};TS7DataItem items[MAX_ITEMS] {0}; items[0].Area S7_AREA_DB;
    items[0].DBNumber 1; items[0].Start 0; items[0].Amount 0; items[0].WordLen 4; items[0].pData 9900; items[0].DataSize 34; items[0].Desc buf1;items[1].Area S7_AREA_PE; items[1].DBNumber 2; items[1].Start 2; items[1].Amount 2; items[1].WordLen 3; items[1].pData 8800; items[1].DataSize 34; items[1].Desc buf2;items[2].Area S7_AREA_PE; items[2].DBNumber 2; items[2].Start 2; items[2].Amount 2; items[2].WordLen 3; items[2].pData 8800; items[2].DataSize 34; items[2].Desc buf2;int num sizeof(items) / sizeof(items[0]); num 3; Cli_ReadMultiVars((PS7DataItem
    )items, num);——函数调用——————————— int Cli_ReadMultiVars(PS7DataItem* item, int ItemsCount,SendFun sendFun) {SendReadData sndData{0};printf(-ItemsCount%d—–%p—\n,ItemsCount,item);for(int i0;iItemsCount;i){ TS7DataItem* item1 (TS7DataItem)itemi; //指针1 等于下个元素的地址 itemi items[1]的地址 printf(i%d, Area%d, DBNumber%d, Start%d, Amount%d, WordLen%d \n,i,item1-Area,item1-DBNumber,item1-Start,item1-Amount,item1-WordLen // item1-pData, // item1-DataSize, // item1-Desc );} } ——输出结果———————————i0, Area132, DBNumber1, Start0, Amount0, WordLen4 i1, Area129, DBNumber2, Start2, Amount2, WordLen3 i2, Area129, DBNumber2, Start2, Amount2, WordLen3 ■ str 函数 ■ _strlen函数实现 static int _strlen(const char s) {int Len;Len 0;if (s NULL) {return 0;}do {if (*s 0) {break;}Len;s;} while (1);return Len; } ■ strstr函数使用 [1] 函数原型 char *strstr(const char *haystack, const char *needle); [2] 头文件 #include string.h [3] 函数功能 搜索子串在指定字符串中第一次出现的位置 [4] 参数说明 haystack –被查找的目标字符串父串 needle –要查找的字符串对象子串 注若needle为NULL, 则返回父串 [5] 返回值 (1) 成功找到返回在父串中第一次出现的位置的 char *指针 (2) 若未找到也即不存在这样的子串返回: “NULL” #include stdio.h #include string.h int main(int argc, char *argv[]) {char *res strstr(xxxhost: www.baidu.com, host);if(res NULL) printf(res1 is NULL!\n);else printf(%s\n, res); // print:–host: www.baidu.comres strstr(xxxhost: www.baidu.com, cookie);if(res NULL) printf(res2 is NULL!\n);else printf(%s\n, res); // print:–res2 is NULL!return 0; }■ strcasestr函数 [1] 描述 strcasestr函数的功能、使用方法与strstr基本一致。 [2] 区别 strcasestr函数在子串与父串进行比较的时候“不区分大小写” [3] 函数原型 #define _GNU_SOURCE #include string.h char *strcasestr(const char *haystack, const char *needle); #define _GNU_SOURCE // 宏定义必须有否则编译会有Warning警告信息 #include stdio.h #include string.h int main(int argc, char *argv[]) {char *res strstr(xxxhost: www.baidu.com, Host);if(res NULL) printf(res1 is NULL!\n);else printf(%s\n, res); // print:–host: www.baidu.comreturn 0; }■ char *strdup(const char s); strdup()函数主要是拷贝字符串s的一个副本由函数返回值返回这个副本有自己的内存空间和s没有关联。 strdup 函数复制一个字符串使用完后要使用delete函数删除在函数中动态申请的内存 strdup函数的参数不能为NULL一旦为NULL就会报段错误因为该函数包括了strlen函数而该函数参数不能是NULL。 注 strdup()函数是c语言中常用的一种字符串拷贝库函数一般和free()函数成对出现。 char __strdup(const char* s) {size_t len strlen(s) 1;void* ret malloc(len);if(retnullptr){return nullptr;}return (char*)ret; }■ C库函数 ■ ftell(FILE *stream) C 库函数 long int ftell(FILE *stream) 返回给定流 stream 的当前文件位置。 声明下面是 ftell() 函数的声明。 long int ftell(FILE *stream) 参数 stream – 这是指向 FILE 对象的指针该 FILE 对象标识了流。 返回值 该函数返回位置标识符的当前值。如果发生错误则返回 -1L全局变量 errno 被设置为一个正值。 ■ printf ■ snprintf int snprintf(char *str, size_t size, const char *format, …) snprintf() 是一个 C 语言标准库函数用于格式化输出字符串并将结果写入到指定的缓冲区与 sprintf() 不同的是 snprintf() 会限制输出的字符数避免缓冲区溢出。str – 目标字符串用于存储格式化后的字符串的字符数组的指针。 size – 字符数组的大小。 format – 格式化字符串。 … – 可变参数可变数量的参数根据 format 中的格式化指令进行格式化。。■ getopt() 函数 C语言处理参数的 getopt() 函数 while ((ch getopt(argc, argv, hHsSp:P:)) ! EOF) {switch(ch) {case h :case H :usbd_msg_help_info();return 0;case p :case P :usb_port atoi(optarg);udc_name get_udc_name(usb_port);if(udc_name NULL){printf([error] parameter(-p {usb_port}) error,please check for help information(cmd: g_mass_storage -h)\n);return -1;}break;case s :case S :hcusb_gadget_msg_deinit();hcusb_set_mode(usb_port, MUSB_HOST);is_deinit true;break;default:break;} }■ bzero 在string.h头文件中 原型为void bzero(void s, int n); //能够将内存块字符串的前n个字节清零■ sscanf 利用 sscanf和正则表达式提取数据 参考博主 %c 一个单一的字符 %d 一个十进制整数 %i 一个整数 %e, %f, %g 一个浮点数 %o 一个八进制数 %s 一个字符串 %x 一个十六进制数 %p 一个指针 %n 一个等于读取字符数量的整数 %u 一个无符号整数 %[ ] 一个字符集 正则就使用这个而且仅支持贪婪模式能匹配个多少就匹配多少个 %% 一个精度符 示例一 #include stdio.h #define STR ATCOMaabbcc void main(void) {int result 0;char str_para1[50] {0};result sscanf(STR, ATCOM%s, str_para1);printf(result—— %d \r\n, result);printf(parameter1– %s \r\n, str_para1); }输出 result—— 1 parameter1– aabbccsscanf(ATCOMaabbcc, ATCOM%3s, str_para1); printf(result—— %d \r\n, result); printf(parameter1– %s \r\n, str_para1); 输出 result—— 1 parameter1– aab示例二 示例三 数字字符串 char str[32] ; sscanf(123456abcdedf, %31[0-9a-z], str); printf(str%s/n, str);输出结果 str123456abcdedf上面的格式中[0-9]表示这是一个仅包含0-9这几个字符的字符串 前面使用数字31修饰词表示这个字符串缓冲区的最大长度(这也是sscanf最为人诟病的地方很容易出现缓冲区溢出错误 实际上sscanf是可以避免出现缓冲区溢出的只要在书写任何字符串解析的格式时注意加上其缓冲区尺寸的限制)。示例四 在格式[]中增加了a-z的描述。 char str[32] ; sscanf(123456abcdedf, %31[^a-z], str); printf(str%s/n, str);输出结果 str123456示例五 har str[32] ; int ret sscanf(123456abcdedf, %[0-9]%31[a-z], str); printf(ret%d, str%s/n,ret, str);输出结果 ret1, strabcdedf加上*修饰表示一个被忽略的数据同时也不需要为它准备空间存放解析结果。 如上面的例子中我们就只使用了str一个参数存放%31[a-z]的解析结果而sscanf也只返回1表示只解析了一个数据。示例六 在[]中增加表示相反的意思上面的[a-z]表示一个不包含任何a-z之间的字符串。 char str[32] ; sscanf(123456abcdedf, %31[^a-z], str); //31限制str字节大小 printf(str%s/n, str);输出结果 str123456