C规范
函数中局部变量定义,最好放开头
语法只要求:在{}前定义局部变量,不用非得把局部变量挤在函数开头。
在函数开头定义:方便修改,跨平台更通用
在函数中定义:方便阅读
有些C语言编译器,不在开头定义局部变量,编译不通过,为了代码更通用跨平台适用、只好牺牲可读性、在函数开头定义局部变量,即使是for循环中的i也要放开头(感觉真的很多此一举,为了通用性,没办法)。
参考:
https://blog.csdn.net/lansedehuhuan/article/details/7040398
C语法
C和C++的数组没有“值传递”只有“址传递”,和Java不同
以下三种参数形式,本质都是指针,函数内部修改会改变原数组。
void f1(int *arr)
{
arr[0]=1;
}
void f2(int arr[10])
{
arr[0]=1;
}
void f3(int arr[])
{
arr[0]=1;
}
参考:
https://www.runoob.com/cprogramming/c-passing-arrays-to-functions.html
警告
switch上的警告
#548-D transfer of control bypasses initialization of:
分析代码中的goto语句后是否声明了变量,如果有,则将变量声明移动至goto前。
switch语句本质上是用goto实现的
,因此同样可能存在上述问题。解决办法除了参考2.把变量声明写在switch之前,还可以在switch的每个分支上加上花括号{},或者把switch语句改成if/else。
查了这个教程,让每个分支加上大括号或者换成if-else,重新编译就没了。
switch (g_usTaskStep)
{
case 0:
{ // case的大括号要包住break;
g_usTaskStep = 1;
break;
}
case 1:
{
g_usTaskStep = 2;
break;
}
default:
{
g_usTaskStep = 0;
break;
}
}
参考:
https://blog.csdn.net/venom_snake/article/details/106784095
.h和.c文件
.h头文件循环引用
为了“简洁”(强迫症),我把a.c中所有需要引用的xx.h都放在了对应头文件a.h中,然后恰好xx.h中需要引用a.h,于是就造成了循环引用问题。
死活找不到,一编译就报错:
折腾好久,真是血的教训!
出现这种问题,很大原因是有一个Globalvar.h文件,集合了所有全局变量,而在分模块SXTest.c中,既有该变量类型的定义、又有该变量的使用,因此,就有了引用:
SXTest.c引用SXTest.h和Globalvar.h
Globalvar.h引用SXTest.h
如果把SXTest.c中的引用全部挪到SXTest.h中,就会造成循环引用:
SXTest.c引用SXTest.h
SXTest.h引用Globalvar.h
Globalvar.h引用SXTest.h
所以,Globalvar.h这种文件,仅仅是为了人好检查而存在,却导致所有模块都与其有互引关系,其合理性有待商榷。
经典redefined错误:定义放到了.h里,extern放到了.c里!
再次出现,非常经典的引用错误:
error #10056: symbol "_pEUartCfg_Set_Data" redefined: first defined in "./src/Source/AppSource/GroundTest/SXTest.obj"; redefined in "./src/Source/AppSource/GroundTest/DCCtrl.obj"
找半天才发现,问题在这个变量的定义,我错误地把这个变量的定义放到了.h里,extern放到了.c里,搞反了。
全局变量定义和声明
报错:
error #10056: symbol "_bChannel" redefined: first defined in "./src/Source/schedule.obj"; redefined in "./src/Source/main.obj"
【又犯了一次同样的错误而找不到问题!2024年09月03日 18:06:26】
// 错误:仅在.h中定义并初始化
// .h文件
INT8U g_ucSocketNum = 1;
INT16U g_usTxPort = 8080;
INT8U g_ucChannel = 0;
// .c文件
没有
// 正确:
// (1)全局变量:需要在.h中用extern声明,在.c中定义并初始化,如果是结构体、需要专门写函数初始化。
// .h文件
extern INT8U g_ucSocketNum;
extern INT16U g_usTxPort;
extern INT8U g_ucChannel;
// .c文件
INT8U g_ucSocketNum = 1;
INT16U g_usTxPort = 8080;
INT8U g_ucChannel = 0;
// (2)文件变量:仅仅在.c中使用,只需要在.c中定义并初始化,.h中不用体现。
// .c文件
INT8U ucSocketNum = 1;
INT16U usTxPort = 8080;
INT8U ucChannel = 0;
字节顺序
小端模式符合内存顺序,但是不符合我们书写数字的顺序。
例如,我们写十六进制数 0x0000 0008,左边是高字节、右边是低字节,
而存储地址时,地址的左边是低地址、右边是高地址。
如果按照小端,低地址放低字节位,也就是手写的右边放地址的左边(低字节、低地址),手写的左边放地址的右边(高字节、高地址)。
按照大端,高地址放低字节,也就是手写的右边放地址的右边(低字节、高地址),手写的左边放地址的左边(高字节、低地址)。
赋值
给一个16位的变量赋值,
小端模式下,以下两种方式都可以。
但如果是大端模式,方式2就不对了,就只能一个字接一个字节拼接。
void f(INT8U *pucBuf_o)
{
INT16U frameLen = 3;
//方式1
pucBuf_o[2] = frameLen & 0xff;
pucBuf_o[3] = (frameLen >> 8) & 0xff;
//方式2,直接复制长度为2B的内存
memcpy(&pucBuf_o[2], &frameLen, 2);
}
小端模式,一个INT16U和两个INT8U值不同
我们常用小端模式,需要注意INT16U和两个INT8U,顺序是不一样的!
例如,帧头低位字节是0x5A,帧头高位字节是0x54。
定义:为了不解错,推荐老老实实用定义方式1
,完全按照协议来,虽笨但不用考虑顺序。不推荐定义方式2
,解析时需要头脑清醒,徒增难度。
赋值:推荐赋值方式1-1
,否则需要保持头脑清醒,徒增难度。
//定义方式1:帧头用两个U8表示,值分别是5A和54 【推荐这种】
typedef struct ST422Msg
{
INT8U uiHeadL; // 帧头低位 0 0x5A
INT8U uiHeadH; // 帧头高位 1 0x54
...
} ST422Msg_t;
INT8U g_TxBuf422[MAX422LEN];
//赋值方式1-1:
pucBuf_o[0] = 0x5A;
pucBuf_o[1] = 0x54;
//赋值方式1-2:
INT16U head = 0x545A; //这里是【逆序】,【非常易错,我把这个搞反了】
memcpy(&pucBuf_o[2], &head, 2); //小端模式,可以直接复制内存。大端不行。
//定义方式2:帧头用一个U16表示,值是545A。因为是小端模式,54在左侧是高字节位,5A在右侧是低字节位。
typedef struct ST422Msg
{
INT16U uiHead; // 帧头,值为【0x545A】
...
} ST422Msg_t;
//赋值方式2-1:
pucBuf_o[0] = 0x545A; //这里是【逆序】
//赋值方式2-2:
INT16U head = 0x545A; //这里是【逆序】
memcpy(&pucBuf_o[2], &head, 2); //小端模式,可以直接复制内存。大端不行。
很微妙,只有在具体编程时才能体会到。如果帧头是0x5A5A5A5A,则INT32U和4个INT8U一样,因为4个字节都一样、都是5A,顺序没影响。
指针
1、memcpy的参数
报错:
No source available for "0x100100"
TMS320C6713: Error: Memory Map Error: READ access by CPU to address 0x64010000, which is RESERVED in Hardware.
INT16U SocketRev(INT8U bSocketNum, INT8U *bRxdata, INT32U *dwTxIP, INT16U *wTxPort, INT8U bChannel)
{
INT32S len = 31 + 2 + 256;
//memcpy(&bRxdata, (void *) 0x80000000, len); // 错误 已经是指针了,就不必再取地址了。
memcpy(bRxdata, (void *) 0x80000000, len); // 正确
return 0;
}
2、数组名作为参数传递时,到底带不带&符号?【都可以,为了懒,一般不带】
3、结构体嵌套时,访问成员变量用·
而不是->
编译报错:#45 expression must have pointer type
废了好大劲儿才找到问题所在。
// 错误:pstCfkMsg_o->stCfkMsgData->uiCode
memcpy(&pstCfkMsg_o->stCfkMsgData->uiCode, &pucRxBuf_i[index], byteNum);
// 正确:pstCfkMsg_o->stCfkMsgData.uiCode
// ST_CfkMsgData stCfkMsgData = pstCfkMsg_o->stCfkMsgData; //【不能用局部变量,会导致无法赋值!!!】
// memcpy(&(stCfkMsgData.uiCode), &pucRxBuf_i[index], byteNum); //一波未平一波又起,找了半天才发现错在这里,为了省事儿在函数中搞了个局部变量stCfkMsgData,结果这里的memcpy并没有修改原始的pstCfkMsg_o->stCfkMsgData。真是醉了,还是不要偷懒了。
memcpy(&(pstCfkMsg_o->stCfkMsgData.uiCode), &pucRxBuf_i[index], byteNum);
局部变量和全局变量
定义了全局变量,又重复定义了同名局部变量,导致赋值无效
注意:在变量上移到全局时,不要忘了把局部的定义去掉!
void main(){
INT8U ucCmdH = 0x0; // 指令字的高8位
if (revFlag == BTRUE)
{
ucCmdH = g_stCfkMsgA.stCfkMsgData.ucCfkCmd[1];
// INT8U ucCmdH = g_stCfkMsgA.stCfkMsgData.ucCfkCmd[1]; //【错误代码】这里重复定义了同名局部变量,实际上是在变量上移时、忘了把定义去掉
}
switch (ucCmdH) //错误代码导致,每次到这里,ucCmdH都会清零,气死了,搞了一下午才找到错。
{
case 0x10:
xxx;
break;
case 0x20:
xxx;
break;
}
}
作者:西伯尔
出处:http://www.cnblogs.com/sybil-hxl/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。