C语言使用记录

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>

C语言 分割字符串含有两个连续的分隔符的情况
strsep

/*
 * 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;问结果是多少


浮点数

  1. C语言判断float是不是NaN

#include <math.h>
isnan(f)
  1. 浮点数相等判断
    在程序中如何判断两个浮点数相等
/* FLT_EPSILON为单精度浮点数精度,包含在float.h头文件中 */
fabs(test_float_data-35.6f)<FLT_EPSILON
35.6f35.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;

格式化输出

详解C语言中的 %*s 和 %.*s

//接口实现
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语言特殊用法

双感叹号!!运算符的用法
神奇的Duff’s device

C语言第三方库

littlefs

https://github.com/littlefs-project/littlefs
littlefs

minIni

minIni - a minimal INI file parser
闪存中的键值对:无文件系统 minINI
McuOnEclipseLibrary

os

cola_os

mem

xmem
mem_malloc

json

cJSON

SPI驱动SD卡

sdcard_spi
sdcard_spi_driver

Easylogger

armink/EasyLogger
EasyLogger | 一款轻量级且高性能的日志库
3个适用于单片机开发的开源日志库

hardfalult

CmBacktrace | 一款 ARM Cortex-M 系列 MCU 错误追踪库
Cortex-M系列HardFault_Handler()最强分析“CmBacktrace”
HardFault调试方式(keil)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值