C语言部分复习笔记
1. 指针和数组
数组指针 和 指针数组
int* p1[10]; // 指针数组int (*p2)[10]; // 数组指针
因为 [] 的优先级比 * 高,p先和 [] 结合说明p是一个数组,p先和*结合说明p是一个指针
括号保证p先和*结合,说明p是一个指针变量,然后指着指向的是一个大小为10个整型的数组。所以p是一个 指针,指向一个数组,叫数组指针。
arr和&arr的区别
arr代表数组首元素的地址,&arr代表整个数组的地址
void test(int(*arr)[10], int size) // 这里arr也是整个数组的数组指针{for (int i = 0; i < size; ++i){cout << ((int*)arr)[i] << " ";}cout << endl;}int main(){int arr[10] = { 0 };int(*p)[10] = &arr; // 数组指针需要指整个数组test(p, 10);return 0;}
二维数组传参
void test(int arr[3][5])//ok?{}void test(int arr[][])//ok? X{}void test(int arr[][5])//ok?{}// 总结:二维数组传参,函数形参的设计只能省略第一个[]的数字。// 因为对一个二维数组,可以不知道有多少行,但是必须知道一行多少元素。这样才方便运算。void test(int* arr)//ok?X{}void test(int* arr[5])//ok?{}void test(int(*arr)[5])//ok?arr是指向一个大小为5的一维数组{}void test(int** arr)//ok?{}int main(){int arr[3][5] = { 0 };test(arr);}
函数指针
保存函数的地址:函数指针
#include <stdio.h>void test(){}int main(){printf("%p\n", test);printf("%p\n", &test); // 一样cout << typeid(test).name() << endl; // void __cdecl(void) 函数名cout << typeid(&test).name() << endl; // void (__cdecl*)(void) 函数指针void(*p1)(void) = test;void(*p2)(void) = &test; // 一样的return 0;}
函数指针数组
typedef void(*handler)(void);int main(){handler arr[12] = { 0 };void(*arr1[12])(void) = { 0 };}
const和指针
const修饰的指针变量:
-
const位于*前的,表示指针指向的对象内容无法修改,p指向的空间内容(指向对象的内容)无法修改
-
const位于*后面的,表示指针指向的位置无法修改,p的内容(保存的对象地址)无法修改
const int* p = nullptr;int const* p = nullptr;int* const p = nullptr;
sizeof和指针,数组/strlen和指针,数组
sizeof是根据对象的类型判断大小,但是有一个特殊处理就是数组名,sizeof(数组名)
-
sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小
-
&数组名,这里的数组名表示整个数组,取出的是整个数组的地址
-
除此之外所有的数组名都表示首元素的地址
-
但是参数数组也是一个特殊的存在,当数组作为参数进行传递的时候,数组其实退化成了指针
//一维数组int a[] = {1,2,3,4};printf("%d\n",sizeof(a)); // 16printf("%d\n",sizeof(a+0)); // 4/8printf("%d\n",sizeof(*a)); // 4 printf("%d\n",sizeof(a+1)); // 4/8printf("%d\n",sizeof(a[1])); // 4printf("%d\n",sizeof(&a)); // 4/8printf("%d\n",sizeof(*&a)); // 16(*和&抵消了)printf("%d\n",sizeof(&a+1)); // 4/8printf("%d\n",sizeof(&a[0])); // 4/8printf("%d\n",sizeof(&a[0]+1)); // 4/8// 字符数组char arr[] = {'a','b','c','d','e','f'}; // 6个 系统不会给最后补0 ""这样赋值才行printf("%d\n", sizeof(arr)); // 6printf("%d\n", sizeof(arr+0)); // 4/8printf("%d\n", sizeof(*arr)); // 1printf("%d\n", sizeof(arr[1])); // 1printf("%d\n", sizeof(&arr)); // 4/8printf("%d\n", sizeof(&arr+1)); // 4/8printf("%d\n", sizeof(&arr[0]+1)); // 4/8printf("%d\n", strlen(arr)); // 未知printf("%d\n", strlen(arr+0)); // 未知printf("%d\n", strlen(*arr)); // 错误printf("%d\n", strlen(arr[1])); // 错误printf("%d\n", strlen(&arr)); // 报错printf("%d\n", strlen(&arr+1)); // 报错,因为&arr的类型char(*)[6]printf("%d\n", strlen(&arr[0]+1)); // 未知 优先级 [] > * > &char arr[] = "abcdef"; // 7个 最后补0printf("%d\n", sizeof(arr)); // 7printf("%d\n", sizeof(arr+0)); // 4/8printf("%d\n", sizeof(*arr)); // 1printf("%d\n", sizeof(arr[1])); // 1printf("%d\n", sizeof(&arr)); // 4/8printf("%d\n", sizeof(&arr+1)); // 4/8printf("%d\n", sizeof(&arr[0]+1)); // 4/8printf("%d\n", strlen(arr)); // 6printf("%d\n", strlen(arr+0)); // 6printf("%d\n", strlen(*arr)); // 报错printf("%d\n", strlen(arr[1])); // 报错printf("%d\n", strlen(&arr)); // 报错printf("%d\n", strlen(&arr+1)); // 报错printf("%d\n", strlen(&arr[0]+1)); // 5const char *p = "abcdef"; // 最后会补'\0'printf("%d\n", sizeof(p)); // 4/8printf("%d\n", sizeof(p+1)); // 4/8printf("%d\n", sizeof(*p)); // 1printf("%d\n", sizeof(p[0])); // 1printf("%d\n", sizeof(&p)); // 4/8printf("%d\n", sizeof(&p+1)); // 4/8printf("%d\n", sizeof(&p[0]+1)); // 4/8printf("%d\n", strlen(p)); // 6printf("%d\n", strlen(p+1)); // 5printf("%d\n", strlen(*p)); // 报错printf("%d\n", strlen(p[0])); // 报错printf("%d\n", strlen(&p)); // 报错printf("%d\n", strlen(&p+1)); // 报错printf("%d\n", strlen(&p[0]+1)); // 5//二维数组int a[3][4] = {0};printf("%d\n",sizeof(a)); // 48printf("%d\n",sizeof(a[0][0])); // 4printf("%d\n",sizeof(a[0])); // 16printf("%d\n",sizeof(a[0]+1)); // 4/8 (指针) a[0][1]// 这里a[0] 表示a的首个元素,因为sizeof的特殊所以被当成整个数组大小 +1 后这个特殊就没了printf("%d\n",sizeof(*(a[0]+1))); // 4printf("%d\n",sizeof(a+1)); // 4/8printf("%d\n",sizeof(*(a+1))); // 4/8X 16 a[1]printf("%d\n",sizeof(&a[0]+1)); // 4/8 printf("%d\n",sizeof(*(&a[0]+1))); // 4X 16 a[1]printf("%d\n",sizeof(*a)); // 4/8X 16 a[0]printf("%d\n",sizeof(a[3])); // 4/8X 16
总结:先看类型再判断
2. 库函数的模拟实现
memcpy
void* memcpy(void* dest, const void* src, size_t num){assert(dest && src);char* d = (char*)dest;const char* s = (const char*)src;while (num--){*d++ = *s++;}return dest;}
注意:c++使用括号强转类型,生成的是临时变量,不能进行++
memmove
void* memmove(void* dest, const void* src, size_t num){assert(dest && src);char* d = static_cast<char*>(dest);const char* s = static_cast<const char*>(src);while (num--){if (dest < src){*d++ = *s++;}else{*((char*)(d + num)) = *(s + num); // 这里根据num的减少来推进}}return dest;}
strstr
// 从目的字符串中找src字符串static char* strstr(const char* dest, const char* src){assert(dest && src);const char* left = dest, * right = dest;const char* cur = src;while (true){while (*left != '\0' && *left != *cur) left++;if (*left == '\0')break;// *left == *curright = left;while (*right == *cur){right++;cur++;if (*cur == '\0')return const_cast<char*>(left);}cur = src; // cur 回执left++;}return nullptr;}
memset/strcmp
void* memset(void* ptr, int val, size_t num){assert(ptr);char* cur = static_cast<char*>(ptr);while (num--){*cur++ = val;}return ptr;}int strcmp(const char* str1, const char* str2){assert(str1 && str2);while (*str1 != '\0' && *str2 != '\0' && *str1++ == *str2++);//if (*str1 < *str2)// return -1;//else if (*str1 > *str2)// return 1;//else return 0;return *str1 - *str2;}
3. 自定义类型
内存对齐规则
-
第一个成员在与结构体变量偏移量为0的地址处。
-
其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
-
结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
-
如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
对齐数 = 编译器默认的一个对齐数(VS下是8) 与 该成员大小的较小值
联合体
联合也是一种特殊的自定义类型 这种类型定义的变量也包含一系列的成员,特征是这些成员公用同一块空间(所以联合也叫共用体)
联合的成员是共用同一块内存空间的,这样一个联合变量的大小,至少是最大成员的大小(因为 联合至少得有能力保存最大的那个成员)
联合大小的计算:
联合的大小至少是最大成员的大小,当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍
4. 整形的存储规则
原码/反码/补码
计算机中的有符号数有三种表示方法,即原码、反码和补码
三种表示方法均有符号位和数值位两部分,符号位都是用0表示“正”,用1表示“负”,而数值位三种表示方法各不相同
-
原码:直接将二进制按照正负数的形式翻译成二进制就可以
-
反码: 将原码的符号位不变,其他位依次按位取反就可以得到了
-
补码: 反码+1就得到补码
正数的原、反、补码都相同
对于整形来说:数据存放内存中其实存放的是补码
大小端
大端(存储)模式,是指数据的低位保存在内存的高地址中,而数据的高位,保存在内存的低地址中(高低)
小端(存储)模式,是指数据的低位保存在内存的低地址中,而数据的高位,保存在内存的高地址中(高高)
如何判断:
#include <stdio.h>int check_sys(){int i = 1;return (*(char *)&i);}int main(){int ret = check_sys();if(ret == 1)printf("小端\n");elserintf("大端\n");return 0;}//代码2int check_sys(){union{int i;char c;}un;un.i = 1;return un.c;}
5. 编译链接
宏
#define 替换规则 在程序中扩展#define定义符号和宏时,需要涉及几个步骤:
-
在调用宏时,首先对参数进行检查,看看是否包含任何由#define定义的符号。如果是,它们首先被替换。
-
替换文本随后被插入到程序中原来文本的位置。对于宏,参数名被他们的值替换。
-
最后,再次对结果文件进行扫描,看看它是否包含任何由#define定义的符号。如果是,就重复上述处理过程。
注意:
-
宏参数和#define 定义中可以出现其他#define定义的变量。但是对于宏,不能出现递归
-
当预处理器搜索#define定义的符号的时候,字符串常量的内容并不被搜索。
也就是说"宏"只会被当成字符串,宏不会生效,这时#宏:把一个宏参数变成对应的字符串
#include <stdio.h>#define PRINT1(FORMAT, VALUE) \printf("the value is "FORMAT"\n", VALUE)#define PRINT2(FORMAT, VALUE) \printf("the value of "#VALUE" is "FORMAT"\n", VALUE) // yes//printf("the value of ""VALUE"" is "FORMAT"\n", VALUE) // noint main(){char a = -1;signed char b = -1;unsigned char c = -1;printf("a=%d,b=%d,c=%d\n", a, b, c); // 对齐 -1 -1 255printf("file:%s\n line:%d\n time:%s\n", __FILE__, __LINE__, __TIME__);const char* p = "hello ""bit\n"; // 字符串合并printf("%s\n", p);PRINT1("%d", 10);PRINT2("%d", 10);return 0;}
可以把位于它两边的符号合成一个符号。 它允许宏定义从分离的文本片段创建标识符
#define OFFSETOF(struct_name, member_name) \((size_t)&(((struct_name*)0)->member_name))// 获取成员变量的偏移量
宏的优缺点:
优点
-
用于调用函数和从函数返回的代码可能比实际执行这个小型计算工作所需要的时间更多。所以宏比函数在程序 的规模和速度方面更胜一筹。
-
更为重要的是函数的参数必须声明为特定的类型。所以函数只能在类型合适的表达式上使用。反之这个宏怎可 以适用于整形、长整型、浮点型等可以用于>来比较的类型。宏是类型无关的。(C++模板)
缺点
-
每次使用宏的时候,一份宏定义的代码将插入到程序中。除非宏比较短,否则可能大幅度增加程序的长度。
-
宏是没法调试的。
-
宏由于类型无关,也就不够严谨。
-
宏可能会带来运算符优先级的问题,导致程序容易出现错。
#undef 于移除一个宏定义
gcc -D ARRAY_SIZE=10 programe.c // 命令行宏定义
条件编译
#if defined(OS_UNIX) #ifdef OPTION1 unix_version_option1(); #elif defined(OPTION2)unix_version_option2(); #elseunix_version_option3(); #endif #elif defined(OS_MSDOS) #ifdef OPTION2 msdos_version_option2(); #endif #endif
编译链接过程
相关文章:

C语言部分复习笔记
1. 指针和数组 数组指针 和 指针数组 int* p1[10]; // 指针数组int (*p2)[10]; // 数组指针 因为 [] 的优先级比 * 高,p先和 [] 结合说明p是一个数组,p先和*结合说明p是一个指针 括号保证p先和*结合,说明p是一个指针变量,然后指…...
Rust学习笔记 (命令行命令) : 用override set 设置工具链
在cargo run某个项目时出现了如下错误:error: failed to run custom build command for ring v0.16.20(无法运行“Ring v0.16.20”的自定义构建命令),在PowerShell命令行运行命令 rustup override set stable-msvc后成功运行。 o…...
cv::Mat类的矩阵内容输出的各种格式的例子
操作系统:ubuntu22.04OpenCV版本:OpenCV4.9IDE:Visual Studio Code编程语言:C11 功能描述 我们可以这样使用:cv::Mat M(…); cout << M;,直接将矩阵内容输出到控制台。 输出格式支持多种风格,包括O…...
Redis--注册中心集群 Cluster 集群-单服务器
与“多服务器集群”一致需要创建redis配置模板 参照以下链接 CSDN 创建redis容器 node01服务器上创建容器 docker run -d --name redis-6381 --net host --privilegedtrue \ -v /soft/redis-cluster/6381/conf/redis.conf:/etc/redis/redis.conf \ -v /soft/redis-cluster/6…...

CV01_相机成像原理与坐标系之间的转换
目录 0.引言:小孔成像->映射表达式 1. 相机自身的运动如何表征?->外参矩阵E 1.1 旋转 1.2 平移 2. 如何投影到“像平面”?->内参矩阵K 2.1 图像平面坐标转换为像素坐标系 3. 三维到二维的维度是如何丢失的?…...

Android Lint
文章目录 Android Lint概述工作流程Lint 问题问题种类警告严重性检查规则 用命令运行 LintAndroidStudio 使用 Lint忽略 Lint 警告gradle 配置 Lint查找无用资源文件 Android Lint 概述 Lint 是 Android 提供的 代码扫描分析工具,它可以帮助我们发现代码结构/质量…...

【算法刷题 | 动态规划14】6.28(最大子数组和、判断子序列、不同的子序列)
文章目录 35.最大子数组和35.1题目35.2解法:动规35.2.1动规思路35.2.2代码实现 36.判断子序列36.1题目36.2解法:动规36.2.1动规思路36.2.2代码实现 37.不同的子序列37.1题目37.2解法:动规37.2.1动规思路37.2.2代码实现 35.最大子数组和 35.1…...

vue3 vxe-grid列中绑定vxe-switch实现数据更新
1、先上一张图: <template #valueSlot"{ row }"><vxe-switch :value"getV(row.svalue)" change"changeSwitch(row)" /></template>function getV(value){return value 1;};function changeSwitch(row) {console.l…...

Hive SQL:实现炸列(列转行)以及逆操作(行转列)
目录 列转行行转列 列转行 函数: EXPLODE(ARRAY):将ARRAY中的每一元素转换为每一行 EXPLODE(MAP):将MAP中的每个键值对转换为两行,其中一行数据包含键,另一行数据包含值 数据样例: 1、将每天的课程&#…...

MD5算法详解
哈希函数 是一种将任意输入长度转变为固定输出长度的函数。 一些常见哈希函数有:MD5、SHA1、SHA256。 MD5算法 MD5算法是一种消息摘要算法,用于消息认证。 数据存储方式:小段存储。 数据填充 首先对我们明文数据进行处理,使其…...
ES6的代理模式-Proxy
语法 target 要使用 Proxy 包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理handler 一个通常以函数作为属性的对象,用来定制拦截行为 const proxy new Proxy(target, handle)举个例子 <s…...

排序(堆排序、快速排序、归并排序)-->深度剖析(二)
前言 前面介绍了冒泡排序、选择排序、插入排序、希尔排序,作为排序中经常用到了算法,还有堆排序、快速排序、归并排序 堆排序(HeaSort) 堆排序的概念 堆排序是一种有效的排序算法,它利用了完全二叉树的特性。在C语言…...

七一建党节|热烈庆祝中国共产党成立103周年!
时光荏苒,岁月如梭。 在这热情似火的夏日, 我们迎来了中国共产党成立103周年的重要时刻。 这是一个值得全体中华儿女共同铭记和庆祝的日子, 也是激励我们不断前进的重要时刻。 103年, 风雨兼程,砥砺前行。 从嘉兴…...
Spring Boot应用知识梳理
一.简介 Spring Boot 是一个用于快速开发基于 Spring 框架的应用程序的工具。它简化了基于 Spring 的应用程序的配置和部署过程,提供了一种快速、便捷的方式来构建独立的、生产级别的 Spring 应用程序。 Spring Boot 的一些主要优点包括: 1. 简化配置…...
Spring中利用重载与静态分派
Spring中利用重载与静态分派 在Java和Spring框架中,重载(Overloading)和静态分派(Static Dispatch)是两个非常重要的概念,它们在处理类方法选择和执行过程中扮演着关键角色。本文旨在深入探讨Spring环境下…...
文本三剑客之awk:
文本三剑客awk: grep 查 sed 增删改查 主要:增改 awk 按行取列 awk awk默认的分隔符:空格,tab键,多个空格自动压缩为一个。 awk的工作原理:根据指令信息,逐行的读取文本内容,然…...
SpringSecurity-授权示例
用户基于权限进行授权 定义用户与权限 authorities()。 package com.cms.config;import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.core.userdetails.User; import…...

选哪个短剧系统源码好:全面评估与决策指南
在短剧内容创作和分享日益流行的今天,选择合适的短剧系统源码对于构建一个成功的短剧平台至关重要。短剧系统源码不仅关系到平台的稳定性和用户体验,还直接影响到内容创作者和观众的互动质量。本文将提供一份全面的评估指南,帮助您在众多短剧…...

AI时代的软件工程:挑战与改变
人工智能(AI)正以惊人的速度改变着我们的生活和工作方式。作为与AI关系最为密切的领域之一,软件工程正经历着深刻的转变。 1 软件工程的演变 软件工程的起源 软件工程(Software Engineering)是关于如何系统化、规范化地…...

Zuul介绍
Zuul 是 Netflix 开源的一个云平台网络层代理,它主要用于路由、负载均衡、中间件通信和动态路由。Zuul 本质上是一个基于 JVM 的网关,它提供了以下功能: 1.路由:Zuul 允许客户端和服务器之间的所有入站和出站请求通过一个中心化的…...
附加模块--Qt OpenGL模块功能及架构
一、模块功能: 主要变化 Qt OpenGL 模块的分离: 在 Qt 6 中,原来的 Qt OpenGL 功能被拆分为多个模块 传统的 Qt OpenGL 模块 (QGL*) 已被标记为废弃 新的图形架构: Qt 6 引入了基于 QRhi (Qt Rendering Hardware Interface) 的…...
Android第十五次面试总结(第三方组件和adb命令)
Android 第三方组件转为系统组件核心流程 这通常是在进行 Android 系统定制(如 ROM 开发、固件制作)时完成,目的是让第三方应用拥有更高的权限和系统身份。主要过程如下: 核心准备:签名!赋予系统身份 …...

(2025)Windows修改JupyterNotebook的字体,使用JetBrains Mono
(JetBrains Mono字体未下载就配置,这种情况我不知道能不能行,没做过实验,因为我电脑已经下载了,不可能删了那么多字体做实验,我的建议是下载JetBrains Mono字体,当你使用VsCode配置里面的JetBrains字体也很有用) 首先参考该文章下载字体到电脑上 VSCode 修改字体为JetBrains …...
Doris 数据库深度解析:架构、原理与实战应用
一、Doris 的架构与原理 1. 架构组成 Doris 是一个分布式 MPP(大规模并行处理)数据库,它的架构主要由以下几部分组成: FE(Frontend):负责管理元数据、解析 SQL 查询、优化查询计划࿰…...

大故障:阿里云核心域名爆炸了
大故障:阿里云核心域名被拖走了 今天早上许多群里出现网站故障的讨论,比如 cnblogs 全国访问一片红,一看原来是阿里云又出故障了。 今天早上许多群里出现网站故障的讨论,比如 cnblogs 全国访问一片红,一看原来是阿里云…...

【Latex】Windows/Ubuntu 绘制 eps 矢量图通用方法(drawio),支持插入 Latex 数学公式
一直感觉 Visio 或者 PPT 中 Mathtype 对 latex 公式渲染效果不好,且在 Ubuntu 下的支持不好,最近重新调研发现一个好用的工具 drawio。 在线使用 https://app.diagrams.net/?srcabout 也有桌面版的应用,Windows 就下载 exe 安装器&#x…...

uniapp Vue2 获取电量的独家方法:绕过官方插件限制
在使用 uniapp 进行跨平台应用开发时,获取设备电量信息是一个常见的需求。然而,uniapp 官方提供的uni.getBatteryInfo方法存在一定的局限性,它不仅需要下载插件,而且目前仅支持 Vue3,这让使用 Vue2 进行开发的开发者陷…...

Android 平台RTSP/RTMP播放器SDK接入说明
一、技术背景 自2015年起,大牛直播SDK持续深耕音视频直播领域,自主研发并迭代推出跨平台 RTSP/RTMP 播放模块,具备如下核心优势: 全平台兼容:支持 Android/iOS/Windows/Linux 等主流系统; 超低延迟&#…...

Golang基础学习
初见golang语法 go项目路径 cd $GOPATH //ls可以看到有bin,pkg,src三个文件 cd src/ mkdir GolangStudy cd GolangStudy mkdir firstGolanggo程序执行: go run hello.go//如果想分两步执行: go build hello.go ./hello导入包的方式 import "f…...
深入理解汇编语言中的顺序与分支结构
本文将结合Visual Studio环境配置、顺序结构编程和分支结构实现,全面解析汇编语言中的核心编程概念。通过实际案例演示无符号/有符号数处理、分段函数实现和逻辑表达式短路计算等关键技术。 一、汇编环境配置回顾(Win32MASM) 在Visual Studi…...