C语言总结
1.数据类型
有符号:
char(1B) -128~127
short(2B) +-32开头的五位数
int(基于32位以上4B 基于16位操作2B)+-21开头10位数
long(基于Windows 4B 基于Linux 8B)
long long (8B)18位左右
unsigned char val = -1;
printf("%u",val);---->255 (负数转成2进制 先写对应正数的二进制 然后加上符号位 按位取反加一 符号位不取反)
对数据类型重命名:#include<stdint.h>
typedef unsigned long long uint64_t;
u:无符号
int:整型
64:占64个比特位 8B
重点:
整形如何比较相等?==
浮点型如何比较?精度差:fabs(a-b)<=0.000001 相等
不要直接用==比较,而是比较它们差的绝对值是否小于一个极小。
C/C++中的浮点数(float/double)遵循IEEE 754标准,用有限的二进制位存储小数
研究浮点数二进制
float 二进制 = 电脑用来存储小数的 32 位 01 编码
- 1 位符号:0 正数,1 负数
- 8 位指数:表示小数点挪几位
- 23 位尾数:表示小数的有效数字
- 值 = (-1)符号位 × 1.尾数 × 2^(指数 - 127)
e.g 12.5的浮点数二进制
①先写出12.5的二进制数 12的二进制是1100 0.5的二进制则要一直乘2 ,直至小数部分为0,取整数,即为1, 所以12.5的二进制数为1100.1。
②1100.1=1.1001×2^3(科学计数法)
③符号位:0
指数:3(小数点挪三位)+127=130 (二进制为 1000 0010)
尾数:10010000000000000000000(补够23位)
即:12.5的浮点数二进制为:0 10000010 10010000000000000000000
符号位 指数+127的二进制 1.1001小数部分补0(补够23位)
2.运算符
^:异或运算符
二进制位 同位置的数,值相同 该位结果是0,值不同 该位结果是1
1)找落单数据
①val^val=0; ②val^0=val;
2)交换函数的实现
常用的方法:
t=a; a=b; b=t;
---------------------
以下面试中可能会被问到:
a=a^b;
b=a^b;
a=a^b;
----------------------
a=a+b;
b=a-b;
a=a-b;
&:按位与
都为 1 时结果才为 1,否则为 0
1)判断奇偶数
if (n & 1) {
printf("奇数\n");
} else {
printf("偶数\n");
}
2)获取IP地址
unsigned int ip = 0x12345678;
// 第一段:18
unsigned char a = (ip >> 24) & 0xFF;
// 第二段:52
unsigned char b = (ip >> 16) & 0xFF;
// 第三段:86
unsigned char c = (ip >> 8) & 0xFF;
// 第四段:120
unsigned char d = ip & 0xFF;
printf("%u.%u.%u.%u\n", a, b, c, d);
// 输出:18.52.86.120
①&0xFF 获得低位的二进制数
②>>8位 再&0xFF 循环往复 得到IP地址
3)提取 RGB 颜色分量提取 RGB 颜色分量
// 提取 RGB 颜色分量
unsigned int color = 0x336699; // 0x33 0x66 0x99
unsigned char red = (color >> 16) & 0xFF; // 0x33
unsigned char green = (color >> 8) & 0xFF; // 0x66
unsigned char blue = color & 0xFF; // 0x99
~:取反
二进制: 0101
~ 之后: 1010
<< >> :左移 右移
10>>1 将整型10向右移动1个比特位

>> 将val/2的n次方
<< 将val*2的n次方
|:按位或
场景:把 x 的最低位变成 1
x = x | 1;
3.数组
字符串 str系列函数
#include<string.h>
strlen:求字符'\0'前有效字符个数
strcmp:比较字符串大小
- = 0:
str1 == str2两个字符串完全一样 - > 0:
str1 > str2第一个不同位上 str1 更大 - < 0:
str1 < str2第一个不同位上 str1 更小
与==的区别?
==比的是:两个字符串的地址是否一样strcmp比的是:两个字符串的内容是否一样
strcat:连接字符串
char *strcat(char *dest, const char *src);
- 功能:把
src接到dest后面 - 自动覆盖
dest原来的\0
strcpy:拷贝字符串
char *strcpy(char *dest, const char *src);
- 功能:把
src整个复制到dest - 连
\0一起拷过去 - 注意:
dest空间要足够大,否则溢出
strstr:主串中查找子串
char *strstr(const char *str, const char *substr);
- 功能:在
str中查找子串 substr - 返回:
- 找到 → 返回第一次出现的地址
- 没找到 → 返回
NULL
atoi:字符串->整型 #include<stdlib.h>
int atoi(const char *str);
只认数字、正负号,遇到非数字就停
itoa:整型->字符串
char *itoa(int value, char *str, int radix);
radix:进制 10?16
str:存到哪个字符串中去
可以用sprintf替代
strtok:分割函数
char *strtok(char *str, const char *delim);
- 功能:按分隔符把字符串切成一段一段
- 第一次调用传原字符串,后面传
NULL - 例子(按空格切)
char s[] = "a b c d";
char *p = strtok(s, " "); // a
while (p != NULL) {
p = strtok(NULL, " "); // b → c → d
}
注意:会修改原字符串
mem系列函数(内存操作函数)
memset:逐字节赋值
void *memset(void *s, int c, size_t n);
把从 s 开始的 n 个字节,全部设置成 c
int arr[10];
memset(arr, 0, 10 * sizeof(int)); // 全部清0
memcpy:内存拷贝
void *memcpy(void *dest, const void *src, size_t n);
从 src 拷贝 n 个字节 到 dest。
int a[5] = {1,2,3,4,5};
int b[5];
memcpy(b, a, 5*sizeof(int));
注意:不考虑内存重叠
memmove:安全内存拷贝
void *memmove(void *dest, const void *src, size_t n);
- memmove 可以处理内存重叠
- 内部会判断方向,保证拷贝正确
时间空间复杂度
时间复杂度:算法语句执行总次数,关于问题规模n的函数
大O计法:
推导公式:①1替代加法常数
②只保留最高阶,最高阶前系数化简为1
常见时间复杂度:O(1) O(n) O(logn)(log以2为底)
O(次数) 次数越小,算法效率越高
空间复杂度:O(额外开辟的内存空间 与原问题规模空间有关)
e.g int arr[]={1,2,3,4};//实现翻转
思路:开一个新数组brr ,int brr[4]; 将原数据逆序存到brr中
空间复杂度O(n)
5.指针
基于x86(win32)指针大小:4B
指针:是一个变量,存储的是地址
地址:是一个常量,表示的是地址值(指针是地址的别名)
打印地址:
printf("%p",a);
用户 内核
windows系统 2G(自己用) 2G(系统用)
Linux 3G 1G

Debug版本下变量可能会重排

1.解引用 指针的+1能力
int val=10;
int *p=&val;
*p:解引用p直接解到p所指之外里面去 获得的值为10。
p+1----->类型:int* (基于连续的内存空间)
偏移量:从p所指的位置 偏移量 1*sizeof(int)
指针+1的能力取决于 指针的基类型(所指之物的类型)
*p++:*和++的优先级相同,所以自右向左结合,但是个后置++,先与别的运算符结合再++,即:(*p)++,先解引用再++。
野指针是指向无效内存的指针,由未初始化、释放后未置空、返回局部变量地址等原因产生;危害是程序崩溃或数据损坏,解决方案是初始化、释放后置 NULL、使用前检查。
2.指针数组 和 数组指针
指针数组:
int* arr[] = {&a,&b,&c};
数组指针:
int brr[3];
int(*p)[3] = &brr;
p+1 --> int[3] 大小
(int*)(p+1)[-1] 强转 改变了指针移动步长 brr[2] 元素
3.指针与函数
1)函数指针:指针 指向一个函数
int add(int a,int b){return a+b;}
int mul(int a,int b){return a*b;}
定义指针
int(*p)(int,int) = add;
p(1,2);
p = mul;
p(1,2);
2)指针函数: 是一个函数: 返回值是个指针
void* GetMemory(size_t n,size_t size) //n个元素 每个元素大小size字节
{
void* p = (void*)malloc(n*size);
return p;
}
1. 节省内存
void Show(int* arr,int len){ }
void ShowStudent(const Student* ps){ }
2. 形参修改影响实参 传实参指针 函数内对形参解引用(传指针 解引用)
void Swap(int * pa,int *pb)
{
int t = *pa;*pa = *pb;*pb = t;
}
void Change(int* arr,int len,int val,int newval)
{
for(int i = 0;i<len;i++)
{
if(arr[i] == val)
arr[i] = newval;
}
}
bool GetMem(void** p,size_t n,size_t size)
{
*p = malloc(n*size);
if(*p == NULL) return false;
return true;
}
int main()
{
void* ptr;
if(GetMem(&ptr,10,sizeof(int))==NULL)
return 0;
}
正确
4)void*
只保存地址,不能运算 和 解引用
6.动态内存
malloc+memset ==>calloc
malloc + for 更加灵活
malloc
用法
①ElemType*p=(ElemType*)malloc(总字节数);
②if(p==NULL)内存申请失败 退出函数
③内存申请成功 指针解引用 使用*(p+1)p[1]
④使用完成结束代码逻辑 free(p)
⑤p==NULL;
malloc和new的区别

calloc
对比malloc:int*p=(int*)malloc(10*sizeof(int));
calloc: int*p=(int*)calloc(10,sizeof(int));
malloc每个元素都是随机值 calloc 元素都为0
realloc(扩容)
原理
①申请的堆区内存后,未分配内存空间足够增加新容量,就在原堆区空间后增加新空间;
②申请的堆区后,未分配内存空间不够增加新容量,realloc底层重新找一个更大的未分配的内存空间,将原内存数据拷贝到新空间中去,realloc新空间地址返回出来。
③申请堆区内存后,其他堆区地方没有足够未分配的空间,扩容失败,返回值NULL。
使用步骤
int*p=(int*)malloc(10*sizeof(int));
if(p==NULL) return;
int*t=(int*)realloc(p,2*10*sizeof(int));
if(t==NULL)
{ 扩容失败;释放原空间取决于逻辑(free(p);p=NULL;return;);}
else
扩容成功;
p=t;(把t还给p)
对p所指空间继续操作
free(p);
p=NULL;
free
1.free出现崩溃的原因?
1)对同一块地址多次释放
2)释放的是非堆区空间
3)释放的是堆区空间的首地址
2.free如何知道要释放内存空间?
malloc申请的内存由两部分组成,[指针头][100B],指针头存储了个变量size,跟内存有关,专门用来记录大小,p返回的是用户可用地址。
3.什么是内存泄漏?
内存泄漏是指程序在动态分配内存后,不再使用该内存却没有释放,导致这片内存既不能被程序继续使用,也无法被操作系统回收,直到程序结束。(你从系统借了一块内存,用完忘记还了,系统以为你还在用,别人也用不了,就一直空着浪费了。)
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐


所有评论(0)