C语言实现交换二进制数的任意两位

最近在用LED组成的数码管,由于位号硬件上有所改动,因而需要进行码值位之间的交换; 以下是一段C语言函数,实现将一个八位的二进制数的任意两位交换: #include /** * @brief 交换一个八位的二进制数的任意两位 * @param x 一个八位的二进制数 * @param i 要交换的两个位置 * @param j 要交换的两个位置 * @return 交换后的结果 */ unsigned char swap_bits(unsigned char x, int i, int j) { // 获取第i位和第j位的值 unsigned char bit_i = (x >> i) & 1; unsigned char bit_j = (x >> j) & 1; // 如果第i位和第j位的值不同,那么交换它们 if (bit_i ^ bit_j) { x ^= (1

April 17, 2023 · 1 min · Rancho

与或非等逻辑运算在程序中的应用

今天写代码的时候再次用到了与或非等逻辑运算符来完成一些二进制处理应用,总结记录一下; 一、缘起 以下是一个显示板的原理图,由于LED数量较多,因此在LED控制方案的选择上是选用了一块串转并的芯片74HC595; 对于74HC595的使用在这里就不再赘述; 74HC595串转并是转出8个输出端,再加上三个直接连接单片机引脚的COM端,理论上是可以很容易控制24个LED灯亮灭,就像是控制三个数码管一样,利用人眼的暂留现象,就可以使这24个LED灯的亮灭随意组合; 每一个COM端和8个LED组成一个组合,然后轮询点亮这三组LED,速度够快的话,就能看到三组LED被同时点亮; 二、解决 为了消除残影问题,在每一组的LED点亮后,应该马上写入使LED全部熄灭的命令,以消除可能会出现的残影问题; 首先定义三个uint8_t变量,例如代码中的LED_NUM1、LED_NUM2、LED_NUM3,然后通过控制COM端,分别向每组LED中写入这三个数据: 第一组 COM引脚排布:100 待写入74HC595中的数据:LED_NUM1 第二组 COM引脚排布:010 待写入74HC595中的数据:LED_NUM2 第三组 COM引脚排布:001 待写入74HC595中的数据:LED_NUM3 然后对于每一个单独LED的控制,就需要用到逻辑运算,例如图中的LED1,其属于第三组LED,因为其控制COM引脚为COM3, 因而代码可以写为: //只点亮LED1,而不影响本组内其他LED的显示 LED_NUM3 = LED_NUM3 | 0x01; //只熄灭LED1,而不影响本组内其他LED的显示 LED_NUM3 = LED_NUM3 & (~0x01); 其他的LED控制也是一样的道理; 可以提取出公式,如LED的编号为07,分别接在74HC595的Q0Q1引脚上,设任意一引脚为n号引脚,则其控制代码为: //只点亮LEDn,而不影响本组内其他LED的显示 LED_NUM = LED_NUM | (0x01 PF0 STCP -> PA12 DS -> PB5 COM1->PA15 COM2->PB3 COM3->PB4 */ /** * @brief 模拟SPI向74HC595芯片发送数据 * @param SendVal 待发送八位数据 * @return 无 */ void HC595SendData(uint8_t SendVal) { uint8_t i; for (i = 0; i 0) { LED_NUM1 = LED_NUM1 | 0x08; } else { LED_NUM1 = LED_NUM1 & (~0x08); } HC595SendData(LED_NUM1); HC595SendData(0x00); HAL_GPIO_WritePin(COM1_PIN_GPIO_Port, COM1_PIN_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(COM2_PIN_GPIO_Port, COM2_PIN_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(COM3_PIN_GPIO_Port, COM3_PIN_Pin, GPIO_PIN_RESET); if (LED_second_case > 1) { LED_NUM2 = LED_NUM2 | 0x08; } else { LED_NUM2 = LED_NUM2 & (~0x08); } HC595SendData(LED_NUM2); HAL_Delay(0); HC595SendData(0x00); HAL_GPIO_WritePin(COM1_PIN_GPIO_Port, COM1_PIN_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(COM2_PIN_GPIO_Port, COM2_PIN_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(COM3_PIN_GPIO_Port, COM3_PIN_Pin, GPIO_PIN_SET); if (LED_second_case > 2) { LED_NUM3 = LED_NUM3 | 0x08; } else { LED_NUM3 = LED_NUM3 & (~0x08); } if (LED_power) { LED_NUM3 = LED_NUM3 | 0x40; } else { LED_NUM3 = LED_NUM3 & (~0x40); } if (LED_smart == 1) { if (pwm_high > pwm_t) { LED_NUM3 = LED_NUM3 | (0x20); } else { LED_NUM3 = LED_NUM3 & (~0x20); } } if (pwm_temp % 100 == 0) { if (flag == 1) { pwm_t--; if (pwm_t == 0) { flag = !flag; } } else { pwm_t++; if (pwm_t == 10) { flag = !flag; } } } pwm_high++; if (pwm_high == 10) { pwm_high = 0; } switch (LED_first_case) { case 1: /* code */ LED_NUM3 = LED_NUM3 | (0x01); LED_NUM3 = LED_NUM3 & (~0x02); LED_NUM3 = LED_NUM3 & (~0x10); LED_NUM3 = LED_NUM3 & (~0x04); break; case 2: /* code */ LED_NUM3 = LED_NUM3 & (~0x01); LED_NUM3 = LED_NUM3 | (0x02); LED_NUM3 = LED_NUM3 & (~0x10); LED_NUM3 = LED_NUM3 & (~0x04); break; case 3: /* code */ LED_NUM3 = LED_NUM3 & (~0x01); LED_NUM3 = LED_NUM3 & (~0x02); LED_NUM3 = LED_NUM3 | (0x10); LED_NUM3 = LED_NUM3 & (~0x04); break; case 4: /* code */ LED_NUM3 = LED_NUM3 & (~0x01); LED_NUM3 = LED_NUM3 & (~0x02); LED_NUM3 = LED_NUM3 & (~0x10); LED_NUM3 = LED_NUM3 | (0x04); break; default: break; } HC595SendData(LED_NUM3); HAL_Delay(0); HC595SendData(0x00); } ,

February 14, 2023 · 2 min · Rancho

刷题中碰到的知识点

记录一下刷题中遇到的知识点; 1、(断言)assert 在程序设计中,断言(assertion)是一种放在程序中的一阶逻辑(如一个结果为真或是假的逻辑判断式),目的是为了标示与验证程序开发者预期的结果-当程序执行到断言的位置时,对应的断言应该为真。若断言不为真时,程序会中止执行,并给出错误消息。

August 8, 2022 · 1 min · Rancho

力扣刷题记录

记录自己刷算法题中遇到的问题和自己的思路; 1、无重复长度的最长子串: 题目描述: 提示: 0 <= s.length <= 5 * 104; s` 由英文字母、数字、符号和空格组成; 思路: 就是做两个标志位,表示当前字符串的起始位置和正在比较的字符位置; 遍历整个字符串; 从起始标志位开始,到结束位置,依次比对是否有重复字符; 如果无,就结束位置加加; 如果有,就计算长度,并移动起始位置到重复字符的下一个位置; 然后一直循环; 要特别考虑: au abb bba 这些特殊排序的字符串; 是因为最后一个没有办法比较; 题解代码: int lengthOfLongestSubstring(char *s) { int str_length = strlen(s); int end_flag = 1, start_flag = 0, max_length = 1; char flag = 0; int i = 0; if (str_length == 0) { return 0; } while (*(s + end_flag) != '\0') { for (i = start_flag; i max_length) { max_length = end_flag - start_flag; } flag = 1; break; } if (i == str_length - 2) { if ((*(s + end_flag + 1) == '\0')) { if (end_flag - start_flag + 1 > max_length) { max_length = end_flag - start_flag + 1; } } } } if (flag == 1) { flag = 0; start_flag = i + 1; } else { end_flag++; } } return max_length; 改进: //方法2 int lengthOfLongestSubstring(char * s){ int str_length=strlen(s); int end_flag=1,start_flag=0,max_length=1; char flag=0; int i=0; if(str_length==0) { return 0; } while(*(s+end_flag)!='\0') { for(i=start_flag;imax_length) { max_length=end_flag-start_flag; } flag=0; start_flag=i+1; } else { if(end_flag-start_flag+1>max_length) { max_length=end_flag-start_flag+1; } end_flag++; } } return max_length; } 2、括号匹配 ...

July 17, 2022 · 2 min · Rancho

malloc和free的用法

在C语言中,内存管理是很重要的,最近刷算法题目也用到了malloc和free函数,在此记录一下; 一、函数原型及说明: malloc(): 函数原型: void *malloc(long NumBytes); 该函数分配了NumBytes个字节,并返回了指向这块内存的指针。如果分配失败,则返回一个空指针(NULL)。 关于分配失败的原因,应该有多种,比如说空间不足就是一种。 free(): 函数原型: void free(void *FirstByte); 该函数是将之前用malloc分配的空间还给程序或者是操作系统,也就是释放了这块内存,让它重新得到自由。 二、函数使用注意事项: 1、申请了内存空间后,必须检查是否分配成功; 2、当不需要再使用申请的内存时,记得释放; 释放后应该把指向这块内存的指针指向NULL,防止程序后面不小心使用了它。 3、这两个函数应该是配对使用; 如果申请后不释放就是内存泄露;如果无故释放那就是什么也没有做。释放只能一次,如果释放两次及两次以上会出现错误(释放空指针例外,释放空指针其实也等于啥也没做,所以释放空指针释放多少次都没有问题)。 **4、虽然malloc()函数的类型是(void ),任何类型的指针都可以转换成(void ),但是最好还是在前面进行强制类型转换,因为这样可以躲过一些编译器的检查; 三、具体例子: // Code... char *Ptr = NULL; Ptr = (char *)malloc(100 * sizeof(char)); if (NULL == Ptr) { exit (1); } gets(Ptr); // code... free(Ptr); Ptr = NULL; // code... 四、原理部分: 1、malloc内存空间的来源: 答案是从堆里面获得空间。 也就是说函数返回的指针是指向堆里面的一块内存。操作系统中有一个记录空闲内存地址的链表。当操作系统收到程序的申请时,就会遍历该链表,然后就寻找第一个空间大于所申请空间的堆结点,然后就将该结点从空闲结点链表中删除,并将该结点的空间分配给程序。 在使用malloc()分配内存空间后,一定要记得释放内存空间,否则就会出现内存泄漏。 2、free释放了什么: free()释放的是指针指向的内存。 注意!释放的是内存,不是指针!指针并没有被释放,指针仍然指向原来的存储空间。指针是一个变量,只有程序结束时才被销毁。释放了内存空间后,原来指向这块空间的指针还是存在!只不过现在指针指向的内容的垃圾,是未定义的,所以说是垃圾。因此,释放内存后把指针指向NULL,防止指针在后面不小心又被解引用了。 free() 不会改变 指针变量本身的值,调用 free() 后它仍然会指向相同的内存空间,但是此时该内存已无效,不能被使用。所以建议将 指针的值设置为 NULL,例如: free(ptr); ptr = NULL; free()的源代码: struct mem_control_block { int is_available; //一般来说应该是一个可用空间的首地址,但这里英文单词却显示出空间是否可用的一个标记 int size; //这是实际空间的大小 }; void free(void *ptr) { struct mem_control_block *free; free = ptr - sizeof(struct mem_control_block); free->is_available = 1; return; } 五、直接定义一个数组和使用malloc动态分配的区别: malloc作为一个包含在头文件下的函数,用于申请空间。 ...

July 16, 2022 · 1 min · Rancho

GCC常见命令及用法

GCC工具介绍以及常见的用法; GCC编译命令: 1. GCC工具 GCC编译器: GCC(GNU Compiler Collection)是由 GNU 开发的编程语言编译器。 GCC最初代表“GNU C Compiler”,当时只支持C语言。 后来又扩展能够支持更多编程语言,包括 C++、Fortran 和 Java 等。 因此,GCC也被重新定义为“GNU Compiler Collection”,成为历史上最优秀的编译器, 其执行效率与一般的编译器相比平均效率要高 20%~30%。 GCC的官网地址为:https://gcc.gnu.org/,在Ubuntu系统下系统默认已经安装好GCC编译器,可以通过如下命令查看Ubuntu系统中GCC编译器的版本及安装路径: GCC编译工具链: GCC编译工具链(toolchain),是指以GCC编译器为核心的一整套工具。它主要包含以下三部分内容: gcc-core:即GCC编译器,用于完成预处理和编译过程,把C代码转换成汇编代码。 Binutils :除GCC编译器外的一系列小工具包括了链接器ld,汇编器as、目标文件格式查看器readelf等。 glibc:包含了主要的 C语言标准函数库,C语言中常常使用的打印函数printf、malloc函数就在glibc 库中。 在很多场合下会直接用GCC编译器来指代整套GCC编译工具链。 Binutils工具集: Binutils(bin utility),是GNU二进制工具集,通常跟GCC编译器一起打包安装到系统,它的官方说明网站地址为: https://www.gnu.org/software/binutils/ 。 在进行程序开发的时候通常不会直接调用这些工具,而是在使用GCC编译指令的时候由GCC编译器间接调用。下面是其中一些常用的工具: as:汇编器,把汇编语言代码转换为机器码(目标文件)。 ld:链接器,把编译生成的多个目标文件组织成最终的可执行程序文件。 readelf:可用于查看目标文件或可执行程序文件的信息。 nm : 可用于查看目标文件中出现的符号。 objcopy: 可用于目标文件格式转换,如.bin 转换成 .elf 、.elf 转换成 .bin等。 objdump:可用于查看目标文件的信息,最主要的作用是反汇编。 size:可用于查看目标文件不同部分的尺寸和总尺寸,例如代码段大小、数据段大小、使用的静态内存、总大小等。 系统默认的Binutils工具集位于/usr/bin目录下,可使用如下命令查看系统中存在的Binutils工具集: # 在Ubantu上执行如下命令 ls /usr/bin/ | grep linux-gnu- glibc库: glibc库是GNU组织为GNU系统以及Linux系统编写的C语言标准库,因为绝大部分C程序都依赖该函数库,该文件甚至会直接影响到系统的正常运行,例如常用的文件操作函数read、write、open,打印函数printf、动态内存申请函数malloc等。 在Ubuntu系统下,libc.so.6是glibc的库文件,可直接执行该库文件查看版本,在主机上执行如下命令: # 在Ubantu上执行如下命令 # 以下是Ubuntu 64位机的glibc库文件路径,可直接执行 /lib/x86_64-linux-gnu/libc.so.6 ...

July 9, 2022 · 2 min · Rancho

算数式计算

字符串算式先转为逆波兰式(后缀表达式),然后计算; 一、求逆波兰表达式 核心思想: 逆波兰算法的核心思想是将普通的中缀表达式转换为后缀表达式。 **什么是中缀表达式?**例如a+b,运算符在两个操作数的中间。这是我们从小学开始学习数学就一直使用的表达式形式。 **什么是后缀表达式?**例如a b + ,运算符在两个操作数的后面。后缀表达式虽然看起来奇怪,不利于人阅读,但利于计算机处理。 转换为后缀表达式的好处是: 去除原来表达式中的括号,因为括号只指示运算顺序,不是实际参与计算的元素。 使得运算顺序有规律可寻,计算机能编写出代码完成计算。 核心步骤: 逆波兰算法的核心步骤就2个: 将中缀表达式转换为后缀表达式,例如输入的原始表达式是 3*(5+7) ,转换得到 3 5 7 + * 根据后缀表达式,按照特定的计算规则得到最终计算结果 具体步骤: 中缀表达式转换为后缀表达式: 你需要设定一个栈SOP,和一个线性表 L 。SOP用于临时存储运算符和左括号分界符( ,L用于存储后缀表达式。 遍历原始表达式中的每一个表达式元素: 如果是操作数,则直接追加到 L中。只有 运算符 或者 分界符( 才可以存放到 栈SOP中; 如果是分界符: 如果是左括号 ( , 则 直接压入SOP,等待下一个最近的 右括号 与之配对。 如果是右括号 ) ,则说明有一对括号已经配对(在表达式输入无误的情况下)。不将它压栈,丢弃它,然后从SOP中出栈,得到元素e,将e依次追加到L里。一直循环,直到出栈元素e 是 左括号 ( ,同样丢弃他。 如果是运算符(用op1表示): 如果SOP栈顶元素(用op2表示) 不是运算符,则二者没有可比性,则直接将此运算符op1压栈。 例如栈顶是左括号 ( ,或者栈为空。 如果SOP栈顶元素(用op2表示) 是运算符 ,则比较op1和 op2的优先级。如果op1 > op2 ,则直接将此运算符op1压栈。 如果不满足op1 > op2,则将op2出栈,并追加到L,再试图将op1压栈,如果如果依然不满足 op1>新的栈顶op2,继续将新的op2弹出追加到L ,直到op1可以压入栈中为止。 ...

July 8, 2022 · 1 min · Rancho

结构体中定义函数指针

前些天看了一个RobMaster哨兵机器人的开源代码,里边用了C语言结构体定义函数指针,看得有点迷糊,所以自己查了一些相关的资料; C语言结构体是由一系列具有相同类型或不同类型的数据构成的数据集合。所以,标准C中的结构体是不允许包含成员函数的,当然C++中的结构体对此进行了扩展。那么,我们在C语言的结构体中,只能通过定义函数指针的方式,用函数指针指向相应函数,以此达到调用函数的目的。 结构体定义: struct data_demo { int result; int (*add)(int, int); int (*sub)(int, int); }; 完整例子: #include struct data_demo { int result; int (*add)(int, int); int (*sub)(int, int); }; int add_demo(int i, int j) { return i+j; } int sub_demo(int i, int j) { return i-j; } int main(void) { struct data_demo data = {0, add_demo, sub_demo}; // 也可以如下初始化结构体 完全等价 /* struct data_demo data = { .result = 0, .add = add_demo, .sub = sub_demo }; */ data.result = data.add(20, 30); printf("add:\t%d\n", data.result); data.result = data.sub(30, 20); printf("sub:\t%d\n", data.result); return 0; } 运行结果: ...

May 10, 2022 · 1 min · Rancho

union和struct存放格式

union、struct所占的空间大小; union: ​ 当多个数据需要共享内存或者多个数据每次只取其一时,可以利用联合体(union)。在C Programming Language 一书中对于联合体是这么描述的: 联合体是一个结构; 它的所有成员相对于基地址的偏移量都为0; 此结构空间要大到足够容纳最”宽”的成员; 其对齐方式要适合其中所有的成员; ​ 下面解释这四条描述: 由于联合体中的所有成员是共享一段内存的,因此每个成员的存放首地址相对于于联合体变量的基地址的偏移量为0,即所有成员的首地址都是一样的。为了使得所有成员能够共享一段内存,因此该空间必须足够容纳这些成员中最宽的成员。对于这句“对齐方式要适合其中所有的成员”是指其必须符合所有成员的自身对齐方式。 例如: #include using namespace std; union A{ int a[5]; char b; double c; }; int main(){ cout **a** **b** **b** **b** **b** **c** **c** ```c++ //例如: struct B { char a; short b; int c; } //则sizeof(A)大小应该是4+4=8字节 a c c b b b b //例如: struct B { char a; char b[2]; char c[4]; } //则sizeof(A)大小应该是1+2+4=7字节 a b b c c c c ...

May 6, 2022 · 2 min · Rancho