C语言知识

C语言知识:

  1. \x是转义字符,告诉编译器需要用特殊的方式进行处理。\x表示后面的字符是十六进制数,\0表示后面的字符是八进制数。例如十进制的17用十六进制表示就是‘\x11’,用八进制表示就是‘\021’。
  2. 关于预编译语句
#define  AAA
#ifdef AAA
程序段;
#endif

#if (表达式)
程序段;
#elif(表达式)
程序段;
...
#else
程序段;
#endif

#define MAX 5
#undef MAX //取消#define MAX 5
  1. memcpy()函数用于:复制内存块
    函数声明:void * memcpy ( void * destination, const void * source, size_t num );
    参数
    目的地:指向要在其中复制内容的目标数组的指针,类型转换为 void* 类型的指针。
    源:指向要复制的数据源的指针,类型转换为 const void* 类型的指针。
    数字:要复制的字节数。 size_t 是无符号整数类型。
    memmove用于拷贝字节,如果目标区域和源区域有重叠的话,memmove能够保证源串在被覆之前将重叠区域的字节拷贝到目标区域中,但复制后源内容会被更改。但是当目标区域与源区域没有重叠则和memcpy函数功能相同。
    memset()的作用是在一段内存段中填充固定的值。

  2. sizeof:
    是C/C++中的一个操作符(operator),简单的说其作用就是返回一个对象或者类型所占的内存字节数。
    int i;
    sizeof(i);//ok
    sizeof i;//ok
    sizeof(int);//ok
    sizeof int;//error
    sizeof也可以对一个函数调用求值,其结果是函数返回类型的大小,函数并不会被调用,我们来看一个完整的例子:
    char foo()
    {
    printf(“foo()hasbeencalled.\n”);
    return ‘a’;
    }
    int main()
    {
    size_tsz=sizeof(foo());
    //foo()的返回值类型为char,所以sz=sizeof(char),foo()并不会被调用
    printf(“sizeof(foo())=%d\n”,sz);
    }
    指针变量的sizeof,学过数据结构的你应该知道指针是一个很重要的概念,它记录了另一个对象的地址。在32位计算机中,一个指针变量的返回值通常是4(注意结果是以字节为单位),在64位系统中指针变量的sizeof通常为8。
    charpc=“abc”;
    int
    pi;
    string*ps;
    char**ppc=&pc;
    void(pf)();//函数指针
    sizeof(pc);//结果为4
    sizeof(pi);//结果为4
    sizeof(ps);//结果为4
    sizeof(ppc);//结果为4
    sizeof(pf);//结果为4
    数组的sizeof,数组的sizeof值等于数组所占用的内存字节数,如:
    char a1[] = “abc”;
    int a2[3];
    sizeof(a1);
    // 结果为4,字符末尾还存在一个NULL终止符 sizeof( a2 ); // 结果为3
    4=12(依赖于int)
    一些朋友刚开始时把sizeof当作了求数组元素的个数,如今你应该知道这是不对的,那么应该怎么求数组元素的个数呢Easy,通常有下面两种写法:
    int c1=sizeof(a1)/sizeof(char);//总长度/单个元素的长度 char型
    int c2=sizeof(a2)/sizeof(a2[0]);//总长度/第一个元素的长度 int型

  3. strlen()函数用法

  4. 主要用于计算字符串的长度

  5. strlen()从字符串的开头位置依次往后面计数,直到遇到‘\0’停止,所计算的字符串大小为‘\0’以前的字符所计算的值,最终的字符串长度不包括‘\0’。

  6. 有许多初学者可能会混淆sizeof()和strlen()两个的用法,对于同一个字符串来说sizeof()计算的字符串会包含‘\0’,而strlen()不会包含,因此用sizeof计算的字符串大小会多一个字节。

  7. 关于大小端的判断
    /* 功能:大小端判断 大端—高字节数据存储在低地址 小端—低字节数据存储在低地址
    返回: 1小端 0大端 */
    int Endian()
    {
    int a = 1;
    int p = &a;
    return (
    (char *)p == 1);
    }

bool isNetByteOrder()
{
union test {
int i;
char c;
};
test t;
t.i = 1;
// 如果是大端,则 t.c 为0x00,则 t.c != 1 返回true
// 否则返回false
return (t.c != 1);
}

//或者
bool isNetByteOrder()
{
union {
int i;
char c;
}t;
t.i = 1;
// 如果是大端,则 t.c 为0x00,则 t.c != 1 返回true
// 否则返回false
return (t.c != 1);
}
7. continue 的用法详解:
continue 在while中的用法:退出本次的循环
//continue 在while中的用法
#include<stdio.h>
int main()
{
int i = 1;
while (i<=10)
{
if (i == 5)
{
++i;
continue;
}
printf(“%d “, i);
++i;
}
printf(”\n”);
return 0;
}

[图片]
continue 在剔除多余元素的用法
//以下的代码作用是过滤除了0-9的东西
#include <stdio.h>
int main()
{
int ch = 0;
while ((ch = getchar()) != EOF)
{
if (ch < ‘0’ || ch > ‘9’)
{
continue;
}
putchar(ch);
}
return 0;
}

  1. 整数0强转成指针类型的巧妙操作:
    在C语言中指针是一个整数相当于地址编号,如果给一个整数a强转成指针可以理解为该指针地址编号为a。下面就介绍下该方法应用在结构体中的巧妙之处。
    typedef struct{
    int a;
    char b;
    }Msg;
    如上所示结构体Msg,如果将一个整数0强转成Msg可以理解为,Msg所在的首地址为0,那么((Msg *)0)->b,就可以理解为取结构体成员b的内容,但是我们自己知道其实0这个首地址并没有存储结构体Msg的信息,取内容也就毫无意义,所以我们不要取内容。我们看看对这个成员取地址(引用)会有什么事情发生,&(((Msg *)0)->b)就相当于结构体Msg的成员b的首地址。我们也可以换一种思维理解,其实&(((Msg *)0)->b)就是b相对于结构体首地址的偏移量,因为结构体首地址为0,偏移后的地址减去首地址就是偏移量,偏移后的地址-0=偏移量,那么偏移量就等于偏移后的地址,那么如果在对结构体成员做偏移处理的时候看着就非常清晰,而且后期维护添加成员使用这个方法源码不用做任何改动,因为无论增加多少成员偏移量都是动态的并不是用常量写死的。
    原文链接:https://blog.csdn.net/qq_33865609/article/details/117482961

  2. C语言NULL和"/0"区别:
    在C语言中,我们有时候看到NULL,有时候也看到’\0’,那它们之间有什么区别呢?
    本质来说,NULL,0,'\0’都是一样的,都是值0。是的,你没有听错。说到这本文差不多应该结束了。不过为了不被打,还是继续说一说。它们虽然值都是0,但是含义却是不一样的。
    虽然值是0,但是它的含义不一样,或者说它的类型不一样。NULL是指针类型,不过它是空指针,即值为0。
    //来源:公众号【编程珠玑】 博客:https://www.yanbinghu.com
    //null.c
    #include<stdio.h>
    int main(void){int a = NULL;printf(“%p\n”,a);return 0;}
    我们编译它:
    $ gcc -o null null.c
    null.c: In function ‘main’:
    null.c:14:10: warning: initialization makes integer from pointer without a cast [-Wint-conversion]
    int a = NULL;
    ^
    它给了我们一个警告,提示尝试将指针转换为整数。这也就正验证了我们前面的说法。
    实际上NULL通常是如下定义:
    #define NULL (void*)0
    所以,如果要给一个指针类型初始化,那么你给它一个NULL,使得能够明显的看到这是一个指正。
    当然,在C++中,你更应该使用nullptr,而不是NULL。

我们都知道\是转义符,用单引号包起来,再加转义,实际上就是0,只不过它表示的是字符。就向下面这样:
//来源:公众号【编程珠玑】 博客:https://www.yanbinghu.com
//nul.c
#include<stdio.h>
int main(void){char a = ‘\0’;char b = ‘0’;printf(“a = %d,b = %d\n”,a,b);return 0;}
编译运行:
$ gcc -o nul nul.c
./nul
a = 0,b = 48
我们最常见到的就是它作为字符串的结束符。所以我们常常会看到下面这样的写法:
char str[16];
/do something/
str[15] = ‘\0’;
还记得printf是如何打印字符串,以及strcmp比较停止规则吗?是的,它们都以遇到’\0’结束。
注意,它和’0’完全不一样。通过打印就可以看到了,实际上’\0’的值就是0。
需要特别注意的是,如果’\0’的0后面跟八进制的数,则会被转义。所以’\60’与’0’的值一致。
0:
这个不用多解释。
int a = 0;
“0”:
用双引号包裹的0是字符串,我们看不到的是它结尾还有一个’\0‘
#include<stdio.h>
int main(void){char str[] = “0”;printf(“sizeof str is %d,string len is %d\n”,sizeof(str),strlen(str));return 0;}
运行结果:
sizeof str is 2,string len is 1
“\0”:
这也是字符串,只不过是两个空字符。使用strlen计算字符串长度为0。
" ":
字符串。字符串长度为1,占用空间2字节,是一个空格加空字符。
总结:
到这里你应该明白了,它们的值可能一样,但赋予的含义却不一样,为了代码良好的可读性,你应该在恰当的时候使用合适的值。

  1. C语言结构体的知识:
  2. 定义结构体:
    struct 结构体名{
    结构体所包含的变量或数组
    };
    struct stu{
    char name; //姓名
    int num; //学号
    int age; //年龄
    char group; //所在学习小组
    float score; //成绩
    };
    /

    stu 为结构体名,它包含了 5 个成员,分别是
    name、num、age、group、score。
    结构体成员的定义方式与变量和数组的定义方式相同,只是不能初始化。
    注意大括号后面的分号 ;不能少,这是一条完整的语句。
    */

//定义机构体变量stu1, stu2
struct stu stu1, stu2;

//可以在定义结构体的同时定义结构体变量
struct stu{
char *name; //姓名
int num; //学号
int age; //年龄
char group; //所在学习小组
float score; //成绩
} stu1, stu2;

/如果只需要 stu1、stu2 两个变量,
后面不需要再使用结构体名定义其他变量,那么在定义时也可以不给出结构体名,
如下所示:
/
struct{ //没有写 stu
char *name; //姓名
int num; //学号
int age; //年龄
char group; //所在学习小组
float score; //成绩
} stu1, stu2;

//可以在定义时整体赋值
struct{
char *name; //姓名
int num; //学号
int age; //年龄
char group; //所在小组
float score; //成绩
} stu1, stu2 = { “Tom”, 12, 18, ‘A’, 136.5 };

//可以用typedef 重新定义结构体变量
typedef struct tagPoint
{
double x;
double y;
double z;
} Point;

//可以用typedef 重新定义结构体变量
typedef struct
{
double x;
double y;
double z;
} Point;

//结构体与指针

include <stdio.h>

include <string.h>

struct AGE
{
int year;
int month;
int day;
};
struct STUDENT
{
char name[20]; //姓名
int num; //学号
struct AGE birthday; //生日
float score; //分数
};
int main(void)
{
struct STUDENT student1; /用struct STUDENT结构体类型定义结构体变量student1/
struct STUDENT *p = NULL; /定义一个指向struct STUDENT结构体类型的指针变量p/
p = &student1; /p指向结构体变量student1的首地址, 即第一个成员的地址/
strcpy((*p).name, “小明”); //(*p).name等价于student1.name
(*p).birthday.year = 1989;
(*p).birthday.month = 3;
(*p).birthday.day = 29;
(*p).num = 1207041;
(*p).score = 100;
printf(“name : %s\n”, (*p).name); //(*p).name不能写成p
printf(“birthday : %d-%d-%d\n”, (*p).birthday.year, (*p).birthday.month, (*p).birthday.day);
printf(“num : %d\n”, (*p).num);
printf(“score : %.1f\n”, (p).score);
return 0;
}
/

注意,p 两边的括号不可省略,因为成员运算符“.”的优先级高于指针运算符“”,
所以如果 *p 两边的括号省略的话,那么 p.num 就等价于 (p.num) 了。
从该程序也可以看出:因为指针变量 p 指向的是结构体变量 student1 第一个成员的地址,
即字符数组 name 的首地址,所以 p 和 (p).name 是等价的。
但是,“等价”仅仅是说它们表示的是同一个内存单元的地址,但它们的类型是不同的。
指针变量 p 是 struct STUDENT
型的,而 (p).name 是 char 型的。
所以在 strcpy 中不能将 (p).name 改成 p。
用 %s 进行输入或输出时,输入参数或输出参数也只能写成 (p).name 而不能写成 p。
同样,虽然 &student1 和 student1.name 表示的是同一个内存单元的地址,但它们的类型是不同的。
&student1 是 struct STUDENT
型的,而 student1.name 是 char
型的,
所以在对 p 进行初始化时,“p=&student1;”不能写成“p=student1.name”。
因为 p 是 struct STUDENT
型的,所以不能将 char
型的 student1.name 赋给 p。
*/
int main(void)
{
struct STUDENT student1; /用struct STUDENT结构体类型定义结构体变量student1/
struct STUDENT p = NULL; /定义struct STUDENT结构体类型的指针变量p/
p = &student1; /p指向结构体变量student1的首地址, 即第一项的地址/
strcpy(p->name, “小明”);
p->birthday.year = 1989;
p->birthday.month = 3;
p->birthday.day = 29;
p->num = 1207041;
p->score = 100;
printf(“name : %s\n”, p->name); //p->name不能写成p
printf(“birthday : %d-%d-%d\n”, p->birthday.year, p->birthday.month, p->birthday.day);
printf(“num : %d\n”, p->num);
printf(“score : %.1f\n”, p->score);
return 0;
}
/

但是要注意的是,只有“指针变量名”后面才能加“->”,千万不要在成员名如 birthday 后面加“->”。
综上所述,以下 3 种形式是等价的:
=结构体变量.成员名。
=(*指针变量).成员名。
=指针变量->成员名。
*/

//结构体数组

include <stdio.h>

struct STU
{
char name[20];
int age;
char sex;
char num[20];
};
int main(void)
{
struct STU stu[5] = {{“小红”, 22, ‘F’, “Z1207031”}, {“小明”, 21, ‘M’, “Z1207035”}, {“小七”, 23, ‘F’, “Z1207022”}};
struct STU p = stu;
return 0;
}
/

此时指针变量 p 就指向了结构体数组的第一个元素,即指向 stu[0]。
我们知道,当一个指针指向一个数组后,指针就可以通过移动的方式指向数组的其他元素。
这个原则对结构体数组和结构体指针同样适用,
所以 p+1 就指向 stu[1] 的首地址;p+2 就指向 stu[2] 的首地址……所以只要利用 for 循环,
指针就能一个个地指向结构体数组元素。
同样需要注意的是,要将一个结构体数组名赋给一个结构体指针变量,那么它们的结构体类型必须相同。
*/

include <stdio.h>

struct STU
{
char name[20];
int age;
char sex;
char num[20];
};
int main(void)
{
struct STU stu[3] = {{“小红”, 22, ‘F’, “Z1207031”}, {“小明”, 21, ‘M’, “Z1207035”}, {“小七”, 23, ‘F’, “Z1207022”}};
struct STU *p = stu;
for (; p<stu+3; ++p)
{
printf(“name:%s; age:%d; sex:%c; num:%s\n”, p->name, p->age, p->sex, p->num);
}
return 0;
}
//都等价

include <stdio.h>

struct STU
{
char name[20];
int age;
char sex;
char num[20];
};
int main(void)
{
struct STU stu[3] = {{“小红”, 22, ‘F’, “Z1207031”}, {“小明”, 21, ‘M’, “Z1207035”}, {“小七”, 23, ‘F’, “Z1207022”}};
struct STU *p = stu;
int i = 0;
for (; i<3; ++i)
{
printf(“name:%s; age:%d; sex:%c; num:%s\n”, p[i].name, p[i].age, p[i].sex, p[i].num);
}
return 0;
}
11. 枚举的定义和使用:
#include <stdio.h>
void main() {

    enum DAY
    {
            MON=1, TUE=2, WED=3, THU=4, FRI=5, SAT=6, SUN=7
    }; // 这里DAY 就是枚举类型, 包含了7个枚举元素 
    enum DAY day; // enum DAY  是枚举类型, day 就是枚举变量 
    day = WED; //给枚举变量  day 赋值,值就是某个枚举元素
    printf("%d",day);// 3 , 每个枚举元素对应一个值
    getchar();

}
12. 关于99乘法表输出对齐
int main()
{
char i,j;
for(i=1;i<10;i++)
{
for(j=1;j<=i;j++)
{
printf(“%d%d=%-4d”,i,j,i
j);//“%-4d"表示左对齐,空4个
//printf(”%d%d=%d\t",i,j,i*j);//“\t"表示tab空格
}
printf(”\n");
}
return 0;

}

单片机知识:

  1. 关于keil生成lib的需要注意一下几点:
    [图片]

  2. 在keil中勾选crate lib。

  3. 编译的文件应该全是接口文件,不应该包括主函数。

  4. 选择debug information 可以生成一个可以仿真的库。在源文件中可以仿真。

  5. 关于中断的设置
    void NVIC_Configuration(void)
    {
    NVIC_InitTypeDef NVIC_InitStructure;

     //设置中断优先级分组,目的是分抢占优先级和响应优先级的个数
     NVIC_SetPriorityGrouping(NVIC_PriorityGroup_3);
     
     //开启中断源
     NVIC_InitStructure.NVIC_IRQChannel = RTC_IRQn;
     //设置抢占优先级
     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 7;
     //设置响应优先级
     NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
     
     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
     NVIC_Init(&NVIC_InitStructure);
    

}

Makefile 添加宏定义
在Makefile 中添加宏定义可以用来控制源码的编译,通过CFLAGS中的选项-D定义即可,如:
CFLAGS += -DG_DEBUG
上述即添加了向源码添加了宏定义:G_DEBUG。源码中可通过该宏定义来进行debug信息的控制,如:
#ifdef G_DEBUG
printf(“debug on…\n”);
#endif

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值