C语言进阶
臣有一事不知当不当讲
这个作者很懒,什么都没留下…
展开
-
C语言进阶:第39课、程序中栈、堆、静态存储区
程序中的栈: 栈是现代计算机程序里最为重要的概念之一 栈在程序中用于维护函数调用上下文 函数中的参数和局部变量存储在栈上 栈需有后进先出的行为 栈保存了一个函数调用所需的维护信息 参数、返回地址、局部变量、调用上下文。。。 以栈的方式记录活动信息 每次函数调用都对应着一个栈上的活动记录 调用函数的活动记录位于栈的中部 被调函数的活动记录位于栈的顶部 函数调用栈的变化: 通过层级调用,...原创 2018-05-22 23:00:08 · 398 阅读 · 0 评论 -
C语言进阶:第26课、指针的本质分析
程序中的变量只是一段存储空间的别名,那么是不是必须通过这个别名才能访问这段存储空间?*号的意义: 在指针声明时,*号表示所表明的变量为指针; 在指针使用时,*号表示取指针所指向的内存空间中的值。 *号类似于一把钥匙,通过这把钥匙可以打开内存,读取内存中的值。 int i=0; int j=0; int* p=&i; //指针声明 j=*p; //取值划重点:变量p保存着变量...原创 2018-05-21 11:07:48 · 154 阅读 · 0 评论 -
C语言进阶:25、#和##操作符使用分析
#运算符#运算符用在预处理器期间将宏参数转换为字符串#的转换作用是在预处理器完成的,因此只在宏定义中有效编译器不知道#的转换作用用法: #define STRING(x) #x printf("%s\n", STRING(Hello World!)); //注意,并没有出现双引号对于下面代码:#include <stdio.h>#define STRING(x) #x...原创 2018-05-21 10:40:27 · 239 阅读 · 0 评论 -
C语言进阶:24、#pragma使用分析
#pragma用于指示完成一些特定的动作,并且所定义的很多指示字是编译器特有的。#pragma在不同的编译器间是不可移植的: ——预处理器将忽略它不认识的#pragma指令 ——不同的编译器可能以不同的方式解释一条#pragma指令。一般用法: #pragma parameter 注:不同的parameter参数语法和意义各不相同message参数在大多数的编译器中都有相似的实现;message...原创 2018-05-21 10:14:38 · 268 阅读 · 0 评论 -
C语言进阶:23、#error和#line的用法
#error用于生成一个编译错误消息用法: #error message message不需要用双引号包围#error编译指示字用于自定义程序员特有的编译错误消息,类似的,#warning用于生成编译警告。#error是一种预编译指示字,可用于提示编译条件是否满足#ifndef __cpluscplus //C++内置的宏 通过检测这个宏的存在,来进行错误提醒。#error This ...原创 2018-05-21 09:49:43 · 1814 阅读 · 1 评论 -
C语言进阶:22、条件编译使用分析
条件编译:同一份代码,产生不同的产品基本概念:条件编译的行为类似于C语言中的if...else... 条件编译是预编译指令命令,用于控制是否编译某段代码。#include <stdio.h>#define C 1int main(){ #if( 1 == C ) printf("this is first printf...\n"); #e...原创 2018-05-20 19:44:32 · 247 阅读 · 0 评论 -
C语言进阶:21、宏定义与使用分析
#define是预处理器处理的单元实体之一;#define定义的宏可以出现在程序的任意位置;#define定义之后的代码都可以使用这个宏。#define定义的宏常量可以直接使用;#define定义的宏常量本质为字面量(不占用内存<只读存储区>—和const常量(变量,占内存)的本质区别)。下面的宏定义正确吗?#define error -1 //#def...原创 2018-05-20 17:05:07 · 281 阅读 · 0 评论 -
C语言进阶48: 函数设计原则
函数设计原则: 函数在意义上应该是一个独立的功能模块 函数名要在一定程度上反应函数的功能 函数参数名要能够体现参数的意义 尽量避免在函数中使用全局变量!函数应该是无状态的,全局变量在函数体中是不受欢迎的。 void sc(char* s1, char* s2); void str_copy(char* str_dest, char* str_src);划重点: 当函数...原创 2018-05-20 16:34:39 · 240 阅读 · 0 评论 -
C语言进阶47:递归函数分析
递归是一种数学上分而治之的思想递归需要有边界条件 当边界条件不满足时,递归继续进行 当边界条件满足时,递归停止 递归将大型复杂问题转化为与原问题相同递归函数: 函数体内部可以调用自己递归函数中存在自我调用的函数递归函数是递归的数学思想在程序设计中的应用 递归函数必须有递归出口 函数的无限递归将导致程序栈溢出而崩溃计算字符串的长度:int strlen_r(const char* s){ if(...原创 2018-05-20 15:48:58 · 274 阅读 · 0 评论 -
C语言进阶46:函数和宏分析
宏除了定义常量,还可以定义代码块宏是由预处理器直接替换展开的,编译器不知道宏的存在函数是由编译器直接编译的实体,调用行为由编译器决定多次使用宏会导致可执行程序的体积增大函数是跳转执行的,内存中只有一份函数体存在(也就意味着函数调用有多余的开销)宏的效率比函数要高,因为是直接展开,无调用开销函数调用时会创建活动记录,效率不如宏#include <stdio.h>#define RES...原创 2018-05-20 15:09:50 · 208 阅读 · 0 评论 -
C语言进阶:第27课、数组的本质分析
数组是相同类型的变量的有序集合。int a[5]; // 5个int类型数据,每个元素都是int型数据;a代表数组第一个元素的起始地址。这20个字节空间的名字 为a,a[0]、a[1]等都是a中的元素,并非元素的名字。数组中的元素没有名字。数组的大小: 数组在一片连续的内存空间中存储元素 数组元素的个数可以显式或隐式指定int main(){ int a[...原创 2018-05-21 11:43:05 · 149 阅读 · 0 评论 -
C语言进阶:第28课:指针和数组分析(上)
数组的本质: 数组是一段连续的内存空间 数组的空间大小为sizeof(array_type)"array_size 数组名可以看做指向数组第一个元素的常量指针。 问题: a+1的意义是什么?结果是什么? 指针运算的意义是什么?结果是什么?int main(){ int a[5] = {1, 2, 3, 4, 5}; int* p = NULL; ...原创 2018-05-21 11:54:27 · 210 阅读 · 0 评论 -
C语言进阶:第29课:指针和数组分析(下)
数组名可以当做常量指针使用,那么指针是否也可以当做数组名来使用呢?数组的访问方式: 以下标的形式访问数组中的元素; 以指针的形式访问数组中的元素;指针以固定增量在数组中移动时,效率高于下标形式,指针增量为1且硬件具有增量模型时,效率更高。下标形式与指针形式的转换:a[n] <---> *(a+n) <---> *(n+a) <---> n[a]#include ...原创 2018-05-21 17:25:13 · 170 阅读 · 0 评论 -
C语言进阶:第38课、动态内存分配
动态内存分配的意义: C语言中的一切操作都是基于内存的变量和数组等都是内存的别名 内存分配由编译器在编译期间决定 定义数组的时候必须指定数组长度 数组长度是在编译期就必须确定的需求:程序运行过程中,可能需要使用一些额外的内存空间 malloc 和 free 用于执行动态内存分配和释放 malloc所分配的是一块连续的内存 malloc以字节为单位,并且不带任何的类型信息 free用于将动态内存归...原创 2018-05-22 22:18:42 · 185 阅读 · 0 评论 -
C语言进阶:第37课:指针阅读技巧分析
下面的标识符代表是什么含义? int (*p)(int) int (*p1)(int*, int (*f)(int*)); int (*p2[5])(int*); int (*(*p3)[5])(int*); int* (*(*p4)(int*))(int*); int (*(*p5)(int*))[5];右左法则: 从最里层的圆括号中未定义的标识符看起 首先往右看,再往左...原创 2018-05-22 18:12:10 · 178 阅读 · 0 评论 -
C语言进阶:第36课、函数指针与回调函数
C语言中的函数有自己特定的类型函数的类型由返回值,参数类型和参数个数共同决定:int add(int, int)的类型为int(int , int )C语言通过typedef为函数类型重命名:typedef type name(parameter list)例: typedef int f(int, int) typedef void p(int)函数指针: 函数指针用于指向一个函数 函数名是执...原创 2018-05-22 16:30:23 · 204 阅读 · 0 评论 -
C语言进阶:第35课、数组参数和指针参数分析
为什么C语言中的数组参数会退化为指针?退化的意义: C语言中只会以值拷贝的方式传递参数 当向函数传递数组时: ??将整个数组拷贝一份传入函数(排除) 将数组名看做常量指针传数组首元素地址C语言以高效作为最初设计目标: 1.参数传递的时候如果拷贝整个数组执行效率会大大下降 2.参数位于栈上,太大的数组会导致栈溢出。(函数调用时,参数总是存储在栈上)二维数组参数同样存在退化问题: 二维数组可以看做...原创 2018-05-22 15:50:41 · 259 阅读 · 0 评论 -
C语言进阶:第34课:多维数组和多维指针(难点)
指向指针的指针指针的本质是变量指针会占用一定的内存空间可以定义指针的指针来保存指针变量的地址值。int main(){ int i=0; int* p = NULL; int** pp = NULL; pp = &p; *pp = &i;}为什么需要指向指针的指针? 指针在本质上也是变量 对于指针也同样存在传值调用和传址调用。#include <std...原创 2018-05-22 14:48:37 · 234 阅读 · 0 评论 -
C语言进阶:第33课、mian函数与命令行参数
C语言中main函数称为主函数,一个C程序是从main()函数开始执行。下面的main()函数定义正确吗:上面四种写法都是正确的!main()函数是操作系统调用的函数操作系统总是将main()函数作为应用程序的开始操作系统将main()函数的返回值作为程序的退出状态(return 0 的原因)思考:为什么C编译器支持那么多不同的main()函数原型???return num;num如果是0以外的其...原创 2018-05-22 00:02:37 · 399 阅读 · 0 评论 -
C语言进阶:第32课:数组指针和指针数组分析
下面的声明合法吗? int array[5]; int matrix[3][3]; int* pa = array; int* pm = matrix;array代表数组首元素的地址,那么matrix代表什么?array和&array的地址值相同,但是意义不同,那么他们所代表的类型相同吗?C语言中的数组有自己特定的类型: 数组的类型由【元素类型】和【数组大小】共同决定。 int ar...原创 2018-05-21 23:03:18 · 241 阅读 · 1 评论 -
C语言进阶:第31课:字符串典型问题分析
下面的程序输出什么,为什么? char buf[10] = {0}; char src[] = "hello %s"; snprintf(buf, sizeof(buf), src); printf("buf = %s\n", buf);编译运行:~/will$ gcc test.ctest.c: In function ‘main’:test.c:9: warning: for...原创 2018-05-21 22:03:24 · 206 阅读 · 0 评论 -
C语言进阶:第30课:C语言中的字符串
字符串的概念:字符串是有序字符的集合;字符串是程序中的基本元素之一;C语言中没有字符串的概念: C语言中通过特殊的字符数组模拟字符串; C语言中的字符串是以"\0"结尾的字符数组。C语言中,双引号引用的单个或多个字符是一种特殊的字面量 存储于程序的全局只读存储区;//被编译后存储于只读存储区 本质为字符数组,编译器自动在结尾加上"\0"字符。字符串字面量的本质是数组——不能被改变。#include...原创 2018-05-21 21:38:19 · 219 阅读 · 0 评论 -
C语言进阶45:函数参数的秘密《下》——调用约定和可变参数程序
函数参数的计算次序是依赖于编译器实现的,那么函数参数的入栈次序是如何确定的呢?strcpy(s, "willwilling");哪个参数先入栈呢?——s先入栈。调用约定: 参数会传递给被调用的函数 返回值会被返回给函数调用者调用约定描述参数如何传递到栈中以及栈的维护方式 参数传递顺序 调用栈清理调用约定是编译器的一部分,且各个编译器的调用约定并不相同。调用约定是预定义的,可理解为调用协议调用阅读通...原创 2018-05-20 12:24:33 · 288 阅读 · 0 评论 -
C语言进阶44:函数参数的秘密《上》——参数访问顺序和程序点
函数参数在本质上与局部变量相同,都是在栈上分配空间函数参数的初始值是函数调用时的实参值观察下面代码,判断输出: int k=1; printf("%d, %d\n", k++, k++); (划重点) printf("%d\n", k);编译运行后输出结果:~/will$ ./a.out2, 13所以我们必须注意,函数参数的求值顺序依赖于编译器的实现不能假...原创 2018-05-20 12:05:39 · 406 阅读 · 0 评论 -
C语言进阶43:函数的意义——模块化设计和extern
函数的由来: 程序 = 算法 + 数据 C程序 = 数据 + 函数面向过程的程序设计: 面向过程是一种以过程为中心的编程思想 首先将复杂的问题分解为一个个容易解决的问题 分解过后的问题可以按照步骤一步步完成 函数是面向过程在C语言中的实现 解决问题的每个步骤可以用函数实现函数声明与函数定义: 声明的意义在于告诉编译器程序单元的存在 定义则明确指示程序单元的意义 C语言中通过extern进行...原创 2018-05-20 11:12:16 · 465 阅读 · 0 评论 -
C语言进阶:10、struct和union分析
C语言中的struct可以看做变量的集合: 问题:空结构体占多大内存?#include<stdio.h>struct TS{};int main(){ struct TS t1; struct TS t2; printf("sizeof(TS) = %d\n", sizeof(struct TS)); printf("sizeof(t1) = %d, ...原创 2018-05-04 10:10:57 · 164 阅读 · 0 评论 -
C语言进阶:9、const和volatile
const只读变量。const修饰的变量是可读的,本质上是变量。const修饰的局部变量在栈上分配空间;const修饰的全局变量在全局数据区分配空间;const只在编译期有用,在运行期无用。所以,本质上来讲,const修饰的其实不是常量,而是一个不能被改变的变量(不能出现在赋值符号的左边)。在现代编译器中,修改const全局变量将导致程序崩溃。注意:标准C语言编译器不会将const修饰的全局变量存...原创 2018-05-01 19:16:33 · 120 阅读 · 0 评论 -
C语言进阶:8、goto和void分析
禁用goto:#include<stdio.h>#include<malloc.h>void f1(int n){ int* p=NULL; if(n<0) { goto STATUS; } p = (int*)malloc(sizeof(int)*n); //goto之后,改行代码不会被执行 STATUS: p[0] = n;...原创 2018-05-01 16:32:58 · 172 阅读 · 0 评论 -
C语言进阶:7、循环语句
循环语句的基本工作方式: 通过条件表达式判定是否执行循环体; 条件表达式遵循if语句表达式的原则;do、while、for语句的区别: do语句先执行再判断,循环体至少执行一次; while语句先判断后执行,循环体可能不执行; for语句先判断再执行,相比while更加简洁。do...while的循环方式: do { //loop...原创 2018-05-01 15:53:25 · 354 阅读 · 0 评论 -
C语言进阶:6、分支语句
if语句分析:if语句用于根据条件选择执行语句,if语句可以单独出现,else语句不能独立存在且总与与它最近的if相匹配else语句后可以接其他if语句。if语句中零值比较的注意点:1、bool变量应该直接进行判断,不需要进行比较bool b = TRUE;if(b){ //statement1}else{ //statement2}2、变量与0进行比较时,0值应该出现在比较符号的...原创 2018-05-01 14:55:03 · 172 阅读 · 0 评论 -
C语言进阶:5、变量属性
C语言中的变量可以有自己的属性,只需在定义变量的时候加上“属性”关键字即可。“属性”关键字指明变量的特有属性。1、auto关键字:auto是C语言中局部变量的默认属性,表明被修饰的变量存储在栈上。编译器默认所有的局部变量都是auto属性。2、register关键字:表明局部变量存储在寄存器中,要注意的是,这只是请求寄存器变量,但是不一定成功。register变量必须是CPU寄存器可以接受的值。不能...原创 2018-05-01 14:34:12 · 268 阅读 · 0 评论 -
C语言进阶:4、类型转换
类型转换分为:强制类型转换和隐式类型转换那么,如何选择转换的方式以及需要注意哪些问题呢?强制类型转换的语法:(Type)var_name;(Type)value;强制类型转换的结果:1、目标类型可以容纳目标值,那么结果将不变;2、目标类型不能容纳目标值,那么结果将会出现截断。而且,必须注意的是,不是所有的强制类型转换都能成功。示例:观察下面代码#include<stdio.h>s...原创 2018-05-01 11:35:33 · 178 阅读 · 0 评论 -
C语言进阶:3、浮点数的秘密
浮点数在内存中的存储方式为: 符号位、指数、尾数。float和double类型的数据在计算机内部的表示法是相同的,但是由于所占的内存空间不同,其分别能够表示的数值范围和精度不同。浮点数的转换: 1、将浮点数转换成二进制; 2、用科学计数法表示二进制浮点数; 3、计算指数偏移后的值。注意:计算指数的时候需要加上偏移量,而偏移量的值与类型有关。float类型: ...原创 2018-05-01 10:50:03 · 255 阅读 · 0 评论 -
C语言进阶:1、基本数据类型
基本数据类型 在C语言中,我们讲数据类型,本质上是将一段“定义好长度”的内存片段重新命名。所以,基本数据类型就像是一个“模子”,当我们需要不同长度的内存片段时,就可以使用基本数据类型来申请内存。变量的本质:1、变量其实就是一段可以被重复存储内容的内存空间;2、程序中通过变量来申请并命名这段空间;3、通过自定义的变量名来使用这段内存。值得注意的是,指针存储的内容是地址,也是一种对内存空间的描述...原创 2018-04-30 22:14:59 · 152 阅读 · 0 评论 -
C语言进阶:11、enum、sizeof、typedef分析
enum是C语言中的一种自定义类型: enum值是可以根据需要自定义的整型值; 【枚举值是无法改变的】 第一个自定义的enum值默认为0; 默认情况下的enum值是在前一个定义值的基础上加1; enum类型的变量只能取定义时的离散值; enum Color { RED; BLUE=2; GREEN ...原创 2018-05-04 10:50:07 · 219 阅读 · 0 评论 -
C语言进阶:12、注释符号
注释重要吗?请观察下面代码,判断下面的注释是否正确?int main(){ int/*...*/i; //right 空格替换 char* s = "abcdefg //ijklmn"; //right // Is it a \ //right 换行符 valid comment? //right in/*...*/t j; ...原创 2018-05-04 11:47:11 · 176 阅读 · 0 评论 -
C语言进阶:13、接续符和转义符
C语言中的接续符(\)是指示编译器行为的利器。编译器会在反斜杠(\)剔除,跟在反斜杠后面的字符自动接续到前一行;在连接单词时,反斜杠之后不能有空格,反斜杠的下一行之前也不能有空格;(会报错)backslash and newline separated by space // warning 在接续符后多加了一个空格接续符适合在定义宏代码块时使用。源代码:#include <stdio....原创 2018-05-04 15:02:31 · 313 阅读 · 0 评论 -
C语言进阶42:内存操作经典问题《二》——常见内存错误与malloc()和free()函数
常见内存错误: 结构体指针未初始化 *** 结构体成员指针未分配足够的内存 内存分配成功但未初始化 *** 内存操作越界动态内存申请之后,应该立即检查指针值是否为NULL,防止使用NULL指针:int* p = (int*) malloc(56);if ( p != NULL){ //do something...}free(p);free指针之后必须立即赋值为NULL:in...原创 2018-05-19 16:32:22 · 284 阅读 · 0 评论 -
C语言进阶41:内存操作经典问题《一》——野指针
野指针: 指针变量中的值是非法的内存地址,进而形成野指针 野指针不是NULL指针,是指向不可用内存地址的指针 NULL指针并不危害,很好判断,也很好调试 C语言中无法判断一个指针所保存的地址是否合法一个指针指向malloc申请的内存地址,其实指针是有效的,但是调用free后,这个地址将返还给内存,这个地址将不可用指针将变成野指针。野指针的由来: 局部指针变量没有被初始化(指针变量定义的时候应该被初...原创 2018-05-19 16:17:36 · 308 阅读 · 0 评论 -
40、程序中的内存布局
先看一张示意图:不同代码在可执行程序中的对应关系 程序和进程的不同: 程序是 静态的 概念,表现形式为一个可执行文件 进程是 动态的 概念,程序由操作系统加载运行后得到进程 每个程序可以对应多个进程 每个进程只能对应一个程序问题:包含脚本代码的文本文件是一种类型的可执行程序吗?如果是,对应什么样的进程呢?脚本程序是一种可执行程序,但是它需要操作系统加载脚本解释程序来生成进程,然后进程...原创 2018-05-19 15:12:31 · 1001 阅读 · 1 评论