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修饰的指针变量:

  1. const位于*前的,表示指针指向的对象内容无法修改,p指向的空间内容(指向对象的内容)无法修改

  2. const位于*后面的,表示指针指向的位置无法修改,p的内容(保存的对象地址)无法修改

     const int* p = nullptr;int const* p = nullptr;int* const p = nullptr;

sizeof和指针,数组/strlen和指针,数组

sizeof是根据对象的类型判断大小,但是有一个特殊处理就是数组名,sizeof(数组名)

  1. sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小

  2. &数组名,这里的数组名表示整个数组,取出的是整个数组的地址

  3. 除此之外所有的数组名都表示首元素的地址

  4. 但是参数数组也是一个特殊的存在,当数组作为参数进行传递的时候,数组其实退化成了指针

 //一维数组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. 自定义类型

内存对齐规则

  1. 第一个成员在与结构体变量偏移量为0的地址处。

  2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处

  3. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍

  4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍

对齐数 = 编译器默认的一个对齐数(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定义符号和宏时,需要涉及几个步骤:

  1. 在调用宏时,首先对参数进行检查,看看是否包含任何由#define定义的符号。如果是,它们首先被替换。

  2. 替换文本随后被插入到程序中原来文本的位置。对于宏,参数名被他们的值替换。

  3. 最后,再次对结果文件进行扫描,看看它是否包含任何由#define定义的符号。如果是,就重复上述处理过程。

注意:

  1. 宏参数和#define 定义中可以出现其他#define定义的变量。但是对于宏,不能出现递归

  2. 当预处理器搜索#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) // no​int 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))// 获取成员变量的偏移量

宏的优缺点:

优点

  1. 用于调用函数和从函数返回的代码可能比实际执行这个小型计算工作所需要的时间更多。所以宏比函数在程序 的规模和速度方面更胜一筹。

  2. 更为重要的是函数的参数必须声明为特定的类型。所以函数只能在类型合适的表达式上使用。反之这个宏怎可 以适用于整形、长整型、浮点型等可以用于>来比较的类型。宏是类型无关的。(C++模板)

缺点

  1. 每次使用宏的时候,一份宏定义的代码将插入到程序中。除非宏比较短,否则可能大幅度增加程序的长度

  2. 宏是没法调试的。

  3. 宏由于类型无关,也就不够严谨

  4. 宏可能会带来运算符优先级的问题,导致程序容易出现错

#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

编译链接过程

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/38850.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Web2Code :网页理解和代码生成能力的评估框架

多模态大型语言模型&#xff08;MLLMs&#xff09;在过去几年中取得了爆炸性的增长。利用大型语言模型&#xff08;LLMs&#xff09;中丰富的常识知识&#xff0c;MLLMs在处理和推理各种模态&#xff08;如图像、视频和音频&#xff09;方面表现出色&#xff0c;涵盖了识别、推…

VuePress介绍

从本文开始&#xff0c;动手搭建自己的博客&#xff01;希望读者能跟着一起动手&#xff0c;这样才能真正掌握。 ‍ VuePress 是什么 VuePress 是由 Vue 作者带领团队开发的&#xff0c;非常火&#xff0c;使用的人很多&#xff1b;Vue 框架官网也是用了 VuePress 搭建的。即…

4PCS点云配准算法实现

4PCS点云配准算法的C实现如下&#xff1a; #include <iostream> #include <pcl/io/pcd_io.h> #include <pcl/point_types.h> #include <pcl/common/common.h> #include <pcl/common/distances.h> #include <pcl/common/transforms.h> #in…

php 通过vendor文件 生成还原最新的composer.json

起因&#xff1a;因为历史原因&#xff0c;在本项目中composer.json基本算废了&#xff0c;没法直接使用composer管理扩展&#xff0c;今天尝试修复一下composer.json。 历史文件&#xff0c;可以看出来已经很久没有维护了&#xff0c;我们主要是恢复require的信息 {"na…

基于CNN的股票预测方法【卷积神经网络】

基于机器学习方法的股票预测系列文章目录 一、基于强化学习DQN的股票预测【股票交易】 二、基于CNN的股票预测方法【卷积神经网络】 文章目录 基于机器学习方法的股票预测系列文章目录一、CNN建模原理二、模型搭建三、模型参数的选择&#xff08;1&#xff09;探究window_size…

下代iPhone或回归可拆卸电池,苹果这操作把我看傻了

刚度过一个愉快的周末&#xff0c;苹果又双叒叕摊上事儿了。 iPhone13 系列被曝扎堆电池鼓包了。 早在去年&#xff0c;就有 iPhone13 和 iPhone14 用户反馈过类似的问题&#xff0c;表示在手机仅仅使用了一年多的时间就出现了电池鼓包的情况&#xff0c;而且还把屏幕给撑起来了…

舞会无领导:一种树形动态规划的视角

没有上司的舞会 Ural 大学有 &#x1d441; 名职员&#xff0c;编号为1∼&#x1d441;。 他们的关系就像一棵以校长为根的树&#xff0c;父节点就是子节点的直接上司。 每个职员有一个快乐指数&#xff0c;用整数 &#x1d43b;&#x1d456; 给出&#xff0c;其中1≤&…

校园卡手机卡怎么注销?

校园手机卡的注销流程可以根据不同的运营商和具体情况有所不同&#xff0c;但一般来说&#xff0c;以下是注销校园手机卡的几种常见方式&#xff0c;我将以分点的方式详细解释&#xff1a; 一、线上注销&#xff08;通过手机APP或官方网站&#xff09; 下载并打开对应运营商的…

当年很多跑到美加澳写代码的人现在又移回香港?什么原因?

当年很多跑到美加澳写代码的人现在又移回香港&#xff1f;什么原因&#xff1f; 近年来&#xff0c;确实有部分曾经移民到美国、加拿大、澳大利亚等地的香港居民选择移回香港。这一现象与多种因素相关&#xff0c;主要可以归结为以下几点&#xff1a; 疫情后的环境变化&#…

【STM32】温湿度采集与OLED显示

一、任务要求 1. 学习I2C总线通信协议&#xff0c;使用STM32F103完成基于I2C协议的AHT20温湿度传感器的数据采集&#xff0c;并将采集的温度-湿度值通过串口输出。 任务要求&#xff1a; 1&#xff09;解释什么是“软件I2C”和“硬件I2C”&#xff1f;&#xff08;阅读野火配…

2025第13届常州国际工业装备博览会招商全面启动

常州智造 装备中国|2025第13届常州国际工业装备博览会招商全面启动 2025第13届常州国际工业装备博览会将于2025年4月11-13日在常州西太湖国际博览中心盛大举行&#xff01;目前&#xff0c;各项筹备工作正稳步推进。 60000平米的超大规模、800多家国内外工业装备制造名企将云集…

最细最有条理解析:事件循环(消息循环)是什么?进程与线程的定义、关系与差异

目录 事件循环&#xff1a;引入 一、浏览器的进程模型 1.1、什么是进程&#xff08;Process&#xff09; 1.2、什么是线程&#xff08;Thread&#xff09; 1.3、进程与线程之间的关系联系与区别 二、浏览器有哪些进程和线程 2.1、浏览器的主要进程 ①浏览器进程 ②网络…

ctfshow sqli-libs web561--web568

web561 ?id-1 or 1--?id-1 union select 1,2,3--?id-1 union select 1,(select group_concat(column_name) from information_schema.columns where table_nameflags),3-- Your Username is : id,flag4s?id-1 union select 1,(select group_concat(flag4s) from ctfshow.f…

扩展学习|风险评估和风险管理:回顾其基础上的最新进展

文献来源&#xff1a;[1]Aven, T. (2016). Risk assessment and risk management: Review of recent advances on their foundation. European journal of operational research, 253(1), 1-13. 文章简介&#xff1a;大约30-40年前&#xff0c;风险评估和管理被确立为一个科学领…

数据结构 - C/C++ - 链表

目录 结构特性 内存布局 结构样式 结构拓展 单链表 结构定义 节点关联 插入节点 删除节点 常见操作 双链表 环链表 结构容器 结构设计 结构特性 线性结构的存储方式 顺序存储 - 数组 链式存储 - 链表 线性结构的链式存储是通过任意的存储单元来存储线性…

技术分享:分布式数据库DNS服务器的架构思路

DNS是企业数字化转型的基石。伴随微服务或单元化部署的推广&#xff0c;许多用户也开始采用分布式数据库将原来的单体数据库集群服务架构拆分为大量分布式子服务集群&#xff0c;对应不同的微服务或服务单元。本文将从分布式数据库DNS服务器的架构需求、架构分析两方面入手&…

湖北大学2024年成人高考函授报名专升本市场营销专业介绍

在璀璨的学术殿堂中&#xff0c;湖北大学如同一颗璀璨的明珠&#xff0c;熠熠生辉。为了满足广大社会人士对于继续深造、提升自我、实现职业梦想的渴望&#xff0c;湖北大学特别开设了成人高等继续教育项目&#xff0c;为广大有志之士敞开了一扇通往知识殿堂的大门。 而今&…

【FFmpeg】av_write_frame函数

目录 1.av_write_frame1.1 写入pkt&#xff08;write_packets_common&#xff09;1.1.1 检查pkt的信息&#xff08;check_packet&#xff09;1.1.2 准备输入的pkt&#xff08;prepare_input_packet&#xff09;1.1.3 检查码流&#xff08;check_bitstream&#xff09;1.1.4 写入…

【创建者模式-建造者模式】

概要 将一个复杂对象的构建与表示分离&#xff0c;使得同样的构建过程可以创建不同的表示。 建造者模式包含以下角色 抽象建造者类&#xff08;Builder&#xff09;&#xff1a;这个接口规定要实现复杂对象的那些部分的创建&#xff0c;并不涉及具体的部件对象的创建。具体建…

在WSL Ubuntu中启用root用户的SSH服务

在 Ubuntu 中&#xff0c;默认情况下 root 用户是禁用 SSH 登录的&#xff0c;这是为了增加系统安全性。 一、修改配置 找到 PermitRootLogin 行&#xff1a;在文件中找到 PermitRootLogin 配置项。默认情况下&#xff0c;它通常被设置为 PermitRootLogin prohibit-password 或…