C语言细节总结
来源:互联网 发布:js appendchild的用法 编辑:程序博客网 时间:2024/05/20 03:46
由传智扫地僧C语言提高篇整理而来 传智播客
数据类型 & 变量
- 数据类型的本质: 固定大小内存块的别名。
- 变量名的本质: 一段连续内存空间的别名。通过变量名向内存读写数据,而不是向变量读写数据。
对数据类型取别名: typedef
对变量(内存空间)取别名: C++引用
内存四区模型、函数调用模型(栈)
- 堆区: 动态内存申请与释放。malloc() free()
- 栈区: 自动分配释放,存放函数的参数值,局部变量的值
- 开口可以向上或向下
- 无论栈的开口向上还是向下, 数组buf的增长方向都是向上(地址增大) 栈的生长方向和buf的生长方向是两个概念。在编译时,buf所代表的内存空间编号就已经确定了。
- 全局区(静态区): 全局变量和静态变量。包括全局初始化区、全局未初始化区。常量字符串
- 代码区
指针
- 指针变量和它所指向的内存块是两个不同概念。
- free()后,野指针。释放了所指的内存空间,但指针变量本身没有重置为NULL。
- 指针也是一种数据类型,其类型是所指向的内存空间的数据类型。(解决指针的步长问题,上述第2点)
- 指针的步长,由所指的内存空间的数据类型决定。
- 通过指针间接赋值是指针存在的最大意义。
变量取地址赋给形参,在被调函数中通过 *p 修改变量。- 函数调用时,通过n级指针做形参,修改n-1级指针的值(返回n-1级指针的值!!!)。
在子函数中,将变量的值传递出去,通过一级指针,即一级指针做形参;
在子函数中,将一级指针的值传递出去,通过二级指针,即二级指针做形参;
在子函数中,将二级指针的值传递出去,通过三级指针,即三级指针做形参; - 指针做函数参数,具有输入、输出特性。
指针做输入:主调函数分配内存;指针做输出:被调函数中分配内存。
用指针传递运算结果,return语句用于表示函数运行状态。
- 函数调用时,通过n级指针做形参,修改n-1级指针的值(返回n-1级指针的值!!!)。
- 二级指针做输入三种内存模型
- 指针数组。
- 二维数组。
- malloc()分配内存,存放指针(程序员自己打造内存空间) eg: p = (char **)malloc(sizeof(char *) * num);
- 多级指针避免野指针方法
- 分配成功,memset()置0;
- 释放时,判断指针是否为NULL,非NULL则通过free()释放,释放完后指针置NULL.
- 一般应用禁用malloc/new
- 内存泄漏检测工具: mtrace、memwatch
- 在C语言中,const修饰的变量,通过指针可以修改。(C编译警告,C++编译报错)不同于C++的地方
数组
- 数组名 & 指针步长
- 数组名: 数组首元素的地址; 数组名+1: 第二个元素的地址
- &数组名: 整个数组的地址; &数组名+1: 数组最后一个元素之后的地址
- 数组名是常量指针。Why? 保证数组所占内存能够安全释放。
- 数组做函数形参,本质为指针,在子函数中sizeof(数组名)返回的是指针占用的字节,而非原数组占用的字节。数组名作形参,退化为指针
- 数组类型定义 eg: typedef int (Array)[10];
- 数组指针的定义
- 通过数组类型定义 Array *p;
- 声明一个数组指针类型 eg: typedef int (*p)[10];
- 直接定义 int (*p)[10];
- 多维数组名的本质:数组指针
- 二维数组 a[3][5]
- a+i: 第i行的地址 相当于二级指针
- *(a+i): 第i行首元素的地址 相当于一级指针
- *(a+i)+j: 相当于&a[i][j]
- *( *(a+i)+j) = a[i][j] 或: a[i] = *(a+i); a[i][j] = *(a+i)[j] = *(*(a+i)+j) []结合性:从左到右
- 二维数组 a[3][5]
- 数组和指针的等价关系
- 一维数组 char a[10] —–> 指针 char *a
- 指针数组 char *a[10] —–> 指针的指针 char **a (数组名是首元素的地址,元素为指针)
- 二维数组 char a[10][5] —–> 数组的指针 char (*a)[5] (二维数组可以看作是一维数组,因而看作指针,又因为每个元素是数组,所以是数组指针)
字符串
- C语言没有字符串类型,通过字符数组模拟字符串,以’\0’(0)结尾。
- 字符串的内存分配。堆、栈、数据区。指针和buf的区别
- char buf[5] = “abcd”; // “abcd”为于常量区,buf数组位于栈区,常量区的”abcd”拷贝至buf中。
- char *p = “abcd”; // “abcd”位于常量区,指针指向 ‘a’的地址。
- 字符串相关函数
- strstr() 从母串中查找子串,返回字串第一次出现的位置。
- 递归实现字符串反转
- 参数入栈、出栈,逆序打印
- 递归和全局变量。递归结果存入全局变量。 strncat(dest, src, 1)
- 递归和非全局变量。指针做函数参数。
结构体
- 定义结构体
- 定义类型的同时定义变量
- 匿名类型、定义变量
- 通过类型定义
- 类型重定义
- ‘.’是寻址操作,计算偏移量,在CPU中进行,没有操作内存。
- 结构中嵌套结构体、结构体指针、自身类型的指针
- 结构体中嵌套指针时,深拷贝和浅拷贝问题
- 浅拷贝:仅把指针变量的值拷贝,没有对指针所指的内存空间的数据进行拷贝。
- 深拷贝:显式分配内存进行拷贝。
- 成员的偏移量
定义一个结构体变量,分配内存时,会按成员顺序固定分配。一旦结构体定义下来,结构体中成员的内存布局就定下来了。
链表
- 链表由节点(结构体)构成,每个节点包含数据域和指针域两部分。指针域中存储下一个节点的地址。
eg: 单向链表: 头指针 -> 节点 -> 节点 -> … -> 节点(指针为NULL) - 分类:
- 带头指针 & 不带头指针
- 节点的组织形式: 单向链表、双向链表、循环链表
- 静态链表 & 循环链表
- 传统链表 & 非传统链表(linux内核链表、通用链表)
- 链表算法和数据类型进行分离 (C++中模板类)
- 通用链表将链表节点(区别于业务节点)设置为节点的第一个域,避免了求偏移量
文件操作
- 按照字符读写 fgetc() fputc()
- 按照行读写 fgets() fputs()
- 按照块读写 fread() fwrite()
阅读全文
0 0
- C语言细节总结
- C语言的一些细节总结
- OC语言细节总结
- c语言基础的一些细节总结(1)
- c 语言细节
- c语言细节
- C语言细节问题
- C语言细节
- C语言细节
- C 语言细节
- C语言细节
- C语言细节
- C语言的细节!
- c语言细节知识点
- C语言编程细节
- C语言细节考察
- C语言一些细节
- C语言编译细节
- hibernate加载策略之lazy
- 剑指offer 二叉搜索树以及双向链表
- 1024. 科学计数法 (20)
- Java集合源码学习(四)HashMap分析
- 2016TID敏捷持续集成演讲材料——公开版
- C语言细节总结
- 冒泡排序(C实现)
- [leetcode]67. Add Binary@Java
- Linux配置虚礼机的网络和yum源
- Oracle 索引
- java中equals(),==与hashcode()的区别?
- Netty4启动ServerBootStrap源码分析
- 类字面常量.class
- 一个整数二进制位中1的个数