1.static的作用
首先,在函数里面定义一个static变量,那么这个变量将和全局变量一样放在静态存储区,在整个函数的执行中,该变量的值不变。也就是它使变量的生命周期变长了。
void test() {
static int a;
printf("%d", a);
a = 1;
printf("%d", a);
}
int main() {
test();
test();
return 0;
}
// 输出 0111
0是因为static声明会将变量初始化为0x00
而第三个1是因为只初始化1次,第二次调用时会使用之前的值
所以这时用途就来了,一般来说,一个函数执行完了,里面的变量也会被销毁,但是static声明的变量它不会随着函数执行完毕就被销毁。
它还有模块化的作用,这个作用在大型程序非常有用
对于static声明的全局变量,只能在本模块使用
对于static声明的函数,只能在本模块使用
在C语言的项目中,模块之间的函数是可以互相引用的,比如下面这样
test.project
test1.c
test2.c
这样test1里面可以使用test2里面的函数,如果重名会怎样,就是说在test1和test2都有一个test函数会怎样
可以看到,重复定义了。而static就可以解决这个问题
2.const
这个关键字很多语言都有,它代表了定义的变量是只读的,不可更改
int main() {
const int a = 1;
a = a + 1;
return 0;
}
错误:赋值给了一个只读变量
这里面可以了解什么是只读指针和指针指向的内容只读
比如这样
void main() {
const int *p; // 指向只读整型的指针 const 修饰整型 意味着整型只读
int const *q; // 指向整型的只读指针 const 修饰指针 意味着指针只读
}
但是前面改变指针p的指向是可以的。但指针q就不行了,它是只读指针,即指针指向不可改变,但指针指向的整型是可以改变的。
我们知道数组名是一个指针,指向数组第一个元素,现在可以进一步确定为只读指针。即是指向不可改变。补充:指向常量字符串的指针char *data = “hello”; 内容不可修改,指针指向可以
void main() {
int a = 11;
int *p = &a;
int arr[3] = {1,2,3};
printf("%d", *arr);
*arr = 10;
printf("%d", arr[0]); // 说明数组名指向数组的第一个元素
arr = p; // 改变指向报错
}
3.使用预处理器#define 定义一个常量(一年多少秒)
#define SECONDS_PER_YEAR (unsigned long)(60*60*24*365)
4.写一个字符串拼接函数 strcat (string.h)
// return:char* 字符指针(字符串)
// param1: char* target 目标字符串
// param2: const char* source 源字符串(const的目的说明不要改变它,因为它是来做贡献的)
// function: 将源字符串拼接到目标字符串,返回目标字符串
char* strcat(char* target, const char* source) {
if(target == NULL || source == NULL) return 0; // 如果为空返回0
char *res = target; // 先记录目标字符串的起始位置
while(*target != '\0') {
target = target + 1; // 移动到目标字符串的尾部
}
while(*source != '\0') {
*target = *source; // 遍历源字符串,赋值给目标字符串
target = target + 1;
source = source + 1;
}
*target = *source; // 要把 '\0'添加上
return res; // 返回目标字符串
}
void main() {
char s1[6] = "hello";
char* s2 = "world"; // s2相当于只读指针
strcat(s1, s2);
printf("%s", s1);
}
5.字符串转整数
int my_atoi(char *str) {
char i = 0;
int res = 0;
// printf("%d", str[4] == '\0');
if(str[i] == '-') {
i = i + 1;
while(str[i] != '\0') {
if((int)str[i] < 49 || (int)str[i] > 57) {
return 0;
}
res = res*10 + ((int)str[i] - 48);
i = i + 1;
}
}else {
while(str[i] != '\0') {
if((int)str[i] < 48 || (int)str[i] > 57) {
return 0;
}
printf("%d", str[i]);
res = res*10 + ((int)str[i] - 48);
i = i + 1;
}
}
return res;
}
void main() {
char s3[5] = "1239";
int res = -1;
res = my_atoi(s3);
printf("%d", res);
}
6.类型强制转换的问题
char ==> int 没事,1字节变2字节 浪费空间而已
int ==> char 有事,精度损失
如果是char数组的某位转int会怎样
7.volatile关键字
它的作用就是让cpu读值的时候,永远都到内存中取,而不是缓存,因为用这个关键字修饰的变量意味着它的值是非常容易改变的。
int a = 1; // 首先内存里面有a的值 然后缓存里面也有a的值
int b = a;
int c = a; // 编译器优化,为了减少读取操作,而且a的值一直都不变,所以cpu直接读缓存的a的值
volatile int a = 1;
int b = a;
int c = a; // 这里编译器不会再优化,因为a是volatile修饰的变量,每次cpu都只会取读a在内存的值
8.整型变量置位
第三位置为1
char a = 3;
a = a | 4; // a |= 4;
9.访问固定的内存地址
int *p;
*p = (int*) 0x62FE17;
printf("%d", *p); //1 假设62FE17位置数据是1
10.整型自动转换
整型有符号数加整型无符号数,有符号数会变为无符号数(前提:int类型)
无符号数是什么
void main() {
unsigned char a = 1; // 这里定义了一个无符号数 它的二进制的最高位没有用来表示正负, 0000 0001
char b = -1; // 这是一个有符号的数,它的最高位用来表示正负, 正:0 负:1 所以它的二进制为 1000 0001
}
从上面看,a和b能表示的数据量是相同的,但范围是不一样的。 unsigned char 类型能表示0-255 共256个数字 (2^0 + 2^1 + … + 2^7) = 2^8 - 1= 255
而char能表示-127-127 (-0 和 +0 一样)
void main() {
char a = -0;
char s[9];
itoa(a, s, 2); // itoa将数字转为二进制字符串 函数原型: char* itoa(int x, char* string, radix)
// 可以传s进去拿结果 或者 返回值接收也行
printf("%s", s);
}
了解了无符号数和有符号数后,来看下面这程序
void main() {
int a = -1000; // 1000 0001
unsigned int b = 2; // 0000 0010
unsigned int c;
c = b + a; // c是一个很大的无符号数
printf("%u", c);
}
11.C语言的栈内存和堆内存
栈:也叫堆栈,像什么局部变量就是存在栈里面的,栈内存由编译器管理,堆内存是由程序员分配释放的,使用malloc和free处理。堆栈使用不恰当就会造成溢出,比如深度递归,不断将函数压栈,导致超出了栈空间大小。如果频繁使用堆,会造成内存碎片,因为数组什么的都是在堆上连续分配的,假如回收了其中某一段,那段内存就是内存碎片,很难再使用它,造成空间浪费。那么从它这个产生的原理就应该尽量避免它发生,如果这片内存的使用时间一致,不会出现中间某个先被回收,那就可以避免造成很多碎片。
堆:对应链表这种数据结构,栈:对应栈数据结构。
堆是往高地址方向申请的,栈是往低地址方向使用的。
12.引用和指针的区别
引用就是限制了的指针
指针:可以指向NULL
引用:不行
指针:可以不初始化
引用:不行
指针:可以改变指向
引用:不行
13.进程间通信
1.管道:有血缘关系的进程间通信,比如父子进程
2.有名管道:有无血缘的进程都可以通信,因为他是有名的,通过名字区分
3.套接字:socket IP地址和端口 支持不同机器的进程通信
4.信号量:0代表进程正在使用资源,-1代表有进程在等待,+1说明有个空闲资源
5.信号机制
6.共享内存
7.消息队列
14.函数 宏 内联函数
函数的优点是书写不容易出错,歧义,缺点:压栈出栈
宏:编写容易出现歧义,因为它就是简单的文本替换,但它不需要要压栈出栈,性能更好
内联函数,兼具两者优点
15.预编译
#include
#include<> 去系统指定目录寻找
#include “” 在当前目录寻找
#define
#ifndef
#define
#endif
16.switch
float double string这些都不可以成为判断表达式