0 标准库
Part I Part 1 Brief Overview of C89 vs C99 vs C11
C Library Overview
Information Technology —Programming languages, their environments and system software interfaces —Extensions to the C Library
易错点
const char * 、char const *、 char * const 三者的区别
const char * p 和 char const * p 是等效的,表示p所指向的内容为常量,不能通过修改,即无法使用*p=x;但是不表示不能通过其他的变量修改p指向的内容,例如:char *k=p,此时可以通过k修改p指向的内容;
char * const q,表示q为指针常量,不能将其他的指针赋值给q,但是可以通过q修改其指向的内容
0.1 <string.h>
/*
* Get next token from string *stringp, where tokens are possibly-empty
* strings separated by characters from delim.
*
* Writes NULs into the string at *stringp to end tokens.
* delim need not remain constant from call to call.
* On return, *stringp points past the last NUL written (if there might
* be further tokens), or is NULL (if there are definitely no moretokens).
*
* If *stringp is NULL, strsep returns NULL.
*/
char *strsep(char **stringp, const char *delim)
{
char *s;
const char *spanp;
int c, sc;
char *tok;
if ((s = *stringp)== NULL)
return (NULL);
for (tok = s;;) {
c = *s++;
spanp = delim;
do {
if ((sc =*spanp++) == c) {
if (c == 0)
s = NULL;
else
s[-1] = 0;
*stringp = s;
return (tok);
}
} while (sc != 0);
}
/* NOTREACHED */
}
C语言版本
Exploring C11/C18 and C++14/C++17
请教当前C语言最新标准是什么?C11、C17还是C18?
C语言编译
#if #error 属于预编译,不可以使用sizeof
static_assert属于编译,可以使用sizeof
assert属于运行期间断言,定义NDEBUG取消assert
static_assert 与assert区别
_Static_assert 关键字和 static_assert 宏
assert 宏
编译的整个过程:预编译、编译、汇编、链接
IAR中常用的 #pragma 命令和扩展关键字
一种在编译阶段检查结构体大小的方法
编译阶段检测结构体大小是否正常
IAR编译器支持GCC的__attribute__机制
C语言__attribute__的使用
#pragma是C语言规定的预编译指令
#pragma命令详解(一)
问题总结
C语言位操作中指定的某一位数置0、置1、取反
C语言整型溢出会怎样
C Tutorial
Steve Summit’s home page
stdint.h - integer types
Recommended C style and coding rules
数据结构之树的存储结构 C语言版
文件目录结构的树形显示(数据结构课程设计,树、队列,C语言描述)
C语言标准库函数qsort排序的介绍与使用
printf用法大全,C语言printf格式控制符一览表
浮点数的表示方法
C语言返回局部变量
有符号数与无符号数的转换:
C语言无符号数减法
w为数值存储的二进制位数,S表示有符号数十进制数值,U表示无符号数十进制数值
eg:
假设一个有符号16位整数码值为0xFFFE,要得到该整数的真实十进制值,需要进行如下转换:
按无符号数处理0xFFFE=65534 ,因为65534>32768(2^15)
需要进行转换:真实数值=65534-65536(2^16)=-2
/**
* @brief 获取指定日期为星期几
* @param year:年,2000
* @param month:月 1-12
* @param day:日 1-31
* @retval 星期几 0=周日,1-6=周一~周六
*/
static uint8_t GetWeekday(uint16_t year,uint8_t month,uint8_t day)
{
int16_t y, c, m, d, w;
y = year % 100;//年 如2015 即年是15年
c = year / 100;// 年份前两位 如2015即20
m = month; d = day;
if (m == 1 || m == 2) { //判断月份是否为1或2
y--;
m += 12;//某年的1、2月要看作上一年的13、14月来计算
}
w = y + y / 4 + c / 4 - 2 * c + 13 * (m + 1) / 5 + d - 1;//蔡勒公式的公式
while (w < 0) w += 7;//确保余数为正
w %= 7;
return w ;
}
/**
* @brief 获取每月的天数,
* @param year:年,2000
* @param month:月 1-12
* @retval 当前月的天数
*/
static uint8_t GetDaysOfMonth(uint16_t year,uint8_t month)
{
uint8_t DaysOfMonth=0;
switch(month)
{
case 2:
DaysOfMonth=((year%4==0 &&(year%100)!=0)||year%400==0)?29:28;
break;
case 4: case 6: case 9: case 11:
DaysOfMonth=30;
break;
default:
DaysOfMonth=31;
break;
}
return DaysOfMonth;
}
int day_diff(int year_start, int month_start, int day_start, int year_end, int month_end, int day_end)
{
int y2, m2, d2;
int y1, m1, d1;
/*用于判断日期是否大于3月(2月是判断闰年的标识),还用于纪录到3月的间隔月数*/
m1 = (month_start + 9) % 12;
/*如果是1月和2月,则不包括当前年(因为是计算到0年3月1日的天数)*/
y1 = year_start - m1/10;
/*365*y1 是不算闰年多出那一天的天数, y1/4 - y1/100 + y1/400 是加所有闰年多出的那一天
(day_start - 1) 用于计算当前日到1日的间隔天数。*/
d1 = 365*y1 + y1/4 - y1/100 + y1/400 + (m1*306 + 5)/10 + (day_start - 1);
/*(m2*306 + 5)/10 用于计算到当前月到3月1日间的天数 306=365-31-28(1月和2月),5是全年中不是31天月份的个数*/
m2 = (month_end + 9) % 12;
y2 = year_end - m2/10;
d2 = 365*y2 + y2/4 - y2/100 + y2/400 + (m2*306 + 5)/10 + (day_end - 1);
return (d2 - d1);
}
OpenHarmony / kernel_liteos_a,goto语句使用
https://openharmony.gitee.com/openharmony/kernel_liteos_a/blob/master/kernel/base/core/los_task.c
LITE_OS_SEC_TEXT_INIT UINT32 LOS_TaskCreateOnly(UINT32 *taskID, TSK_INIT_PARAM_S *initParam)
{
UINT32 intSave, errRet;
VOID *topStack = NULL;
VOID *stackPtr = NULL;
LosTaskCB *taskCB = NULL;
VOID *pool = NULL;
errRet = OsTaskCreateParamCheck(taskID, initParam, &pool);
if (errRet != LOS_OK) {
return errRet;
}
taskCB = OsGetFreeTaskCB();
if (taskCB == NULL) {
errRet = LOS_ERRNO_TSK_TCB_UNAVAILABLE;
goto LOS_ERREND;
}
errRet = OsTaskSyncCreate(taskCB);
if (errRet != LOS_OK) {
goto LOS_ERREND_REWIND_TCB;
}
OsTaskStackAlloc(&topStack, initParam->uwStackSize, pool);
if (topStack == NULL) {
errRet = LOS_ERRNO_TSK_NO_MEMORY;
goto LOS_ERREND_REWIND_SYNC;
}
stackPtr = OsTaskStackInit(taskCB->taskID, initParam->uwStackSize, topStack, TRUE);
errRet = OsTaskCBInit(taskCB, initParam, stackPtr, topStack);
if (errRet != LOS_OK) {
goto LOS_ERREND_TCB_INIT;
}
if (OsConsoleIDSetHook != NULL) {
OsConsoleIDSetHook(taskCB->taskID, OsCurrTaskGet()->taskID);
}
*taskID = taskCB->taskID;
return LOS_OK;
LOS_ERREND_TCB_INIT:
(VOID)LOS_MemFree(pool, topStack);
LOS_ERREND_REWIND_SYNC:
#if (LOSCFG_KERNEL_SMP_TASK_SYNC == YES)
OsTaskSyncDestroy(taskCB->syncSignal);
#endif
LOS_ERREND_REWIND_TCB:
SCHEDULER_LOCK(intSave);
OsInsertTCBToFreeList(taskCB);
SCHEDULER_UNLOCK(intSave);
LOS_ERREND:
return errRet;
}
求三个数中最小的
#include <stdio.h>
int min_fun(int a, int b, int c)
{
int min;
return c<(min=a<b?a:b)?c:min;
}
int max_fun(int a, int b, int c)
{
int max;
return c>(max=a>b?a:b)?c:max;
}
int main()
{
int a =-5, b=6, c=7;
int ret = min_fun(a,b,c);
printf("min value:%d \n",ret);
ret = max_fun(a,b,c);
printf("max value:%d \n",ret);
return 0;
}
交换字节顺序,大小端变换
/* 交换字节顺序,大小端变换 */
#define ENDIAN_SWAP_16(x) ((((uint16)(x) & 0xff00) >> 8) |(((uint16)(x) & 0x00ff) << 8))
#define ENDIAN_SWAP_32(x) ((((uint32_t)(x) & 0xff000000) >> 24) | \
(((uint32_t)(x) & 0x00ff0000) >> 8) | \
(((uint32_t)(x) & 0x0000ff00) << 8) | \
(((uint32_t)(x) & 0x000000ff) << 24))
C语言中不同类型的数据转换规则
C语言中不同类型的数据转换规则
1.自动类型转换
在C语言中,自动类型转换遵循以下规则:
①若参与运算量的类型不同,则先转换成同一类型,然后进行运算
②转换按数据长度增加的方向进行,以保证精度不降低。如int型和long型运算时,先把int量转成long型后再进行运算
a、若两种类型的字节数不同,转换成字节数高的类型
b、若两种类型的字节数相同,且一种有符号,一种无符号,则转换成无符号类型
③所有的浮点运算都是以双精度进行的,即使是两个float单精度量运算的表达式,也要先转换成double型,再作运算.
④char型和short型参与运算时,必须先转换成int型
⑤在赋值运算中,赋值号两边量的数据类型不同时,赋值号右边量的类型将转换为左边量的类型。
如果右边量的数据类型长度比左边长时,将丢失一部分数据,这样会降低精度,丢失的部分按四舍五入向前舍入,
更正:此处在博友反馈后,代码VS和Linux下实测丢失部分是直接舍去,而不是四舍五入;
2.强制类型转换
强制类型转换一般格式如下:
(类型名)(表达式)
这种强制类型转换操作并不改变操作数本身
首先进行一个实验,分别定义一个signed int型数据和unsigned int型数据,然后进行大小比较:
unsigned int a = 20signed int b = -130b?还是b>a?实验证明b>a,也就是说-130>20,为什么会出现这样的结果呢?
这是因为在C语言操作中,如果遇到无符号数与有符号数之间的操作,编译器会自动转化为无符号数来进行处理,因此a=20,b=4294967166,这样比较下去当然b>a了。
unsigned int a=20signed int b=-130;a + b结果输出为4294967186,同样的道理,在运算之前,a=20,b被转化为4294967166,所以a+b=4294967186
减法和乘法的运算结果类似。 uint i=3;i * -1;问结果是多少
浮点数
- C语言判断float是不是NaN
#include <math.h>
isnan(f)
- 浮点数相等判断
在程序中如何判断两个浮点数相等
/* FLT_EPSILON为单精度浮点数精度,包含在float.h头文件中 */
fabs(test_float_data-35.6f)<FLT_EPSILON
35.6f和35.6在程序中,两者并不相同
35.6f表示的是单精度浮点数,实际值为3.55999985E+1
35.6表示的是双精度浮点数=35.6d,实际值为3.5600000000000001E+1
因此当使用以下判断时,不会成立
fabs(test_float_data-35.6)<FLT_EPSILON
void* 指针
void指针可以指向任意类型的数据,亦即可用任意数据类型的指针对void指针赋值。例如:
int * pint;
void *pvoid;
pvoid = pint; /* 不过不能 pint= pvoid; */
如果要将pvoid赋给其他类型指针,则需要强制类型转换如:
pint= (int *)pvoid;
格式化输出
//接口实现
void elog_port_output(const char *log, size_t size) {
//日志使用printf输出,printf已经重定向到串口USART1
printf("%.*s", size, log);
}
%s表示字符串输出,.<十进制数>是精度控制格式符,输出字符时表示输出字符的位数,在精度控制时,小数点后的十进制数可以使用*来占位,在后面提供一个变量作为精度控制的具体值。
sprintf注意事项
关于sprintf内存溢出
sprintf, snprintf, _snprintf, sprintf_s 等的区别
C语言snprintf()函数:将格式化的数据写入字符串—sprintf()
snprintf返回值陷阱封装
如何将可变数量的参数传递给printf / sprintf
sprintf( &basic_data_rw_buff, "%s", drv )
当drv大小超过basic_data_rw_buff的大小时,会内存溢出覆盖别的内存且当前程序不会报错,但是会影响别的程序运行且错误难以排查(只显示程序错误,错误不会关联到内存溢出部分)
墙裂建议:只使用snprintf
int snprintf(char *str, int n, char * format [, argument, …]);
注意:snprintf()并不是标C中规定的函数,但是在许多编译器中,厂商提供了其实现的版本。
IAR:参数n是要向str写入3个字符,包括’\0’字符;并且返回要写入字符串的实际长度
byte_to_use = 3;
print_byte = snprintf( basic_data_rw_buff, byte_to_use, "%04d/%02d/%02d,", RTC_date_time_now.year, RTC_date_time_now.month,RTC_date_time_now.day );
// byte_to_use=3,且包含'\0'字符;
// print_byte=11,为格式化输出后字符串的实际长度
++ – 使用注意事项
disk.nbr=0;
disk.is_initialized[disk.nbr] = 0;
disk.drv[disk.nbr] = drv;
disk.lun[disk.nbr] = lun;
DiskNum = disk.nbr++;
path[0] = DiskNum + '0';
执行完该段程序后,path[0]=‘0’,原因是:DiskNum = disk.nbr++; 先将disk.nbr的值赋给DiskNum,然后再自增1。
__FILE__ ,__FUNCTION__, __LINE__
C/C++ 宏定义 __FILE__、__LINE__、__func__、__TIME__ 等
__FILE__去掉路径
编译错误锦集
Error #119: cast to type <…> is not allowed
C语言特殊用法
C语言第三方库
littlefs
https://github.com/littlefs-project/littlefs
littlefs
minIni
minIni - a minimal INI file parser
闪存中的键值对:无文件系统 minINI
McuOnEclipseLibrary
os
mem
json
SPI驱动SD卡
Easylogger
armink/EasyLogger
EasyLogger | 一款轻量级且高性能的日志库
3个适用于单片机开发的开源日志库
hardfalult
CmBacktrace | 一款 ARM Cortex-M 系列 MCU 错误追踪库
Cortex-M系列HardFault_Handler()最强分析“CmBacktrace”
HardFault调试方式(keil)