C语言杂谈
C语言程序处理过程
预处理:宏定义展开、头文件展开、条件编译,这里并不会检查语法
编译:检查语法,将预处理后文件编译生成汇编文件
汇编:将汇编文件生成目标文件(二进制文件)
链接:将目标文件链接为可执行程序
程序只有在运行才加载到内存(由系统完成),但是某个变量具体分配多大,是在编译阶段就已经确定了,换句话说,在编译阶段做完处理后,程序运行时系统才知道分配多大的空间,所以,很多时候说,这个变量的空间在编译时就分配(确定)了。
字符串操作
1、字符串基本操作
1)字符串初始化
/* C语言没有字符串类型,用字符数组模拟
C语言字符串以数字0,或字符 '\0' 结尾,数字 0 和字符 '\0' 等价 */
char str1[100] = { 'a', 'b', 'c', 'd' }; //没赋值的自动以数字0填充
char str2[] = { 'a', 'b', 'c', 'd' }; //数组长度为4,结尾没有数字0
char str4[] = "abcdef"; //常用赋值方式,栈区
char *p = "abcdef"; //文字常量区,内容不允许被修改
char *buf = (char *)malloc(100); //堆区
strcpy(buf, "abcd"); //"abcd"拷贝到buf指向的内存区域中
2)sizeof和strlen区别
//使用字符串初始化,常用
char buf8[] = "abc";
//strlen: 测字符串长度,不包含数字0,字符'\0'
//sizeof:测数组长度,包含数字0,字符'\0'
printf("strlen = %d, sizeof = %d\n", strlen(buf8), sizeof(buf8));
char buf9[100] = "abc";
printf("strlen = %d, sizeof = %d\n", strlen(buf9), sizeof(buf9));
3)'\0' 后面最好别跟数字,因为几个数字合起来有可能是一个转义字符
//\012相当于\n
char str[] = "\0129";
printf("%s aaaa\n", str);
4)字符'\0', 数字0, 字符'0', NULL的区别
a) 字符'\0' ASCII码值为 0 的字符
字符'\0' 和 数字 0 是等级的,'\0'中'\'是转义字符
char buf[100];
//下面是等级,在数组第10个位置设置字符串结束符
buf[10] = 0;
buf[10] = '\0';
b) 字符'0'是字符串的某个字符内容为'0', ASCII码值为 48 的字符
char buf[100];
buf[0] = '0'; //第0个字符为字符 '0'
c) NULL 标准头文件(stdio.h)的宏 其值为数字 0
const的使用
1)const声明变量为只读(不可更改)
const int a = 10;
a = 100; //error
技巧:去掉指针类型看constant修饰谁
//修饰*,指针指向能变,指针指向的内存不能变
//修饰指针变量,指针指向的内存,指针指向不能变
char buf[100] = "abcdef";
const char *p = buf; //类似于文字常量区 char *p = "123445";
char const *p = buf; //修饰*,指针指向能变,指针指向的内存不能变
//p[0] = '1'; //error
p = "123456"; //ok
char * const p1 = buf; //修饰指针变量,指针指向的内存,指针指向不能变
//p1 = "123456"; //error
p1[0] = '1'; //ok
const char * const p2 = buf; //p2, 只读
数组类型
int a[] = { 1, 3, 5 }; //3个元素
a: 数组首行首元素地址,一级指针
&a: 整个数组的首地址,二级指针
首行首元素地址和首行(整个一维数组)地址值虽然是一样,但是它们的步长不一样
a+1: 跳过1元素,一元素为4字节,步长4个字节
&a+1: 跳过整个数组,整个数组长度为 3*4 = 12,步长 3 * 4 = 12
sizeof(a): 传参为:数组首行首元素地址,测数组(int [3])的长度,3 * 4 = 12
sizeof(a[0]): 传参为:数组首元素(不是地址),每个元素为int类, 4字节
sizeof(&a):传参为:一维数组整个数组的地址(首行地址),编译器当做指针类型,4字节
(重要)首行地址 --> 首行首元素地址(加*)
&a:首行地址
*&a -> a : 首行首元素地址
//数组也是一种数据类型,类型本质:固定大小内存块别名
//由元素类型和内存大小(元素个数)共同决定 int a[5] int[5]
//可以通过typedef定义数组类型
//有typedef:类型
//没有typedef:变量
typedef int ARRARY[5]; //定义了一个名字为ARRARY的数组类型(int[5]类型)
//等价于typedef int (ARRARY)[5];
//根据数组类型,定义变量
//ARRARY的位置替代为d,去掉typedef,int d[5]
ARRARY d; //相当于int d[5];
结构体字节对齐规则
内存对齐的原则
默认情况下,数据成员的对齐规则(以最大的类型字节为单位)。
当然,字节对齐可以通过程序控制,采用指令:
#pragma pack(xx)
#pragma pack(1) //1字节对齐
#pragma pack(2) //2字节对齐
#pragma pack(4) //4字节对齐
#pragma pack(8) //8字节对齐
#pragma pack(16) //16字节对齐
原则1:数据成员的对齐规则(以最大的类型字节为单位)。
结构体(struct)的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存放在offset为该数据成员大小的整数倍的地方(比如int在32位机为4字节,则要从4的整数倍地址开始存储)
1 struct
2 {
3 int a;
4 short b;
5 }A;
6 //对齐单位为 4 个字节(结构体A中最大的类型为int(4个字节))
7 //偏移量:
8 a: 4*0 = 0
9 b: 2*2 = 4
10
11 sizeof(A) = 8;
1 struct
2 {
3 int a;
4 char b;
5 short c;
6 }A;
7 //对齐单位 4 个字节
8 sizeof(A) = 8;
9 a: 4*0 = 0 //偏移0个字节
10 b: 1*4 = 4 //偏移4个字节
11 c: 2*3 = 6
原则2:结构体作为成员的对齐规则。
如果一个结构体B里嵌套另一个结构体A,则结构体A应从offset为A内部最大成员的整数倍的地方开始存储。
(struct B里存有struct A,A里有char,int,double等成员,那A应该从8的整数倍开始存储。),结构体A中的成员的对齐规则仍满足原则1、原则2。
注意:
1. 结构体A所占的大小为该结构体成员内部最大元素的整数倍,不足补齐。
2. 不是直接将结构体A的成员直接移动到结构体B中
1 struct A
2 {
3 int a;
4 double b;
5 float c;
6 };
7
8 struct
9 {
10 char e[2];
11 int f;
12 double g;
13 short h;
14 struct A i;
15 }B;
16 //对齐单位 8 个字节
17 sizeof(B) = 48;
18 //普通成员偏移量
19 e: 1*0 = 0
20 f: 4*1 = 4
21 g: 8*1 = 8
22 h: 2*8 = 16
23 结构体起点坐标:
24 8*3 = 24
25 //结构体成员偏移量
26 a: 24 + 4*0 = 24
27 b: 24 + 8*1 = 32
28 c: 24 + 4*4 = 40
原则3:收尾工作
结构体的总大小,也就是sizeof的结果,必须是其内部最大成员的整数倍,不足的要补齐。
原则4:指定对齐单位
1 #pragma pack(2) //指定对齐单位为2个字节
2 typedef struct
3 {
4 int a;
5 char b;
6 short c;
7 char d;
8 }A;
9 a: 2*0 = 0
10 b: 1*4 = 4
11 c: 2*3 = 6
12 d: 1*8 = 8
13 sizeof(A) = 10;
有关不完整类型的字节对齐
使用位域的主要目的是压缩存储,其大致规则为:
1) 一个位域必须存储在同一个字节中,不能跨两个字节。如一个字节所剩空间不够存放另一位域时,应从下一单元起存放该位域。
2) 如果相邻位域字段的类型相同,且其位宽之和小于类型的sizeof大小,则后面的字段将紧邻前一个字段存储,直到不能容纳为止;
3) 如果相邻位域字段的类型相同,但其位宽之和大于类型的sizeof大小,则后面的字段将从新的存储单元开始,其偏移量为其类型大小的整数倍;
4) 如果相邻的位域字段的类型不同,则各编译器的具体实现有差异,VC6采取不压缩方式,Dev-C++和gcc采取压缩方式;
5) 如果位域字段之间穿插着非位域字段,则不进行压缩;
6) 整个结构体的总大小为最宽基本类型成员大小的整数倍。
1 //以 4 字节对齐
2 struct A
3 {
4 int a1:5;
5 int a2:9;
6 char c;
7 int b:4;
8 short s;
9 }B;
10
11 sizeof(B) = 16
12
13 a1 + a2 = 14 位,少于 32位,4字节
14 a1+a2存放在一起:4*0 = 0
15 c: 1 * 4 = 4
16 b: 4 * 2 = 8 //所占的大小为该结构体成员内部最大元素的整数倍(4对齐),不足补齐。
17 s: 2 * 6 = 12
1 struct A
2 {
3 int a1 : 5;
4 int a2 : 9;
5 char c;
6 int b : 4;
7 short s;
8 }B;
9 sizeof(B) = 12;
10
11 a1+a2 = 14 (小于32位,4字节)
12 a1+a2: 2 * 0 =0;
13 c: 1 * 4 = 4;
14 b: 2 * 3 = 6; //4超过2字节,以2字节为单位
15 s:2 * 5 = 10
C语言杂谈的更多相关文章
- C语言杂谈(三)存储类别
本文讨论C语言中的存储类别,包括数据在内存的存储.变量的存储类别.函数的存储类别.生存周期.下图为计算机的存储空间,有寄存器和内存. 一.存储区域 1.寄存器:存放立即参加运算的数据. 2.系统区:存 ...
- C语言杂谈(二)自增运算符++与间接访问运算符*的结合关系和应用模式
自增运算符++有前缀和后缀两种,在搭配间接访问运算符*时,因为顺序.括号和结合关系的影响,很容易让人产生误解,产生错误的结果,这篇文章来详细分析一下这几种运算符的不同搭配情况. ++.--和*的优先级 ...
- C语言杂谈(一)scanf()、scanf_s()与错误 C4996
错误 C4996 初学C语言时,第一个接触到的I/O函数便是scanf()了.但在高版本的 Visual Studio (包括但不限于2015.2013.2012)编译代码时,却会出现意想不到的错误. ...
- C++ 的语言杂谈(一)--C++不是新手友好的
C++的语言品味是独特的,喜欢的人特别喜欢,讨厌的人特别讨厌.虽然Bjane Stroustrup不断地宣称C++的发展方向是新手友好的,但实际上对新手来说,最重要的还是有强大方便的标准库可以使用(像 ...
- shell、cmd、dos和脚本语言杂谈(转)
问题一:DOS与windows中cmd区别 在windows系统中,“开始-运行-cmd”可以打开“cmd.exe”,进行命令行操作. 操作系统可以分成核心(kernel)和Shell(外壳)两部 ...
- _杂谈_C语言历史
早期的操作系统软件主要是用汇编语言(包括UNIX操作系统在内)编写的.由于汇编语言依赖于计算机硬件,所以程序的可读性和可移植性都比较差,所以呢,为了提高操作系统软件的可读性和可移植性,最好改用高级语言 ...
- 杂谈PID控制算法——最终篇:C语言实现51单片机中的PID算法
真遗憾,第二篇章没能够发表到首页上去.趁热打铁.把最终篇——代码篇给发上来. 代码的设计思想请移步前两篇文章 //pid.h #ifndef __PID__ #define __PID__ /*PID ...
- Linux杂谈: 实现一种简单实用的线程池(C语言)
基本功能 1. 实现一个线程的队列,队列中的线程启动后不再释放: 2. 没有任务执行时,线程处于pending状态,等待唤醒,不占cpu: 3. 当有任务需要执行时,从线程队列中取出一个线程执行任务: ...
- 【转】PHP 杂谈《重构-改善既有代码的设计》之一 重新组织你的函数
原文地址: PHP 杂谈<重构-改善既有代码的设计>之一 重新组织你的函数 思维导图 点击下图,可以看大图. 介绍 我把我比较喜欢的和比较关注的地方写下来和大家分享.上次我写 ...
随机推荐
- 小白学python第1问: int 占几个字节?
windows 64位机器,python3.7:后面的文章中,没有特别说明的话,都是在该环境下运行 int 占几个字节? C语言中(GCC编译器),int 占据4个字节,python呢? 我们用pyt ...
- k8s全方位监控-prometheus-alertmanager部署-配置第一条告警邮件
1.alertmanager告警插件部署 [root@VM_0_48_centos prometheus]# cat alertmanager-pvc.yaml apiVersion: v1 kind ...
- 四、Java基础
Java基础 在开始学习Java基础之前,我们先来学习一下IDEA 打开IDEA,新建一个项目(New Project),选择空项目(Empty Project),填写项目名(Project name ...
- python的变量与基本数据类型
今日内容 python多版本共存 python的注释 python的变量与常量 变量的本质 变量的命名规范 python基本数据类型 内容详细 python多版本共存 先将两个版本的python解释器 ...
- Android 应用框架层 SQLite 源码分析
概述 Android 在应用框架层为开发者提供了 SQLite 相关操作接口,其归属于android.database.sqlite包底下,主要包含SQLiteProgram, SQLiteDat ...
- (转载)字符编码那点事:快速理解ASCII、Unicode、GBK和UTF-8
- 如何用webgl(three.js)搭建处理3D隧道、3D桥梁、3D物联网设备、3D高速公路、三维隧道桥梁设备监控-第十一课
开篇废话: 跟之前的文章一样,开篇之前,总要写几句废话,大抵也是没啥人看仔细文字,索性我也想到啥就聊啥吧. 这次聊聊疫情,这次全国多地的疫情挺严重的,本人身处深圳,深圳这几日报导都是几十几十的新增病例 ...
- 分布式 PostgreSQL 集群(Citus),官方快速入门教程
多租户应用程序 在本教程中,我们将使用示例广告分析数据集来演示如何使用 Citus 来支持您的多租户应用程序. 注意 本教程假设您已经安装并运行了 Citus. 如果您没有运行 Citus,则可以使用 ...
- MySQL二进制binlog日志说明以及利用binlog日志恢复数据
MySQL的binlog日志对于mysql数据库来说是十分重要的.在数据丢失的紧急情况下,我们往往会想到用binlog日志功能进行数据恢复(定时全量备份+binlog日志恢复增量数据部分). 一.关于 ...
- Java代码块与构造器方法执行顺序
直接上源码: public class Demo4 { { //这里是代码块 System.out.println("这里是代码块"); } static { //这里是静态代码块 ...