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语言杂谈的更多相关文章

  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. 使提示框居中显示&自定义提示框

    ToastActivity.java文件: 1 public class ToastActivity extends AppCompatActivity { 2 private Button mbtn ...

  2. Docker-可视化管理工具总结-推荐使用Portainer

    对于初学docker的小白,一款好的可视化工具有助于快速掌握docker基本形态和概念,下面针对docker可视化工具做些总结 ui-for-docker UI For Docker是一个使用Dock ...

  3. Python:GUI库tkinter(三)

    这一章是对前两章的总结: Python:GUI库tkinter(一) Python:GUI库tkinter(二) 前两章是对控件的介绍,第一章可以知道各控件使用时的具体参数,第二章以具体的例子展示了每 ...

  4. 面向对象编程(C++篇2)——构造

    目录 1. 引述 2. 详述 2.1. 数据类型初始化 2.2. 类初始化 1. 引述 在C++中,学习类的第一课往往就是构造函数.根据构造函数的定义,构造函数式是用于初始化类对象的数据成员的.无论何 ...

  5. ElasticSearch 文档(document)内部机制详解

    1.数据路由 1.1 文档存储怎么路由到相应分片? 一个文档,最终会落在主分片的一个分片上,到底应该在哪一个分片?这就是数据路由. 1.2 路由算法 shard = hash(routing) % n ...

  6. mysql常用索引

    1.索引 在关系数据库中,索引是一种单独的.物理的对数据库表中一列或多列的值进行排序的一种存储结构,它是某个表中一列或若干列值的集合和相应的指向表中物理标识这些值的数据页的逻辑指针清单.索引的作用相当 ...

  7. 解决vue单页面应用做微信JSSDK注入权限时出现“invalid signature”(ios端)

    --都说微信开发多坑,没想到遇到一个天坑. 在做一个vue项目时,要用到微信JS-SDK,官方文档详见:https://developers.weixin.qq.com/doc/offiaccount ...

  8. vue3-关于$props,$parents等引用元素和组件的注意事项

    同一个组件内可以使用,但是在不同的组件内,不要用$parents或$refs来访问另一个组件内的数据, 这会使代码的耦合性变高,同时也会让代码的可读性变差, 在不同组件访问数据时,使用props等来传 ...

  9. pycharm实用常用快捷键

    我们在实用pycharm时候,可以使用一些快捷键来帮助我们写代码. 1 alt + enter 快速导包,在需要导入包时候可以使用这个快捷键: 2 alt + 1 可以快速打开或者关闭左侧projec ...

  10. ssh-ssl编译安装

    升级前准备 #下载所需依赖包#yum install -y gcc gcc-c++ glibc make autoconf openssl openssl-devel pcre-devel pam-d ...