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. 1 struct
  2. 2 {
  3. 3 int a;
  4. 4 short b;
  5. 5 }A;
  6. 6 //对齐单位为 4 个字节(结构体A中最大的类型为int(4个字节))
  7. 7 //偏移量:
  8. 8 a: 4*0 = 0
  9. 9 b: 2*2 = 4
  10. 10
  1. 11 sizeof(A) = 8;
  1. 1 struct
  2. 2 {
  3. 3 int a;
  4. 4 char b;
  5. 5 short c;
  6. 6 }A;
  7. 7 //对齐单位 4 个字节
  8. 8 sizeof(A) = 8;
  9. 9 a: 4*0 = 0 //偏移0个字节
  10. 10 b: 1*4 = 4 //偏移4个字节
  11. 11 c: 2*3 = 6
  1.  

原则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. 1 struct A
  2. 2 {
  3. 3 int a;
  4. 4 double b;
  5. 5 float c;
  6. 6 };
  7. 7
  8. 8 struct
  9. 9 {
  10. 10 char e[2];
  11. 11 int f;
  12. 12 double g;
  13. 13 short h;
  14. 14 struct A i;
  15. 15 }B;
  16. 16 //对齐单位 8 个字节
  17. 17 sizeof(B) = 48;
  18. 18 //普通成员偏移量
  19. 19 e: 1*0 = 0
  20. 20 f: 4*1 = 4
  21. 21 g: 8*1 = 8
  22. 22 h: 2*8 = 16
  23. 23 结构体起点坐标:
  24. 24 8*3 = 24
  25. 25 //结构体成员偏移量
  26. 26 a: 24 + 4*0 = 24
  27. 27 b: 24 + 8*1 = 32
  28. 28 c: 24 + 4*4 = 40

原则3:收尾工作

结构体的总大小,也就是sizeof的结果,必须是其内部最大成员的整数倍,不足的要补齐。

原则4:指定对齐单位

  1. 1 #pragma pack(2) //指定对齐单位为2个字节
  2. 2 typedef struct
  3. 3 {
  4. 4 int a;
  5. 5 char b;
  6. 6 short c;
  7. 7 char d;
  8. 8 }A;
  9. 9 a: 2*0 = 0
  10. 10 b: 1*4 = 4
  11. 11 c: 2*3 = 6
  12. 12 d: 1*8 = 8
  13. 13 sizeof(A) = 10;

有关不完整类型的字节对齐

使用位域的主要目的是压缩存储,其大致规则为:

1) 一个位域必须存储在同一个字节中,不能跨两个字节。如一个字节所剩空间不够存放另一位域时,应从下一单元起存放该位域。

2) 如果相邻位域字段的类型相同,且其位宽之和小于类型的sizeof大小,则后面的字段将紧邻前一个字段存储,直到不能容纳为止;

3) 如果相邻位域字段的类型相同,但其位宽之和大于类型的sizeof大小,则后面的字段将从新的存储单元开始,其偏移量为其类型大小的整数倍;

4) 如果相邻的位域字段的类型不同,则各编译器的具体实现有差异,VC6采取不压缩方式,Dev-C++和gcc采取压缩方式;

5) 如果位域字段之间穿插着非位域字段,则不进行压缩;

6) 整个结构体的总大小为最宽基本类型成员大小的整数倍。

  1. 1 //以 4 字节对齐
  2. 2 struct A
  3. 3 {
  4. 4 int a1:5;
  5. 5 int a2:9;
  6. 6 char c;
  7. 7 int b:4;
  8. 8 short s;
  9. 9 }B;
  10. 10
  11. 11 sizeof(B) = 16
  12. 12
  13. 13 a1 + a2 = 14 位,少于 32位,4字节
  14. 14 a1+a2存放在一起:4*0 = 0
  15. 15 c: 1 * 4 = 4
  16. 16 b: 4 * 2 = 8 //所占的大小为该结构体成员内部最大元素的整数倍(4对齐),不足补齐。
  17. 17 s: 2 * 6 = 12
  1. 1 struct A
  2. 2 {
  3. 3 int a1 : 5;
  4. 4 int a2 : 9;
  5. 5 char c;
  6. 6 int b : 4;
  7. 7 short s;
  8. 8 }B;
  9. 9 sizeof(B) = 12;
  10. 10
  11. 11 a1+a2 = 14 (小于32位,4字节)
  12. 12 a1+a2: 2 * 0 =0;
  13. 13 c: 1 * 4 = 4;
  14. 14 b: 2 * 3 = 6; //4超过2字节,以2字节为单位
  15. 15 s2 * 5 = 10

C语言杂谈的更多相关文章

  1. C语言杂谈(三)存储类别

    本文讨论C语言中的存储类别,包括数据在内存的存储.变量的存储类别.函数的存储类别.生存周期.下图为计算机的存储空间,有寄存器和内存. 一.存储区域 1.寄存器:存放立即参加运算的数据. 2.系统区:存 ...

  2. C语言杂谈(二)自增运算符++与间接访问运算符*的结合关系和应用模式

    自增运算符++有前缀和后缀两种,在搭配间接访问运算符*时,因为顺序.括号和结合关系的影响,很容易让人产生误解,产生错误的结果,这篇文章来详细分析一下这几种运算符的不同搭配情况. ++.--和*的优先级 ...

  3. C语言杂谈(一)scanf()、scanf_s()与错误 C4996

    错误 C4996 初学C语言时,第一个接触到的I/O函数便是scanf()了.但在高版本的 Visual Studio (包括但不限于2015.2013.2012)编译代码时,却会出现意想不到的错误. ...

  4. C++ 的语言杂谈(一)--C++不是新手友好的

    C++的语言品味是独特的,喜欢的人特别喜欢,讨厌的人特别讨厌.虽然Bjane Stroustrup不断地宣称C++的发展方向是新手友好的,但实际上对新手来说,最重要的还是有强大方便的标准库可以使用(像 ...

  5. shell、cmd、dos和脚本语言杂谈(转)

    问题一:DOS与windows中cmd区别   在windows系统中,“开始-运行-cmd”可以打开“cmd.exe”,进行命令行操作. 操作系统可以分成核心(kernel)和Shell(外壳)两部 ...

  6. _杂谈_C语言历史

    早期的操作系统软件主要是用汇编语言(包括UNIX操作系统在内)编写的.由于汇编语言依赖于计算机硬件,所以程序的可读性和可移植性都比较差,所以呢,为了提高操作系统软件的可读性和可移植性,最好改用高级语言 ...

  7. 杂谈PID控制算法——最终篇:C语言实现51单片机中的PID算法

    真遗憾,第二篇章没能够发表到首页上去.趁热打铁.把最终篇——代码篇给发上来. 代码的设计思想请移步前两篇文章 //pid.h #ifndef __PID__ #define __PID__ /*PID ...

  8. Linux杂谈: 实现一种简单实用的线程池(C语言)

    基本功能 1. 实现一个线程的队列,队列中的线程启动后不再释放: 2. 没有任务执行时,线程处于pending状态,等待唤醒,不占cpu: 3. 当有任务需要执行时,从线程队列中取出一个线程执行任务: ...

  9. 【转】PHP 杂谈《重构-改善既有代码的设计》之一 重新组织你的函数

    原文地址: PHP 杂谈<重构-改善既有代码的设计>之一 重新组织你的函数 思维导图   点击下图,可以看大图.    介绍   我把我比较喜欢的和比较关注的地方写下来和大家分享.上次我写 ...

随机推荐

  1. 设计模式(一) 灵活的javaScript语言

    首先先看几个函数: function checkName () {){}// 验证姓名 function checkEmail() {} // 验证邮箱 function checkPassword( ...

  2. [python][nginx][https] Nginx 服务器 SSL 证书安装部署

    目录 前言 1 申请证书 2 Nginx 服务器 SSL 证书安装部署 2.1.准备 Nginx 环境 2.2 证书部署 2.3 Nginx 配置 3 最后 参考链接 前言 博主博客中的图片,使用的是 ...

  3. DeepFaceLab:NVML Shared Library Not Found 解决办法

    解决办法如下 A.出现这个提示的原因是安装后的Nvidia显卡目录C:\Program Files\NVIDIA Corporation\NVSMI不存在,造成的. B.搜索文件Nvidia-SMI. ...

  4. MongoDB数据库的下载安装及配置方法

    MongoDB安装与配置步骤 MongoDB数据库之安装篇 # 1 下载MongoDB数据库 1.打开浏览器,登录"https://www.mongodb.com/try/download/ ...

  5. Vue基础知识的巩固与学习

    生命周期的解析和应用: Vue 实例有一个完整的生命周期,也就是从开始创建.初始化数据.编译模板.挂载Dom→渲染.更新→渲染.卸载等一系列过程,我们称这是 Vue 的生命周期.通俗说就是 Vue 实 ...

  6. jq 页面添加布局

    要求: 1)实现如上图页面布局(10分) 2)实现选择城市所添加的内容追加到你喜欢哪个城市下(10分) 3)实现选择游戏所添加的内容追加到你喜欢哪款游戏下(10分) 4)新增的数据字体颜色设置为粉色( ...

  7. Applied Social Network Analysis in Python 相关笔记

  8. CF1534F1题解

    首先,对于一个在第 \(i\) 行 \(j\) 列的沙子,如果他开始下降,他能够使哪些沙子下降呢? 很容易得知是第 \(j-1,j,j+1\) 列所有行号不小于 \(i\) 的沙子. 对于沙子 \(u ...

  9. test 分支强制替换master 分支的办法

    test分支改动太多,并且master 分支好久没有改动.直接合并到master 分支的话,会产生很多冲突,几十个文件,修复冲突会花很多时间,并且是没有意义的.因此只能使用test 分支强制替换. 代 ...

  10. Objects.equals有坑

    前言 最近review别人代码的时候,发现有个同事,在某个业务场景下,使用Objects.equals方法判断两个值相等时,返回了跟预期不一致的结果,引起了我的兴趣. 原本以为判断结果会返回true的 ...