C语言字符串与指针操作技巧解析
1. 字符串的本质与指针操作在嵌入式开发中字符串处理是最基础也是最重要的技能之一。很多人虽然每天都在使用字符串但对它的本质理解却不够深入。实际上C语言中的字符串本质上就是一个字符指针它指向内存中连续存储的字符序列的首地址。1.1 字符串即指针的实例解析让我们看一个将数值转换为16进制字符串的例子void Value2String(unsigned char value, char *str) { char *Hex_Char_Table 0123456789ABCDEF; str[0] 0; str[1] X; str[4] 0; str[2] Hex_Char_Table[value4]; str[3] Hex_Char_Table[value0x0F]; }这个函数可以进一步简化为void Value2String(unsigned char value, char *str) { str[0]0; str[1]X; str[4]0; str[2]0123456789ABCDEF[value4]; str[3]0123456789ABCDEF[value0x0F]; }关键点字符串常量本身就是指针可以直接用下标操作符访问其中的字符。这种写法不仅简洁还能减少一个指针变量的使用在资源受限的嵌入式系统中尤为重要。1.2 字符串与字节数组的等价性字符串和字节数组在内存中的存储方式非常相似主要区别在于字符串通常以\0结尾。我们可以利用转义字符在字符串中表示任意字节值const unsigned char array[10] {0,1,2,3,4,5,6,7,8,9}; char *array_str \x00\x01\x02\x03\x04\x05\x06\x07\x08\x09;这两种定义方式在内存中的存储几乎相同后者多了一个\0结束符。理解这一点对于处理二进制数据和字符串的相互转换非常有帮助。2. 字符串处理的高级技巧2.1 字符串常量的连接C语言编译器会自动将相邻的字符串常量连接起来这个特性可以用来提高代码可读性printf(A:%d B:%d C:%d D:%d E:%d F:%d\r\n,1,2,3,4,5,6); // 可以改写为更易读的形式 printf(A:%d B:%d C:%d D:%d E:%d F:%d\r\n,1,2,3,4,5,6);2.2 字符串分割的高效方法处理带分隔符的长字符串时传统方法是逐个查找分隔符并提取子串。更高效的做法是将分隔符替换为\0使长字符串在内存中自然分割为多个子串char *substr(char *str, int n) { unsigned char len strlen(str); for(; len0; len--) { if(str[len-1] ) str[len-1]0; } for(; n0; n--) { str (strlen(str)1); } return str; }这种方法避免了频繁的内存拷贝特别适合资源受限的嵌入式系统。3. 数值处理的底层原理3.1 提取数值各位数码在嵌入式显示应用中经常需要提取数值的各位数码。虽然可以使用sprintf但在资源受限的环境中直接计算更高效void getdigi(unsigned char *digi, unsigned int num) { digi[0] (num/10000)%10; digi[1] (num/1000)%10; digi[2] (num/100)%10; digi[3] (num/10)%10; digi[4] num%10; }对于浮点数可以先将小数部分转换为整数再处理void getdigi(unsigned char *digi1, unsigned char *digi2, float num) { unsigned int temp1 num; unsigned int temp2 ((num-temp1)*10000); digi1[0] (temp1/1000)%10; digi1[1] (temp1/100)%10; digi1[2] (temp1/10)%10; digi1[3] (temp1)%10; digi2[0] (temp2/1000)%10; digi2[1] (temp2/100)%10; digi2[2] (temp2/10)%10; digi2[3] (temp2)%10; }3.2 浮点数的底层操作浮点数在内存中也是以字节序列存储的理解这一点可以实现一些高效操作// 传统取反方法 float a 3.14; float b a * -1.0; // 直接操作符号位的方法 float a 3.14; float b; ((unsigned char *)a)[3] ^ 0x80; // 修改IEEE754浮点数的符号位 b a;注意事项直接操作浮点数的内存表示需要对浮点数的存储格式有深入了解在跨平台开发时要特别注意字节序问题。4. printf的灵活应用4.1 printf的底层原理printf函数的核心是fputc通过重定向fputc可以实现printf输出到不同设备// 输出到串口1的fputc实现 int fputc(int ch, FILE *f) { while((USART1-SR 0x40) 0); USART1-DR (u8)ch; return ch; }4.2 多串口分时复用printf在需要多个串口输出时可以通过全局变量控制输出目标unsigned char usart_select 0; int fputc(int ch, FILE *f) { switch(usart_select) { case 0: // USART1 while((USART1-SR 0x40) 0); USART1-DR (u8)ch; break; case 1: // USART2 while((USART2-SR 0x40) 0); USART2-DR (u8)ch; break; case 2: // USART3 while((USART3-SR 0x40) 0); USART3-DR (u8)ch; break; } return ch; } // 使用宏简化切换 #define USE_DEBUG_USART usart_select0 #define USE_WIFI_USART usart_select1 #define USE_GPRS_USART usart_select25. 数据类型的本质与直接操作5.1 整型数的底层操作理解数据类型的本质后可以实现一些高效操作// 传统取反方法 int a 10; int b -a; // 通常编译为neg指令或0-a // 位操作取反方法 int a 10; int b (~a) 1; // 按位取反后加15.2 浮点数的比较由于浮点数的精度问题直接使用比较可能不准确float a, b; // 错误做法 if(a b) { ... } // 正确做法 if(fabs(a-b) 0.000001) { ... }经验之谈在嵌入式系统中如果不需要小数运算尽量使用定点数代替浮点数可以大幅提高性能并减少代码尺寸。6. for循环的创造性使用6.1 for循环的本质解析for循环的三个部分可以包含任意表达式// 标准for循环 for(int i0; i100; i) { ... } // 创造性用法 int i0; for(printf(Start:\n); i10; i) { printf(%d\n, i); }6.2 for循环实现条件判断可以用for循环模拟if语句// 传统if语句 if(strstr(hello world!, abc)) { printf(Found\n); } // 用for循环实现 char *p; for(pstrstr(hello world!, abc); p; pNULL) { printf(Found\n); }6.3 复杂for循环示例char *p; unsigned char n; for(pablmnl45ln,n0; ((*pl)?(n):0), *p; p); // 执行后n的值为3统计l的出现次数这些技巧在实际开发中可能看起来有些炫技但深入理解它们可以帮助我们更好地阅读和理解他人的代码特别是在维护一些开源项目时。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2497690.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!