2、C奇怪的知识技巧

1.奇怪的知识又增加了

1. if(!a- -) / *p- -

  1. (!a–)和(!–a)
    两者都是倒计数用,前者先判断再减一,后者先减一再判断
int a = 1;
	if(!--a) printf("Hello, World! \n");   //打印Hello, World!
int a = 1;
	if(!a--) printf("Hello, World! \n");   //不打印
  1. *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.函数内嵌 / 嵌套函数

  1. 函数内嵌类似函数内联,函数嵌套是函数实现在函数体内部,只有父函数能够调用
    嵌套函数
    不能在外部声明
  2. 可以将嵌套函数的地址传递给其他函数,并由其他函数调用,就好像可以传递其他局部变量的地址
  3. 嵌套函数和父函数可访问同样地变量,但只能访问比嵌套函数声明早的局部变量
  4. 嵌套函数可以使用goto语句跳转到函数之外的某个标号位置,该位置应该位于父函数内部
  5. 通过将其声明为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级别的概念五大分区属于语言级别的概念,两者是不同的概念。

对于多线程并发不可重入函数不能使用全局/静态变量,因为每个线程有自己的栈放置局部变量,但要共享进程中数据段的全局变量,因此并发时会导致全局变量被篡改。

该图从上到下地址由小到大

  1. C语言五大区:
    1.栈区(stack):存放函数形参和局部变量(auto类型),由编译器自动分配和释放。
    2.堆区(heap):该区由程序员申请后使用,需要手动释放否则会造成内存泄漏。如果程序员没有手动释放,那么程序结束时可能由OS回收。
    3.全局/静态存储区:存放全局变量和静态变量(包括静态全局变量与静态局部变量),初始化的全局变量和静态局部变量放在一块,未初始化的放在另一块
    4.字符常量区:常量在统一运行被创建,常量区的内存是只读的,程序结束后由系统释放。
    5.程序代码区:存放程序的二进制代码,内存由系统管理
  1. 程序局部变量存在于(栈区)| 全局变量存在于(静态区)| 动态申请数据存在于(堆区);
    (静态区是编译时分配的,栈区在程序运行时分配)
  2. 局部变量可和全局变量重名,函数内会优先使用局部变量;
  1. 程序的三个基本段:

text段在内存中被映射为只读,但date段与bss段是可写的

1.text段:代码段,就是放程序代码的,编译时确定,只读
2.date段:存放在编译阶段(而非运行时)就能确定的数据,可读可写。也就是通常所说的静态存储区,赋了初值的全局变量和赋初值的静态变量存放在这个区域,常量也存在这个区域
3.bss段:已经定义但没赋初值的全局变量和静态变量存放在这个区域。
在这里插入图片描述

5.声明和定义

声明:告诉编译器变量的类型 在哪里 或者函数的特征(返回值 参数类型 个数)
定义:告诉编译器 在此处分配存储空间 建立变量和函数

同一变量定义只有一个(干嘛要多处分配空间呢 内存很宝贵的~)
但是可以声明多次

N.C编程真理

1. 全局变量、静态全局变量、局部变量未初始化时,编译器将其初始化为0;

  1. 但静态变量只初始化一次,因此他可以作为计数(函数调用过程保持其原始值不变)变量;
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. 递归函数每递归一层就将这一层的内容压栈,直到最后一层函数返回则将其弹出;

参考代码随想录
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值