C 语言零散笔记
C 语言零散笔记

C 语言零散笔记

记录一些 C 语言零散知识点。

未分类

Visual Studio 中防止编译器报错

#define _CRT_SECURE_NO_WARNINGS

防止程序一闪而过:

#include "windows.h"

int main(void)
{
  system("pause");
  return 0;
}

常用预定义宏:

__FILE__:是当前编译的文件的文件名 是一个字符串

__TIME__:是当前编译的文件的编译时间 格式是hh:mm:ss 是字符串

__DATE__:是当前编译的文件的编译日期 格式是Mmm:dd:yyyy 是字符串

__LINE__:是调用该宏语句所在的行数,是个十进制数

两点直线拟合:

static double LineFit(double x)
{
  const double x0 = 0.1;
  const double x1 = 0.2;
  const double y0 = 100.0;
  const double y1 = 150.0;
  double rate, result;
  rate = (y1 - y0) / (x1 - x0);
  result = y0 + rate * (x - x0);
  return result;
}

下述代码实现了结构体按 1 字节对齐。

//方式一
#pragma pack(1)
typedef struct
{
  int a,
  char b,
  double c,
}Struct;
#pragma pack()

//方式2
#pragma pack(push)
#pragma pack(1)
typedef struct
{
  int a,
  char b,
  double c,
}Struct;
#pragma pack(pop)

下述代码实现了数组起始地址按照 32 字节对齐。

__align(32) unsigned char s_arrDataBuf[1024] = {0};

下述代码实现了将数组起始地址固定分配到 0x6C000000。

__align(32) unsigned chars_arrDataBuf[1024] __attribute__((at((u32)0x6C000000)));

字符串相关

追加字符串:

/*********************************************************************************************************
* 函数名称:StringAdd
* 函数功能:追加字符串
* 输入参数:target:目标字符串,add:追加的字符串
* 输出参数:void
* 返 回 值:void
* 创建日期:2018年01月01日
* 注    意:
*********************************************************************************************************/
void StringAdd(char* target, char* add)
{
  unsigned int i, j;

  //确定目标字符串结尾
  i = 0;
  while(0 != target[i]) {i++;}

  //拷贝字符串
  j = 0;
  while(0 != add[j])
  {
    target[i] = add[j];
    i++;
    j++;
  }

  //加上字符串结尾
  target[i] = 0;
}

修改文件名后缀:

/*********************************************************************************************************
* 函数名称:NameModifySuffix
* 函数功能:修改文件名后缀
* 输入参数:name:当前文件名(含后缀),suffix:目标后缀
* 输出参数:void
* 返 回 值:void
* 创建日期:2018年08月31日
* 注    意:
*********************************************************************************************************/
void NameModifySuffix(char* name, char* suffix)
{
  unsigned int pointPos; //后缀点所在位置
  unsigned int i;        //循环变量

  pointPos = 0;
  i = 0;
  while (0 != name[i]) //查找后缀起始位置
  {
    if ('.' == name[i])
    {
      pointPos = i;
    }
    i++;
  }
  name[pointPos + 1] = 0;
  StringAdd(name, suffix);
}

从字符串中移除某个字符:

/*********************************************************************************************************
* 函数名称: StringRemoveChar
* 函数功能: 从字符串中移除某个字符
* 输入参数: string:需要操作的字符串,c:需要移除的字符
* 输出参数: void
* 返 回 值: void
* 创建日期: 2023年08月31日
* 注    意:
*********************************************************************************************************/
static void StringRemoveChar(char* string, char c)
{
  char* str;
  while (0 != *string)
  {
    if (*string == c)
    {
      str = string;
      while (0 != *str)
      {
        *str = *(str + 1);
        str++;
      }
    }
    string++;
  }
}

替换字符串中的字符

/*********************************************************************************************************
* 函数名称: StringReplaceChar
* 函数功能: 替换字符串中的字符
* 输入参数: string:需要操作的字符串,cold:旧字符,cnew:新字符
* 输出参数: void
* 返 回 值: void
* 创建日期: 2023年08月31日
* 注    意:
*********************************************************************************************************/
static void StringReplaceChar(char* string, char cold, char cnew)
{
  while (0 != *string)
  {
    if (*string == cold)
    {
      *string = cnew;
    }
    string++;
  }
}

字符串查找:

/*********************************************************************************************************
* 函数名称: FindString
* 函数功能: 查找字符串
* 输入参数: source:源字符串首地址,即可能含有目的字符串的字符串首地址
*           target:目的字符串首地址
* 输出参数: void
* 返 回 值: 目的字符串距离源字符串首地址偏移量,0xFFFFFFFF 表示查找失败
* 创建日期: 2023年09月04日
* 注    意: 
*********************************************************************************************************/
#define STRING_NODE_INVALID 0xFFFFFFFF
static unsigned int FindString(char* source, char* target)
{
  unsigned int i, j, k, ok;
  
  i = 0; j = 0; k = 0; ok = 0;
  while(0 != source[i])
  {
    //检测到目的字符串的开头
    if(source[i] == target[0])
    {
      j = i;
      k = 0;
      while((0 != source[j]) && (0 != target[k]))
      {
        if(source[j] == target[k])
        {
          j++; k++;
        }
        else
        {
          break;
        }
      }
      
      //成功查找到目的字符串
      if(0 == target[k])
      {
        ok = 1;
        break;
      }
    }
    i++;
  }
  
  //成功查找带字符串
  if(ok)
  {
    return i;
  }
  else
  {
    return STRING_NODE_INVALID;
  }
}

打印函数封装:

#include "stdio.h"
#include "stdarg.h"

/*********************************************************************************************************
* 函数名称: PrintString
* 函数功能: 字符串打印函数
* 输入参数: fmt:字符串数据
* 输出参数: void
* 返 回 值: void
* 创建日期: 2023年08月19日
* 注    意: 
*********************************************************************************************************/
static void PrintString(char* fmt, ...)
{
  //字符串转换缓冲区
  static char s_arrStringBuf[1024];

  //字符串长度
  unsigned int len;

  //定义一个 va_list 类型的变量,用来存储单个参数
  va_list args;

  //为空,直接返回
  if(NULL == fmt)
  {
    return;
  }

  //使 args 执行可变参数的第一个参数
  va_start(args, fmt);

  //字符串转换
  vsprintf(s_arrStringBuf, fmt, args);

  //统计字符串长度
  len = 0;
  while(0 != s_arrStringBuf[len]){ len++; }

  //输出到串口
  WriteUART0((void*)s_arrStringBuf, len);

  //结束可变参数的获取
  va_end(args);
}

获取字符串输入:

char string[100];
printf("input a file name:");
scanf("%s", string);

字符串、数字转换

10 进制字符串转整型,代码如下。

/*********************************************************************************************************
* 函数名称: StringToInt
* 函数功能: 10 进制字符串转整型
* 输入参数: string:字符串形式的数字
* 输出参数: void
* 返 回 值: 转换结果
* 创建日期: 2023年02月10日
* 注    意: 不能含有数字、正负号等其它符号
*********************************************************************************************************/
static int StringToInt(char* string)
{
  int sum;
  int negative;
  unsigned int i;
  sum = 0;
  i = 0;
  negative = 0;
  while(('+' == string[i]) || ('-' == string[i]) || ((string[i] >= '0') && (string[i] <= '9')))
  {
    //正号
    if ('+' == string[i])
    {
      negative = 0;
    }

    //负号
    else if ('-' == string[i])
    {
      negative = 1;
    }

    //空格
    else if (' ' == string[i])
    {
      //不做处理
    }

    //数字
    else if ((string[i] >= '0') && (string[i] <= '9'))
    {
      sum = (sum * 10) + (string[i] - '0');
    }

    //循环变量加一
    i++;
  }

  //负号处理
  if (0 != negative)
  {
    sum = -sum;
  }

  //返回转换结果
  return sum;
}

字符串转浮点数,代码如下。

/*********************************************************************************************************
* 函数名称: StringToDouble
* 函数功能: 字符串转浮点数
* 输入参数: void
* 输出参数: void
* 返 回 值: void
* 创建日期: 2023年07月13日
* 注    意: 除数字、正负号、小数点外,不得带有其它符号
*********************************************************************************************************/
static double StringToDouble(char* string)
{
  double sum, devision;
  int negative, dotFlag;
  unsigned int i;
  sum = 0;
  negative = 0;
  i = 0;
  devision = 1;
  dotFlag = 0;
  while(('+' == string[i]) || ('-' == string[i]) || ('.' == string[i]) || ((string[i] >= '0') && (string[i] <= '9')))
  {
    //正号
    if ('+' == string[i])
    {
      negative = 0;
    }

    //负号
    else if ('-' == string[i])
    {
      negative = 1;
    }

    //小数点
    else if ('.' == string[i])
    {
      dotFlag = 1;
    }

    //空格,不处理
    else if (' ' == string[i])
    {

    }

    //数字
    else if ((string[i] >= '0') && (string[i] <= '9'))
    {
      sum = (sum * 10.0) + (string[i] - '0');
      if (0 != dotFlag)
      {
        devision = devision * 10;
      }
    }

    //循环变量加一
    i++;
  }

  //负号处理
  if (0 != negative)
  {
    sum = -sum;
  }

  //小数点处理
  sum = sum / devision;

  //返回转换结果
  return sum;
}

数值转字符串如下所示。

/*********************************************************************************************************
* 模块名称:SerialString.c
* 摘    要:字符串处理模块
* 当前版本:1.0.0
* 作    者:Leyutek(COPYRIGHT 2018 - 2021 Leyutek. All rights reserved.)
* 完成日期:2021年07月01日
* 内    容:
* 注    意:Bootloarder专用,不要使用printf或sprintf等C语言官方库函数,
*          否则编译器会将C语言官方库编入Bootloarder中,使得Bootloarder程序占用空间大
**********************************************************************************************************
* 取代版本:
* 作    者:
* 完成日期:
* 修改内容:
* 修改文件:
*********************************************************************************************************/

/*********************************************************************************************************
*                                              包含头文件
*********************************************************************************************************/
#include "SerialString.h"
#include "UART0.h"

/*********************************************************************************************************
*                                              宏定义
*********************************************************************************************************/

#define MAX_STRING_CONVER_LEN 64 //字符串转换最大长度

/*********************************************************************************************************
*                                              枚举结构体定义
*********************************************************************************************************/

/*********************************************************************************************************
*                                              内部变量
*********************************************************************************************************/
//数值-ASCII码转换表
static const char s_arrNumTable[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};

//字符串转换缓冲区
static char s_arrStringBuf[MAX_STRING_CONVER_LEN];

/*********************************************************************************************************
*                                              内部函数声明
*********************************************************************************************************/

/*********************************************************************************************************
*                                              内部函数实现
*********************************************************************************************************/

/*********************************************************************************************************
*                                              API函数实现
*********************************************************************************************************/
/*********************************************************************************************************
* 函数名称:PutString
* 函数功能:串口输出一个字符串
* 输入参数:string:字符串
* 输出参数:void
* 返 回 值:void
* 创建日期:2021年07月01日
* 注    意:
*********************************************************************************************************/
void PutString(char* string)
{
  u32 len; //字符串长度

  //统计字符串长度
  len = 0;
  while(0 != string[len])
  {
    len++;
  }

  WriteUART0((unsigned char*)string, len);
}

/*********************************************************************************************************
* 函数名称:PutUint
* 函数功能:串口输出一个10进制无符号整型
* 输入参数:num:显示数值,width:显示宽度,不足补零
* 输出参数:void
* 返 回 值:void
* 创建日期:2021年07月01日
* 注    意:
*********************************************************************************************************/
void PutDecUint(u32 num, u32 width)
{
  u32 divisor;      //除数1、10、100、1000...
  u32 widthDivisor; //显示完整宽度需要的除数大小
  u32 needDivisor;  //显示完整数字需要的除数大小
  u32 stringCnt;    //字符串计数
  u32 digital;      //单个位数据

  //计算显示位宽下需要的除数大小
  widthDivisor = 1;
  while(width)
  {
    width--;
    widthDivisor = widthDivisor * 10;
  }
  widthDivisor = widthDivisor / 10;

  //计算显示完整数字需要的除数大小
  needDivisor = 1;
  while(num >= needDivisor)
  {
    needDivisor = needDivisor * 10;
  }
  needDivisor = needDivisor / 10;

  //确定除数初值
  if(widthDivisor > needDivisor)
  {
    divisor = widthDivisor;
  }
  else
  {
    divisor = needDivisor;
  }

  //字符串转换
  stringCnt = 0;
  while(1)
  {
    //已经到个位了
    if(1 == divisor)
    {
      digital = num;
      s_arrStringBuf[stringCnt++] = s_arrNumTable[digital];
      break;
    }
    else
    {
      digital = num / divisor; //取出高位
      num     = num % divisor; //将高位剔除
      divisor = divisor / 10;  //更新除数
      s_arrStringBuf[stringCnt++] = s_arrNumTable[digital]; //数值转ASCII码
    }
  }
  s_arrStringBuf[stringCnt] = 0;

  //输出
  PutString(s_arrStringBuf);
}

/*********************************************************************************************************
* 函数名称:PutDouble
* 函数功能:串口输出一个浮点数
* 输入参数:num:显示数值,pointNum:小数点位数
* 输出参数:void
* 返 回 值:void
* 创建日期:2021年07月01日
* 注    意:
*********************************************************************************************************/
void PutDouble(double num, u32 pointNum)
{
  u32 integer, decimal; //整数、小数转换结果
  u32 divisor;          //除数1、10、100、1000...

  //确定除数
  divisor = 1;
  while(pointNum)
  {
    pointNum--;
    divisor = divisor * 10;
  }

  //得到整数部分
  integer = (u32)num;

  //得到小数部分
  num = num * divisor;
  decimal = ((u32)num) % divisor;

  //显示
  PutDecUint(integer, 1); //整数部分
  PutString(".");         //小数点
  PutDecUint(decimal, 1); //小数部分
}

读写文件

C 语言创建并写入二进制文件。

#include <stdio.h>

//创建
FILE* bin = fopen("name.bin", "wb");

//写入
fwrite(buf, xxx, xxx, bin);

//保存
fclose(bin);

C 语言创建并写入 csv 表格文件,csv 文件以 “,” 为数据间的分隔符,以 “\n” 为行切换。

#include <stdio.h>

//创建
FILE* file = fopen("name.csv", "w");

//写入
fprintf(file, "%d,", data);

//保存
fclose(file);

获取用户输入的文件名的代码如下所示。

#include <stdio.h>

/*********************************************************************************************************
* 函数名称: GetFileName
* 函数功能: 获取文件名
* 输入参数: name:名字缓冲区
* 输出参数: void
* 返 回 值: void
* 创建日期: 2023年07月13日
* 注    意:
*********************************************************************************************************/
static void GetFileName(char* name)
{
  printf("Please input file name: ");
  scanf("%s", name);
}

以文本文件形式将整个文件读入到内存中,代码如下所示

#include <stdio.h>
#include <malloc.h>

/*********************************************************************************************************
* 函数名称: ReadSourceFileToMemoryByChar
* 函数功能: 以文本文件形式将整个文件读入到内存中
* 输入参数: path:文件路径
*            size:用于输出文件大小
* 输出参数: void
* 返 回 值: 内存首地址
* 创建日期: 2023年02月10日
* 注    意: 分配内存失败、打开文件失败会直接卡死
*********************************************************************************************************/
static char* ReadSourceFileToMemoryByChar(char* path, unsigned int* size)
{
  FILE* sourceFile;
  char* buf;

  //打开文件(以文本的形式)
  sourceFile = fopen(path, "r");
  if (NULL == sourceFile)
  {
    printf("ReadSourceFileToMemoryByChar: Fail to open source file: %s\r\n", path);
    while (1) {}
  }

  //统计文件长度
  fseek(sourceFile, 0, SEEK_END);
  *size = ftell(sourceFile);

  //申请动态内存
  buf = malloc(*size);
  if (NULL == buf)
  {
    printf("ReadSourceFileToMemoryByChar: Fail to malloc for source file\r\n");
    while (1) {}
  }

  //跳转到文件开头
  fseek(sourceFile, 0, SEEK_SET);

  //读入整个文件
  fread(buf, *size, 1, sourceFile);

  //关闭文件
  fclose(sourceFile);

  //返回内存首地址
  return buf;
}

以二进制文件形式将整个文件读入到内存中,代码如下所示。

#include <stdio.h>
#include <malloc.h>

/*********************************************************************************************************
* 函数名称: ReadSourceFileToMemoryByByte
* 函数功能: 以二进制文件形式将整个文件读入到内存中
* 输入参数: path:文件路径
*            size:用于输出文件大小
* 输出参数: void
* 返 回 值: 内存首地址
* 创建日期: 2023年02月10日
* 注    意: 分配内存失败、打开文件失败会直接卡死
*********************************************************************************************************/
static char* ReadSourceFileToMemoryByByte(char* path, unsigned int* size)
{
  FILE* sourceFile;
  char* buf;

  //打开文件(以文本的形式)
  sourceFile = fopen(path, "rb");
  if (NULL == sourceFile)
  {
    printf("ReadSourceFileToMemoryByByte: Fail to open source file: %s\r\n", path);
    while (1) {}
  }

  //统计文件长度
  fseek(sourceFile, 0, SEEK_END);
  *size = ftell(sourceFile);

  //申请动态内存
  buf = malloc(*size);
  if (NULL == buf)
  {
    printf("ReadSourceFileToMemoryByByte: Fail to malloc for source file\r\n");
    while (1) {}
  }

  //跳转到文件开头
  fseek(sourceFile, 0, SEEK_SET);

  //读入整个文件
  fread(buf, *size, 1, sourceFile);

  //关闭文件
  fclose(sourceFile);

  //返回内存首地址
  return buf;
}

Windows 下路径不存在则创建路径:

static void CheckDir(char* dir)
{
  if(0 != _access(dir, 0))
  {
    _mkdir(dir);
  }
}