1.奇怪的知识又增加了
1. if(!a- -) / *p- -
- (!a–)和(!–a)
两者都是倒计数用,前者先判断再减一,后者先减一再判断
int a = 1;
if(!--a) printf("Hello, World! \n"); //打印Hello, World!
int a = 1;
if(!a--) printf("Hello, World! \n"); //不打印
- *p++
由于++和同等优先级,结合方向为自右向左,因此它等价与(p++)。先引用p的值,实现*p的运算,然后再使p自增1。
*(P–):先对p进行“*”运算,再使p自减。
*(–P):先使p自减,再进行“*”运算。
2.多个.c引用一个.h(STM32打码经常用到)
头件的中的#ifndef,这是一个很关键的东西。比如你有两个C文件,这两个C文件都include了同一个头文件。而编译时,这两个C文件要一同编译成一个可运行文件,于是问题来了,大量的声明冲突。
还是把头文件的内容都放在#ifndef和#endif中吧。不管你的头文件会不会被多个文件引用,你都要加上这个。一般格式是这样的:
#ifndef <标识>
#define <标识>
…
…
#endif
<标识>在理论上来说可以是自由命名的,但每个头文件的这个“标识”都应该是唯一的。标识的命名规则一般是头文件名全大写,前后加下划线,并把文件名中的“.”也变成下划线,如:stdio.h
#ifndef STDIO_H
#define STDIO_H
…
#endif
例如STM32跑马灯实验中led.h文件:
#ifndef __LED_H
#define __LED_H
#include "sys.h"
//LED 端口定义
#define LED0 PBout(5)// DS0
#define LED1 PEout(5)// DS1
void LED_Init(void);//初始化
#endif
3.函数内嵌 / 嵌套函数
- 函数内嵌类似函数内联,函数嵌套是函数实现在函数体内部,只有父函数能够调用
嵌套函数不能在外部声明 - 可以将嵌套函数的地址传递给其他函数,并由其他函数调用,就好像可以传递其他局部变量的地址
- 嵌套函数和父函数可访问同样地变量,但只能访问比嵌套函数声明早的局部变量
- 嵌套函数可以使用goto语句跳转到函数之外的某个标号位置,该位置应该位于父函数内部
- 通过将其声明为auto,即可声明嵌套函数的原型
void right() {
auto double hypotenuse();
double a = 3.0;
double b = 4.0;
double hypotenuse(double x, double y) {
return (sqrt(x * x + y * y));
}
printf("Long side of %lf and %lf is %lf\n", a, b, hypotenuse(a, b));
}
4.【内存规划】c语言五大内存分区 & 三大段
三大段和五大内存分区之间区别是:代码段,数据段,堆栈段是cpu级别的概念,五大分区属于语言级别的概念,两者是不同的概念。
对于多线程并发不可重入函数不能使用全局/静态变量,因为每个线程有自己的栈放置局部变量,但要共享进程中数据段的全局变量,因此并发时会导致全局变量被篡改。
该图从上到下地址由小到大
- C语言五大区:
1.栈区(stack):存放函数形参和局部变量(auto类型),由编译器自动分配和释放。
2.堆区(heap):该区由程序员申请后使用,需要手动释放否则会造成内存泄漏。如果程序员没有手动释放,那么程序结束时可能由OS回收。
3.全局/静态存储区:存放全局变量和静态变量(包括静态全局变量与静态局部变量),初始化的全局变量和静态局部变量放在一块,未初始化的放在另一块。
4.字符常量区:常量在统一运行被创建,常量区的内存是只读的,程序结束后由系统释放。
5.程序代码区:存放程序的二进制代码,内存由系统管理
- 程序局部变量存在于(栈区)| 全局变量存在于(静态区)| 动态申请数据存在于(堆区);
(静态区是编译时分配的,栈区在程序运行时分配)- 局部变量可和全局变量重名,函数内会优先使用局部变量;
- 程序的三个基本段:
text段在内存中被映射为只读,但date段与bss段是可写的
1.text段:代码段,就是放程序代码的,编译时确定,只读
2.date段:存放在编译阶段(而非运行时)就能确定的数据,可读可写。也就是通常所说的静态存储区,赋了初值的全局变量和赋初值的静态变量存放在这个区域,常量也存在这个区域
3.bss段:已经定义但没赋初值的全局变量和静态变量存放在这个区域。
5.声明和定义
声明:告诉编译器变量的类型 在哪里 或者函数的特征(返回值 参数类型 个数)
定义:告诉编译器 在此处分配存储空间 建立变量和函数
同一变量定义只有一个(干嘛要多处分配空间呢 内存很宝贵的~)
但是可以声明多次
N.C编程真理
1. 全局变量、静态全局变量、局部变量未初始化时,编译器将其初始化为0;
- 但静态变量只初始化一次,因此他可以作为计数(函数调用过程保持其原始值不变)变量;
int g;
static int sg;
int main()
{
int l;
printf("g = %d\nsg = %d\nl = %d\n", g, sg, l);
return 0;
}
局部变量:位于栈内存、随着函数被调用(进入栈)而被创建,函数出栈而消失;
全局变量:位于数据区、在C++中随着对象创建而产生,随着对象被回收而消失;
2. (函数调用中)值传递无法修改变量值,应使用地址传递,不管全局变量、静态变量还是局部变量;
int g = 0;
static int sg = 0;
int fun(int a){
a++;
return 0;
}
int fun1(int *a){
(*a)++;
return 0;
}
int main()
{
int l = 0;
fun(g);
fun(sg);
fun(l);
printf("值传递:\n g = %d\nsg = %d\nl = %d\n", g, sg, l);
fun1(&g);
fun1(&sg);
fun1(&l);
printf("地址传递:\n g = %d\nsg = %d\nl = %d\n", g, sg, l);
return 0;
}
3. 递归不是将其中的递归函数简单的一层层展开在程序中,而是和其他函数调用一样,因此切忌值传递;
如果是简单的展开,值传递可以更改变量的值,如果是函数调用,值传递无法更改变量的值;
4. 递归函数每递归一层就将这一层的内容压栈,直到最后一层函数返回则将其弹出;
参考代码随想录