C语言部分复习笔记

news2024/12/23 15:07:51

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));       // 16
 printf("%d\n",sizeof(a+0));     // 4/8
 printf("%d\n",sizeof(*a));      // 4 
 printf("%d\n",sizeof(a+1));     // 4/8
 printf("%d\n",sizeof(a[1]));    // 4
 printf("%d\n",sizeof(&a));      // 4/8
 printf("%d\n",sizeof(*&a));     // 16(*和&抵消了)
 printf("%d\n",sizeof(&a+1));    // 4/8
 printf("%d\n",sizeof(&a[0]));   // 4/8
 printf("%d\n",sizeof(&a[0]+1)); // 4/8
 // 字符数组
 char arr[] = {'a','b','c','d','e','f'}; // 6个 系统不会给最后补0 ""这样赋值才行
 printf("%d\n", sizeof(arr));        // 6
 printf("%d\n", sizeof(arr+0));      // 4/8
 printf("%d\n", sizeof(*arr));       // 1
 printf("%d\n", sizeof(arr[1]));     // 1
 printf("%d\n", sizeof(&arr));       // 4/8
 printf("%d\n", sizeof(&arr+1));     // 4/8
 printf("%d\n", sizeof(&arr[0]+1));  // 4/8
 printf("%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个 最后补0
 printf("%d\n", sizeof(arr));        // 7
 printf("%d\n", sizeof(arr+0));      // 4/8
 printf("%d\n", sizeof(*arr));       // 1
 printf("%d\n", sizeof(arr[1]));     // 1
 printf("%d\n", sizeof(&arr));       // 4/8
 printf("%d\n", sizeof(&arr+1));     // 4/8
 printf("%d\n", sizeof(&arr[0]+1));  // 4/8
 printf("%d\n", strlen(arr));        // 6
 printf("%d\n", strlen(arr+0));      // 6
 printf("%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));  // 5
 const char *p = "abcdef"; // 最后会补'\0'
 printf("%d\n", sizeof(p));          // 4/8
 printf("%d\n", sizeof(p+1));        // 4/8
 printf("%d\n", sizeof(*p));         // 1
 printf("%d\n", sizeof(p[0]));       // 1
 printf("%d\n", sizeof(&p));         // 4/8
 printf("%d\n", sizeof(&p+1));       // 4/8
 printf("%d\n", sizeof(&p[0]+1));    // 4/8
 printf("%d\n", strlen(p));          // 6
 printf("%d\n", strlen(p+1));        // 5
 printf("%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));           // 48
 printf("%d\n",sizeof(a[0][0]));     // 4
 printf("%d\n",sizeof(a[0]));        // 16
 printf("%d\n",sizeof(a[0]+1));      // 4/8 (指针) a[0][1]
 // 这里a[0] 表示a的首个元素,因为sizeof的特殊所以被当成整个数组大小 +1 后这个特殊就没了
 printf("%d\n",sizeof(*(a[0]+1)));   // 4
 printf("%d\n",sizeof(a+1));         // 4/8
 printf("%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 == *cur
         right = 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");
     else
     rintf("大端\n");
     return 0;
 }
 //代码2
 int 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 255
     printf("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(); 
     #else
         unix_version_option3(); 
     #endif 
 #elif defined(OS_MSDOS) 
     #ifdef OPTION2 
         msdos_version_option2(); 
     #endif 
 #endif

编译链接过程

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

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

相关文章

蒂升电梯职业性格和Verify认知能力SHL测评答题攻略及薪资待遇解密!

​一、蒂升电梯职业性格和认知能力测评考什么 您好&#xff01;蒂升电梯公司邀请您参加的OPQ职业性格测评和Verify认知能力测评是两种常见的评估工具&#xff0c;用于帮助了解个人的职场性格特点和认知能力。 OPQ职业性格测评 这是一种性格测试&#xff0c;通常用于评估个人在…

一文讲解Docker入门到精通

一、引入 1、什么是虚拟化 在计算机中&#xff0c;虚拟化&#xff08;英语&#xff1a;Virtualization&#xff09;是一种资源管理技术&#xff0c;它允许在一台物理机上创建多个独立的虚拟环境&#xff0c;这些环境被称为虚拟机&#xff08;VM&#xff09;。每个虚拟机都可以…

盘古5.0,靠什么去解最难的题?

文&#xff5c;周效敬 编&#xff5c;王一粟 当大模型的竞争开始拼落地&#xff0c;商业化在B端和C端都展开了自由生长。 在B端&#xff0c;借助云计算向千行万业扎根&#xff1b;在C端&#xff0c;通过软件App和智能终端快速迭代。 在华为&#xff0c;这家曾经以通信行业起…

Java登录管理功能的自我理解(尚庭公寓)

登录管理 背景知识 1. 认证方案概述 有两种常见的认证方案&#xff0c;分别是基于Session的认证和基于Token的认证&#xff0c;下面逐一进行介绍 基于Session 基于Session的认证流程如下图所示 该方案的特点 登录用户信息保存在服务端内存&#xff08;Session对象&#xff…

Django 一对多关系

1&#xff0c;创建 Django 应用 Test/app9 django-admin startapp app9 2&#xff0c;注册应用 Test/Test/settings.py 3&#xff0c;添加应用路由 Test/Test/urls.py from django.contrib import admin from django.urls import path, includeurlpatterns [path(admin/,…

安装KB5039212更新卡在25% 或者 96% 进度

系统之家7月1日消息&#xff0c;微软在6月11日的补丁星期二活动中&#xff0c;为Windows 11系统推出了KB5039212更新。然而&#xff0c;部分用户在Windows社区中反映&#xff0c;安装过程中出现失败&#xff0c;进度条在25%或96%时卡住。对于遇到此类问题的Windows 11用户&…

YOLOv8改进 | 主干网络 | C2f融合动态卷积模块ODConv

&#x1f4a1;&#x1f4a1;&#x1f4a1;本专栏所有程序均经过测试&#xff0c;可成功执行&#x1f4a1;&#x1f4a1;&#x1f4a1; 专栏目录 &#xff1a;《YOLOv8改进有效涨点》专栏介绍 & 专栏目录 | 目前已有40篇内容&#xff0c;内含各种Head检测头、损失函数Loss、…

Linux CentOS 7 离线安装.NET环境

下载 下载.NET 例如&#xff1a; aspnetcore-runtime-6.0.15-linux-x64.tar.gz 复制 复制到如下目录&#xff1a; /usr/local/dotnet/aspnetcore-runtime-6.0.15-linux-x64.tar.gz 解压 cd /usr/local/dotnet/ tar -zxvf aspnetcore-runtime-6.0.15-linux-x64.tar.gz 创建…

非标设备行业的数智化项目管理

近年来&#xff0c;中国制造快速发展&#xff0c;企业迫切需要加快转型升级。与传统制造业相比&#xff0c;高端制造业具有明显的优势&#xff1a;高技术、高附加值、低污染、低排放、竞争优势强。一方面&#xff0c;企业对于生产效率和自动化水平的要求不断提高&#xff0c;期…

esp12实现的网络时钟校准

网络时间的获取是通过向第三方服务器发送GET请求获取并解析出来的。 在本篇博客中&#xff0c;网络时间的获取是一种自动的行为&#xff0c;当系统成功连接WiFi获取到网络天气后&#xff0c;系统将自动获取并解析得到时间和日期&#xff0c;为了减少误差每两分钟左右进行一次校…

qt可点击的QLabel

需求——问题与思路 使用wpf实现一个可点击的超链接label相当简单&#xff08;如下图&#xff09;&#xff0c;但是qt的QLabel不会响应点击事件&#xff0c;那就从QLabel继承一个类&#xff0c;然后在该类中重写mousePressEvent函数&#xff0c;并在该函数中对左键点击事件做响…

人工智能——常用数学基础之线代中的矩阵

1. 矩阵的本质&#xff1a; 矩阵本质上是一种数学结构&#xff0c;它由按照特定规则排列的数字组成&#xff0c;通常被表示为一个二维数组。矩阵可以用于描述一组数据&#xff0c;或者表示某种关系&#xff0c;比如线性变换。 在人工智能中&#xff0c;矩阵常被用来表示数据集…

沉浸感拉满的三模游戏外设神器!谷粒金刚3 Pro游戏手柄开箱试玩

沉浸感拉满的三模游戏外设神器&#xff01;谷粒金刚3 Pro游戏手柄开箱试玩 哈喽小伙伴们好&#xff0c;我是Stark-C~ 对于喜欢打游戏的玩家来说&#xff0c;一款得力的游戏外设绝对是提升游戏体验&#xff0c;增加游戏乐趣的重要神器&#xff01;而在众多的外设中&#xff0c…

全同态加密在大模型应用中应用

密码学简介 上文的图例基本展示了常见加密体系。加密体系&#xff0c;如果用比较正式的描述方法&#xff0c;无疑是做了三件事&#xff1a; 首先&#xff0c;通过一个生成算法 &#x1d43e;&#x1d452;&#x1d466;&#x1d43a;&#x1d452;&#x1d45b;(1&#x1d70…

32.哀家要长脑子了!

1.299. 猜数字游戏 - 力扣&#xff08;LeetCode&#xff09; 公牛还是挺好数的&#xff0c;奶牛。。。妈呀&#xff0c;一朝打回解放前 抓本质抓本质&#xff0c;有多少位非公牛数可以通过重新排列转换公牛数字&#xff0c;意思就是&#xff0c;当这个数不是公牛数字时&#x…

ctfshow web入门 sqli-libs web552--web560

web552 宽字节注入 嗯原理我就不讲了&#xff0c;还是有点复杂后面有时间讲讲 总而言之就是用汉字把\的转义作用抵消了然后正常注入即可 ?id-1包 union select 1,2,3--?id-1包union select 1,(select group_concat(table_name) from information_schema.tables where tab…

ChatGPT-4o医学应用、论文撰写、数据分析与可视化、机器学习建模、病例自动化处理、病情分析与诊断支持

2022年11月30日&#xff0c;可能将成为一个改变人类历史的日子——美国人工智能开发机构OpenAI推出了聊天机器人ChatGPT-3.5&#xff0c;将人工智能的发展推向了一个新的高度。2023年11月7日&#xff0c;OpenAI首届开发者大会被称为“科技界的春晚”&#xff0c;吸引了全球广大…

如何使用pytest组织自动化测试用例结构?

如何组织自动化测试工程的目录结构&#xff1f;这篇文章介绍了我是如何组织整个自动化工程目录结构的&#xff0c;本篇介绍下我是如何利用pytest框架组织一个测试用例文件的。 用例文件组织原则 整个testsuite目录下整体上按照特性模块划分目录&#xff0c;每个目录下可以只包…

Python容器 之 列表--下标和切片

列表的切片 得到是 新的列表字符串的切片 得到是 新的字符串 如果下标 不存在会报错 list1 [1, 3.14, "hello", False] print(list1)# 获取 列表中 第一个数据 print(list1[0]) # 1# 获取列表中的最后一个数据 print(list1[-1]) # [False]# 获取中间两个数 即 3.1…

币界网快讯,比特币7月份看牛预测

今日数字货币市场全面开启反弹&#xff0c;比特币从 60,000 美元大关飙升至 63,700 美元&#xff0c;预示着 7 月牛市的到来。在此之前&#xff0c;上周曾短暂跌破 60,000 美元&#xff0c;但受到 BTC 现货 ETF 流入的 7,300 万美元的推动——这是两周以来最大的流入。 BTC价格…