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.数组

1. 对数组操作进行函数封装时,传值 传地址? (传递数组 形参通过指针接收)
     传地址 
①数组会退化成指针
           当数组作为参数传递时,会自动退化成指向第一个元素的指针。
②避免数组副本拷贝
           内存节省:只需要传递4/8字节的指针地址,而不是整个数组。
③函数封装通过函数修改原数组内容

字符串 str系列函数

#include<string.h>

strlen:求字符'\0'前有效字符个数

int len= strlen(arr);

strcmp:比较字符串大小 

int strcmp(const char *str1, const char *str2);
  • = 0str1 == str2 两个字符串完全一样
  • > 0str1 > str2 第一个不同位上 str1 更大
  • < 0str1 < 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;
}
3) 参数设计为指针
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*

         只保存地址,不能运算 和 解引用

void 不能定义变量 void val; 占用内存空间不知道,无法开辟空间
void* p = &val; 定义正确的,p大小占用内存4B, 保存任意地址正确的,但是不可以对p解引用。
void**p = void*类型变量的地址; *p 元素类型大小: void *类型大小固定不变,对 *p的值进行地址值存储。

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.什么是内存泄漏?

        内存泄漏是指程序在动态分配内存后,不再使用该内存却没有释放,导致这片内存既不能被程序继续使用,也无法被操作系统回收,直到程序结束。(你从系统借了一块内存,用完忘记还了,系统以为你还在用,别人也用不了,就一直空着浪费了。

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐