C语言深度剖析书籍学习记录 第一章 关键字

C语言标准定义了32个关键字

union声明联合数据类型

  • Union declaration - cppreference.com
  • 维护足够的空间来置放多个数据成员中的“一种”,而不是为每一个数据成员配置空间,在 union 中所有的数据成员共用一个空间,同一时间只能储存其中一个数据成员,所有的数据成员具有相同的起始地址
union StateMachine
{char character;int number; char *str; double exp;
};
  • 一个 union 只配置一个足够大的空间以来容纳最大长度的数据成员,以上例而言,最大 长度是 double 型态,所以 StateMachine 的空间大小就是 double 数据类型的大小。
  • union主要目的是为了压缩数据存储的空间,如果变量不会在同一时间被使用到就可以使用 union 

enum声明枚举类型

  • Enumeration declaration - cppreference.com 

rigister 声明寄存器变量

  • Storage class specifiers - cppreference.com
  • https://en.wikibooks.org/wiki/C%2B%2B_Programming/Programming_Languages/C%2B%2B/Code/Keywords/register
  • 这个关键字请求编译器尽可能的将变量存在 CPU 内部寄存器中而不是通过内存寻址访问以提高效率。注意是尽可能,不是绝对。因为 CPU 的寄存器有限
  • 注意: 寄存器在 c 和 c++之间有不同的语义。在 c 语言中,可以通过声明数组寄存器来禁止数组到指针的转换: register int a [1] ; 
  • 从 C++ 17 开始,auto 关键字不再是 C++ 存储类说明符,且 register 关键字被弃用
  • 这意味着变量的最大尺寸等于寄存器的大小(通常是一个词),且不能对它应用一元的 '&' 运算符(因为它没有内存位置)。
  • 存储空间分配不同,auto类型分配在栈上,属于动态存储类别,占动态存储区空间,函数调用结束后自动释放;
  • 而static分配在静态存储区,在程序整个运行期间都不释放。两者之间的作用域相同,但生存期不同
  • static局部变量在所处模块的初次运行时进行初始化工作,且只初始化一次。
  • 对于局部静态变量,如果不赋初值,编译期会自动赋初值0或空字符;而auto类型的初值是不确定的。(对于C++中的class对象例外,class的对象实例如果不初始化,则会自动调用默认构造函数,不管是否是static类型) 
  • register 变量必须是 能被 CPU 寄存器所接受的类型。意味着 register 变量必须是一个单个的值,并且其长度应小 于或等于整型的长度

volatile说明变量类型可以被隐含改变

  • cv (const and volatile) type qualifiers - cppreference.com
  • 编 译器对访问该变量的代码就不再进行优化,从而可以提供对特殊地址的稳定访问
  • const volatile修饰的变量 - Jeremy's blog
int i=10;
int j = i;//(1)语句 
int k = i;//(2)语句
  • 这时候编译器对代码进行优化,因为在(1)、(2)两条语句中,i 没有被用作左值。这时候 编译器认为 i 的值没有发生改变,所以在(1)语句时从内存中取出 i 的值赋给 j 之后,这个 值并没有被丢掉,而是在(2)语句时继续用这个值给 k 赋值。编译器不会生成出汇编代码 重新从内存里取 i 的值,这样提高了效率。但要注意:(1)、(2)语句之间 i 没有被用作左 值才行。
volatile int i=10; 
int j = i;//(3)语句 
int k = i;//(4)语句
  •  volatile 关键字告诉编译器 i 是随时可能发生变化的,每次使用它的时候必须从内存中取出 i 的值,因而编译器生成的汇编代码会重新从 i 的地址处读取数据放在 k 中。这样看来,如果 i 是一个寄存器变量或者表示一个端口数据或者是多个线程的共享数 据,就容易出错,所以说 volatile 可以保证对特殊地址的稳定访问。
    但是注意:在 VC++6.0 中,一般 Debug 模式没有进行代码优化,所以这个关键字的作 用有可能看不出来。你可以同时生成 Debug 版和 Release 版的程序做个测试。

mutable 存储类

  • mutable 说明符仅适用于类的对象,这将在本教程的最后进行讲解。它允许对象的成员替代常量。也就是说,mutable 成员可以通过 const 成员函数修改。

thread_local 存储类

  • 使用 thread_local 说明符声明的变量仅可在它在其上创建的线程上访问。 变量在创建线程时创建,并在销毁线程时销毁。 每个线程都有其自己的变量副本。
  • thread_local 说明符可以与 static 或 extern 合并。
  • 可以将 thread_local 仅应用于数据声明和定义,thread_local 不能用于函数声明或定义。
  • 以下演示了可以被声明为 thread_local 的变量:
thread_local int x;  // 命名空间下的全局变量
class X
{static thread_local std::string s; // 类的static成员变量
};
static thread_local std::string X::s;  // X::s 是需要定义的void foo()
{thread_local std::vector<int> v;  // 本地变量
}

定义

  • 所谓的定义就是(编译器)创建一个对象,为这个对象分配一块内存,取上一个名字,这个名字就是变量名或对象名
  • 但注意,这个名字一旦和 这块内存匹配起来,它们就同 生共死,终生不离不弃。并且这块内存的位置也不能被改变。
  • 一个变量或对象在一定的区 域内(比如函数内,全局等)只能被定义一次,如果定义多次,编译器会提示你重复定义 同一个变量或对象。

声明  两重含义

第一重

  • 通知编译器,变量名字 已经匹配到一块内存空间,此刻出现的变量或对象是在别的地方已经定义过了
  • 声明可以出现多次

第二重

  • 通知编译器,名字已经提前预定了,别的地方再也不能用它来作为变量名或对象名
  • 例子:void fun(int i, char c);

例子

  • int i; 定义
  • extern int i; 声明

重点:

  • 定义声明最重要的区别:定义创建了对象并为这个对象分配了内存,声明没有分配内存

基本数据类型

  • 使用sizeof 查看具体平台数据类型的大小

命名规则

  •  所有宏定义、枚举常数、只读变量全用大写字母命名,用下划线分割单词。 
const int MAX_LENGTH = 100; //这不是常量,而是一个只读变量,具体请往后看 
#define FILE_PATH “/usr/tmp”
  • 定义变量的同时千万千万别忘了初始化。因为,定义变量时编译器并不一定清空了 这块内存,它的值可能是无效的数据
  • 不同类型数据之间的运算要注意精度扩展问题,一般低精度数据将向高精度 数据扩展。

sizeof

  • sizeof(p) 和 sizeof(*p)的区别?
  • sizeof(p)是指针类型,64位平台是8,32位平台是4
  • sizeof(*p) 是指针指向的数据类型的大小,如果是long double就是16,double是8,int 是4
int main(){long double *p = nullptr;std::cout << sizeof(p) <<std::endl;std::cout << sizeof(*p) <<std::endl;
}
int main(){int a[100];std::cout << sizeof(a) <<std::endl;    //400std::cout << sizeof(&a) <<std::endl;   //8std::cout << sizeof(&a[0]) <<std::endl;//8
}
#include <iostream>int b[100];
void fun(int b[100]){std::cout << sizeof(b) <<std::endl;    //8
}int main(){fun(b);
}
#include <iostream>
#include <cstring>int main(){char a[1000];for (int i = 0; i < 1000; ++i) {a[i] = -1 - i;}printf("%d",strlen(a));
}
  • for 循环内,当 i 的值为 0 时,a[0]的值为-1。关键就是-1 在内存里面如何存储。
  • 计算机系统中,数值一律用补码来表示(存储)。主要原因是使用补码,可以将符号位和其它位统一处理;同时,减法也可按加法来处理。另外,两个用补码表示的数 相加时,如果最高位(符号位)有进位,则进位被舍弃正数的补码与其原码一致;负数的 补码:符号位为 1,其余位为该数绝对值的原码按位取反然后整个数加 1
    按照负数补码的规则,可以知道-1 的补码为 0xff,-2 的补码为 0xfe......当 i 的值为 127 时,a[127]的值为-128,而-128 是 char 类型数据能表示的最小的负数。当 i 继续增加,a[128] 的值肯定不能是-129。因为这时候发生了溢出,-129 需要 9 位才能存储下来,而 char 类型 数据只有 8 位,所以最高位被丢弃。剩下的 8 位是原来 9 位补码的低 8 位的值,即 0x7f。 当 i 继续增加到 255 的时候,-256 的补码的低 8 位为 0。然后当 i 增加到 256 时,-257 的补 码的低 8 位全为 1,即低八位的补码为 0xff,如此又开始一轮新的循环......
  • 按照上面的分析,a[0]到 a[254]里面的值都不为 0,而 a[255]的值为 0。strlen 函数是计 算字符串长度的,并不包含字符串最后的‘\0’。而判断一个字符串是否结束的标志就是看 是否遇到‘\0’。如果遇到‘\0’,则认为本字符串结束。分析到这里,strlen(a)的值为 255 应该完全能理解了。这个问题的关键就是要明白 char 类型默认情况下是有符号的,其表示的值的范围为[-128,127],超出这个范围的值会产生溢 出。另外还要清楚的就是负数的补码怎么表示。弄明白了这两点,这个问题其实就很简单了。
所谓原码就是前面所介绍的二进制定点表示法,即最高位为符号位,“0”表示正,“1”表示负,其余位表示数值的大小。反码表示法规定:正数的反码与其原码相同;负数的反码是对其原码逐位取反,但符号位除外。补码表示法规定:正数的补码与其原码相同;负数的补码是在其反码的末位加1。根据原码的定义:正零和负零的原码为:+0 : 0000 0000 0000 0000 0000 0000 0000 0000 (32 bit)-0 : 1000 0000 0000 0000 0000 0000 0000 0000而反码为:+0 : 0000 0000 0000 0000 0000 0000 0000 0000-0 : 1111 1111 1111 1111 1111 1111 1111 1111补码为:+0 : 0000 0000 0000 0000 0000 0000 0000 0000-0 : 1 0000 0000 0000 0000 0000 0000 0000 0000可以看出,-0的补码发生溢出,舍弃最高位后,其跟+0在内存的表示一样,都是:0000 0000 0000 0000 0000 0000 0000 0000

switch case

  • case 后面只能是整型或字符型的常量或常量表达式(想想字符型数据在内存里 是怎么存的) 
  • switch case排列顺序
    • 把正常情况放在前面,而把异常情况放在后面
    • 按执行频率排列 case 语句
  • switch 里面不可以使用 continue 
  •  在switch case 语句中能否使用continue关键字?_Keep Fighting All The Time-CSDN博客_switch语句中的continue

循环代码

  • 多重循环中,如果有可能,应当将最长的循环放在最内层,最短的循环放在最外层,以减少 CPU 跨切循环层的次数。

void

  • void *可以指向任何类型的数据 
  • void 不能代表一个真实的变量,因为没有内存空间

return关键字

  • c++ - Return char* from function - Stack Overflow

return char*

  • 函数内新建一个static char数组,这样函数结束数组也不会被销毁   /   使用const char * 字符串存储在常量区
  • 函数内部动态申请内存,使用完需要释放内存
  • 全局声明数组,不好,别人会改
  • 函数返回char* 的解决方案_芒果儿-CSDN博客_c++ 返回char*

const

  • case 语句后必须是一个常量,const 修饰的只读变量仍然是变量,所以是不可以的
  • 【C语言】const关键字用法 - 代码先锋网

  • const 定义的只读变量从汇编的角度来看,只是给出了对应的内存地址,而不是象#define 一样给出的是立即数,所以,const 定义的只读变量在程序运行过程中只有一份拷贝(因为 它是全局的只读变量,存放在静态区),
  • 而#define 定义的宏常量在内存中有若干个拷贝。 #define 宏是在预编译阶段进行替换,而 const 修饰的只读变量是在编译的时候确定其值。
  • #define 宏没有类型,而 const 修饰的只读变量具有特定的类型。 

  • const 离哪个近,修饰哪个变量,不可以改变 

  • 修饰函数的参数  告诉编译器 ,形参输入,在函数体中的不能改变,从而防止了使用者的一些无意的或错误的修改。 
  • 修饰函数的返回值 const 修饰符也可以修饰函数的返回值,返回值不可被改变。例如: const int Fun (void);

参考链接

  • What does sizeof (int) * p semantically mean?
  • c - Difference between sizeof(*p) and sizeof(p)? - Stack Overflow
  • c语言中的 %u 什么意思啊?_百度知道

struct关键字

  • 多种数据组合起来的一个整体,其表现形式是一个结构体
  • 传入传出都是结构体的形式,可以压缩传输的参数
  • 结构体所占的内存大小是其成员所占内存之和  sizeof(struct),这里还涉及到结构体的内存对齐
  • 即使是空的结构体,使用sizeof求内存,其大小是1,主要是为其分配一个地址,空类也是一样的

柔性数组

  • 结构中的最后一个元素允许是未知大小的数组,这就叫做柔性数组成员,但结构中的柔性数组成员前面必须至少一个其他成员。
  • 柔性数组成员允许结构中包含一个大小可变的数组。sizeof 返回的这种结构大小不包括柔性数组的内存。包含柔性数组成员的结构用 malloc ()函数进行内存的动态分配,并且分配的内存应该大于结构的大小,以适应柔性数组的预期大小。
  • 用 sizeof(type_a)得到的只有 4,就是 sizeof(i)=sizeof(int)。那个 0 个元素的数组没有占用空间,而后我们可以进行变长操作了。使用malloc分配内存之后,使用sizeof探测整体的数据大小,仍然是 4
  • 柔性数组只是编外人员,不占结构体的编制。只是说 在使用柔性数组时需要把它当作结构体的一个成员,仅此而已。再说白点,柔性数组其实与 结构体没什么关系,只是“挂羊头卖狗肉”而已,算不得结构体的正式成员
  • 当然,上面既然用 malloc 函数分配了内存,肯定就需要用 free 函数来释放内存
  • 这个柔性数组的概念 实际使用很少
#include <iostream>
#include <cstring>typedef struct st_type{int i;int a[];
}type_a;
int main(){std::cout << sizeof(type_a) << std::endl;  //4type_a * p = (type_a*) malloc(sizeof (type_a) + 100 * sizeof(int));std::cout << sizeof(type_a) << std::endl;  //4free(p);
}

 大端模式 和 小端模式

  • 大端模式(Big_endian):字数据的高字节存储在低地址中,而字数据的低字节则存放在高地址中。
  • 小端模式(Little_endian):字数据的高字节存储在高地址中,而字数据的低字节则存放在低地址中。

 

  • 变量i占4个字节,但只有一个字节的值为1,另外三个字节的值都为0。如果取出低地址上的值为0,毫无疑问,这是大端模式;如果取出低地址上的值为1,毫无疑问,这是小端模式。

使用程序验证

#include <iostream>
#include <cstring>int checkSystem(){union check{int i;char ch;}c;c.i = 1;return (c.ch == 1);
}
int main(){int a = checkSystem();std::cout << a << std::endl;
}

直接查看内存

 

  •  枚举变量的大小,实质是常数所占内存空间的大小(常数为int类型,当前主流的编译器中一般是32位机器和64位机器中int型都是4个字节),枚举类型所占内存大小也是这样。参考链接:enum枚举变量所占内存大小_bulebin的博客-CSDN博客_枚举类型大小

 

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

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

相关文章

js页面自适应屏幕大小_移动端自适应布局方法的calc()与vw

前端人员在处理移动端自适应布局时&#xff0c;目前前端最流行的方法应该就是使用媒体查询&#xff0c;来设置HTML的字体大小&#xff0c;然后用rem为单位对Dom的宽高进行设置&#xff0c;这个方法的优势在于兼容性方面很好&#xff0c;劣势则在于当前市场上不同的机型太多&…

C语言深度剖析书籍学习记录 第二章 符号

\ 连接符号&#xff0c;// \ 可以把下一行也注释调编译器 删除注释时&#xff0c;会使用空格进行替代

详细描述三个适于瀑布模型的项目_IT项目管理笔记——方法选择和软件评估

一、管理需求为什么要管理需求&#xff1f;避免失败&#xff0c;提高项目的成功率和需求管理所带来的其他好处软件生命周期中&#xff0c;一个错误发现得越晚&#xff0c;修复错误的费用越高许多错误是潜伏的&#xff0c;并且在错误产生后很长一段时间才被检查出来在需求阶段&a…

Socket通信 客户端加密数据,传递数据密文到服务端,服务端解密密文 输出明文

server // sdf_cpp_warpper.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。 // server端#ifndef UNICODE #define UNICODE #endif#define WIN32_LEAN_AND_MEAN#include <iostream> #include <string> #include <sstream> #include …

主进程中发生了一个javascript错误_知道html5 Web Worker标准吗?能实现JavaScript的多线程?

js为什么是单线程&#xff1f;主要是因为最开始javascript是单纯的服务于浏览器的一种脚步语言(那时候没有nodejs)。浏览器是为了渲染网页&#xff0c;通过dom与用户交互&#xff0c;如果一个线程需要给dom执行click事件&#xff0c;而另一个进程要删除这个dom&#xff0c;这2个…

C语言深度剖析书籍学习记录 第三章 预处理

宏 _LINE_ 表示正在编译的文件的行号_FILE_ 表示正在编译的文件的名字_DATE_ 表示编译时刻的日期字符串&#xff0c;例如: "25 Dec 2007"_TIME_ 表示编译时刻的时间字符串&#xff0c;例如: "12:30:55"_STDC_ 判断该文件是不是定义成标准 C 程序宏名的书写…

js正则限制字符串长度_正则笔记(3)万字长文,慎点。

正则讲了很久&#xff0c;也拖了很久&#xff0c;今天看看怎么用吧&#xff0c;后续更文应该会比较准勤快了。:-)书接上文【正则笔记(2)】。这次我们来看看正则的使用&#xff1a;(注&#xff1a;斜体表示为对应规则写出的正则表达式)一、 常用的正则表达式&#xff1a;1. 验证…

C语言深度剖析书籍学习记录 第四章 指针和数组

p 称为指针变量,p 里存储的内存地址处的内存称为 p 所指向的内存。 指针变量 p 里存储的任何数据都将被当作地址来处理一个基本的数据类型(包括结构体等自定义类型)加上“*” 号就构成了一个指针类型的模子。这个模子的大小是一定的&#xff0c;与“*”号前面的数据类型无 关。…

js中select下拉框重置_如何利用CSS3制作炫酷的下拉框

很多小伙伴都不清楚CSS3是做什么&#xff1f;用途是什么&#xff1f;接下来我就给展示一个css3制作一个炫酷下拉框。其实不只是这些&#xff0c;还有很多。CSS3是CSS(层叠样式表)技术的升级版本&#xff0c;于1999年开始制订&#xff0c;2001年5月23日W3C完成了CSS3的工作草案&…

select选择框必输校验_轮子这么多,我们为什么选择自研NewSQL

作者介绍李鑫&#xff0c;滴滴资深软件开发工程师&#xff0c;多年分布式存储领域设计及开发经验。曾参与NoSQL/NewSQL数据库Fusion、分布式时序数据库sentry、NewSQL数据库SDB等系统的设计开发工作。一、背景Fusion-NewSQL是由滴滴自研的在分布式KV存储基础上构建的NewSQL存储…

C语言深度剖析书籍学习记录 第五章 内存管理

常见的内存错误 定义了指针变量&#xff0c;但是没有为指针分配内存&#xff0c;即指针没有指向一块合法的内存。 结构体成员指针未初始化 很多初学者犯了这个错误还不知道是怎么回事。这里定义了结构体变量 stu&#xff0c;但是他没 想到这个结构体内部 char *name 这成员在定…

怎么改电脑网络ip地址_抛弃重启路由器获取ip地址方式,巧妙运用ip代理改IP工具...

网络是简单的也是复杂的&#xff0c;在如此庞大的网络世界里有太多的不确定因素&#xff0c;导致我们遇到IP限制问题&#xff0c;从而影响到我们的网络访问&#xff0c;而大家都知道&#xff0c;如果遇到ip被限制的问题&#xff0c;最快速直接的办法就是把被限制的ip更换一个新…

C语言深度剖析书籍学习记录 第六章 函数

函数的好处 1、降低复杂性:使用函数的最首要原因是为了降低程序的复杂性&#xff0c;可以使用函数来隐含信息&#xff0c;从而使你不必再考虑这些信息。2、避免重复代码段:如果在两个不同函数中的代码很相似&#xff0c;这往往意味着分解工作有误。这时&#xff0c;应该把两个…

如何把word分装到两个byte_如何核对两个Word文档的内容差别?同事加班半小时,我只花了30秒...

昨天下班前&#xff0c;老板突然发了两份Word文档过来&#xff0c;一份是原稿&#xff0c;还有一份是修订稿&#xff0c;叫我们找出两份文档的内容差别之处&#xff0c;我只花了30秒就搞定了&#xff0c;然后准时下班&#xff01;你想知道我是怎么操作的吗&#xff1f;下面小源…

stm32f767中文手册_ALIENTEK 阿波罗 STM32F767 开发板资料连载第五章 SYSTEM 文件夹

1)实验平台&#xff1a;alientek 阿波罗 STM32F767 开发板2)摘自《STM32F7 开发指南(HAL 库版)》关注官方微信号公众号&#xff0c;获取更多资料&#xff1a;正点原子第五章 SYSTEM 文件夹介绍第三章&#xff0c;我们介绍了如何在 MDK5 下建立 STM32F7 工程。在这个新建的工程之…

手机安卓学习 内核开发

官网开源代码 Documentation - MiCode/Xiaomi_Kernel_OpenSource - Sourcegraph Xiaomi 11T Pro GitHub - MiCode/Xiaomi_Kernel_OpenSource: Xiaomi Mobile Phone Kernel OpenSourceAndroid 开源项目 | Android Open Source Project google安卓官网 目录概览 参考…

vs 启动调用的目标发生异常_如何解决不可测、异常场景的问题?

阿里QA导读&#xff1a;在软件研发过程中&#xff0c;发布前跨多个系统的联调测试是不可或缺的一环&#xff0c;而在联调过程中&#xff0c;经常会遇到一些比较棘手的困难&#xff0c;阻塞整个联调进程。其中比较典型的有&#xff1a;第三方的研发节奏不一致&#xff0c;导致无…

Linux内核 scatterlist介绍

scatterlist 物理内存的散列表。通俗讲&#xff0c;就是把一些分散的物理内存&#xff0c;以列表的形式组织起来 诞生背景 假设有三个模块可以访问memory&#xff1a;CPU、DMA控制器和某个外设。CPU通过MMU以虚拟地址&#xff08;VA&#xff09;的形式访问memory&#xff1b;…

Linux内核 crypto文件夹 密码学知识学习

密码算法分类 对称算法非对称算法消息摘要&#xff08;单向哈希&#xff09;算法这些算法作为加密函数框架的最底层&#xff0c;提供加密和解密的实际操作。这些函数可以在内核crypto文件夹下&#xff0c;相应的文件中找到。不过内核模块不能直接调用这些函数&#xff0c;因为…