当前位置: 首页 > 科技观察

C语言程序设计核心要点,男读无语,女读泪

时间:2023-03-13 22:10:34 科技观察

转载请联系本人。我不想种田公众号。简介作者拥有十多年的C++开发经验。相比之下,我的C经验只有一两年。千页的C++大部头不得不说是非常通俗易懂。C精简的语法集和标准库使我们能够专注于设计等真正重要的事情,而不是迷失在语法的海洋中,这对初学者来说尤为重要。C虽然有抽象不够的缺点,但我更喜欢它的独创性。只需要很少的时间就可以把每个知识点都研究清楚,看任何C源码都不会有语法障碍。每个人都需要建立知识共识Lessisenough,lessismore,lessisbetterthanmore。我教过6个人编程,HTML,JAVA,和C++。最近,我正在教我的孩子编程。他只有十岁。很多人建议我选择Python,但我最终还是选择了C,因为C简单而强大。现在看来,是个不错的选择。TypeC是一种强类型语言,内置了short、long、int、char、float、double等数据类型,类型是贯穿C语言整个课程的核心概念。struct、union、enum属于c的构造类型,用于定义类型和扩展类型系统。变量变量是用来保存数据的,数据是操作的对象,变量字符表示可以在运行时修改。一个变量由类型名+变量名决定。定义变量需要为变量分配内存,可以在定义变量的同时进行初始化。inti;floatf1=0.5,f2=0.8;constantconstinti=100;constchar*p="helloworld";在运行期间是常量和不可变的,它可以在编译期间确定。数组有简单的变量显然是不够的。我们需要一个数组,它模拟了现实中同类型的多个元素。这些对象紧邻,每个元素都可以通过数组名+位置索引来访问。二维、三维和高纬度阵列本质上仍然是线性的。二维数组通过模拟行和列给人一种平面的感觉,实际存储仍然是连续存储的形式。数组是静态的,数组的长度在定义的时候就已经确定了,在运行的时候是不能扩展的,所以有时候我们要分配更多的空间来扩展。无论使用多少数组元素,它们都在那里。有时,我们会使用一个intn来定义数组中实际使用的元素个数。函数函数封装了行为,是模块化的最小单元。函数使逻辑重用成为可能。C是过程化的,可以将现实世界封装成过程(函数),模拟世界通过过程进行连接和编排。在C编程中,行为和数据是分离的。调用函数时,调用者通过参数向函数传递信息,函数通过返回值将结果返回给调用者。最好是函数没有副作用。您应该尽量避免在函数中修改全局变量或静态局部变量。更好的方法是传入参数。这样的函数只是一个逻辑框,符合线程安全的要求。使用变量和函数,您可以编写简单的程序。控制语句分支:if,else,elseif,switchcase,?:loop:while,dowhile,forbreak,continue,goto,defaultstructurebuilt-indatatype不足以描述现实世界,或者使用build-在type描述上不够直白。结构用于模拟复合类型,这使我们能够扩展类型系统。我们将类型组合在一起以构建更复杂的类型,每个组合的组件称为成员变量。结构中的组件,对象通过点(.)运算符访问成员,通过箭头(->)访问指针。指针C的灵魂是指针,指针带来了灵活性,指针的本质是地址。需要区分指针和指针指向的对象。多个指针变量可以指向同一个对象,一个指针不能同时指向多个对象。与指针相关的基本操作包括:赋值(修改指针)、解引用(访问指针指向的对象)、取地址(&变量),指针支持加减法。因为指针变量必须能够覆盖整个内存空间,所以指针变量的长度等于字长,32位系统32位4字节,64位64位8字节系统。指针的含义远比上面的要丰富。指针与数组结合,有指针数组(int*p[n])和数组指针(int(*p)[n]),指针与函数结合,就有函数指针(ret_type)(*pf)(paramlist)),指针和const结合有constchar*/char*const/constchar*const,和指向指针的指针(int**p)。您可以定义指向内置数据类型的指针或指向结构的指针。void*代表一个通用的(universal)指针,不能解引用,也不能进行指针算术运算。函数指针和回调(callback)c源代码编译链接后,将函数转换为可执行程序文件的文本段。当进程启动时,文本段的内容将被加载到进程的代码段中。代码段是c进程内存空间的一部分,所以任何一个c函数都会占用一块内存空间,函数指针就是指向代码段第一行的函数的汇编指令,而函数调用会跳转到函数的第一条指令执行。函数指针常被用作回调函数,C语言也使用包含函数指针成员的结构体来模拟OOP。本质上,它把C++编译器所做的事情转移给了程序员(C++包含虚函数的类建立了一个虚函数表,并在包含虚函数的类对象上附加了一个指向虚函数表的指针)。字符串char*是一种特殊的指针,它被称为c风格的字符串,因为它总是以'\0'结尾,所以要识别一个字符串,一个char*指针就足够了,字符串的长度隐式表示为0,而大多数与字符串相关的STDCAPI都以str开头,例如strlen/strcpy/strcat/strcmp/strtok。内存和内存管理指针提供了C语言直接操作底层内存的能力。C程序区分栈内存和堆内存。堆栈内存是函数内的局部变量。它随着程序的执行而动态扩展,所以不要返回指向临时变量的指针。栈内存容量有限(8/16M),所以一定要避免在函数中创建过大的局部变量,谨防递归栈爆。堆内存也称为动态内存。它由称为动态内存配置器的标准库组件管理。glibc的默认动态内存配置器称为ptmalloc。最初版本存在性能问题,后来通过使用私有线程提高性能解决了竞争。动态内存配置器是介于内核和应用层之间的一个层次。从内核的角度来看,ptmalloc是一个应用程序,从应用层的角度来看,ptmalloc是一个系统库。malloc和free必须配对。这是程序员的责任。丢失对动态分配内存的引用将导致内存泄漏。指向已释放内存块的指针通常称为野指针(悬空指针)。从c源文件到可执行程序的预处理需要经过预处理-编译-汇编-链接多个阶段。预处理阶段被替换、取消和扩展。预处理语句以#开头。宏定义,#define,宏定义可以用\来连接行,#用来生成字符串,##用来拼接,定义宏的时候注意加(),避免运算符优先级干扰,可以用dowhile(0)将定义视为单独的语句,#undef是define的反函数。#if#ifdef#ifndef#else#elif#endif用于条件编译。为了避免重复包含头文件,经常使用#ifndef#define#endif。#include用于头文件包含;#pragma用于行为控制;#error用于在编译过程中输出错误信息。__FILE__、__LINE__、_DATE_、_TIME_、_STDC_和其他标准预定义宏可用于某些调试目的。#typedef用于定义类型别名。例如,typedefintmoney_t;money_t比int更有意义。typedef也可以用来给结构起别名,有时会这样写:typedefstruct{inta;intb;}xyz_t;这样您就可以在定义结构变量时节省一些击键次数。typedef也可以用来重新定义函数指针类型,如typedefvoid(*PF)(inta,intb);PF是函数指针类型,不是函数指针变量。枚举枚举可以增加代码的可读性和可维护性。枚举本质上是int,只是为了让它更有意义。几个取值有限的int值组合在一起,比如定义性别:enumsex{male=1,female};定义的时候可以赋值,比如male=1,后面的值会依次递增1,不赋值则从0开始。union结构体与union(共同体)的区别在于结构体??的每个成员占用不同的内存,相互之间没有影响;虽然union的所有成员都占用相同的内存,但修改一个成员会影响所有剩余的成员。unionu_data{intn;charch;doublef;};其实union本质上就是对一段内存的多种解释,大小以最大的为准。位域(bitfield)structSNField{unsignedcharseq:7;//framesequnceunsignedcharstartbit:1;//indicateifit'sstartingframe1foryes.};节省篇幅,在底层编码,或者网络等编写处理程序的时候用的比较多,注意这个语法特征,跟机器架构有关系。位操作位和&位或|bitinversion~bitXOR^displacement<<>>static,extern,register,volatile,sizeofstatic修饰全局函数,说明在模块(编译单元)中可用,不需要导出全局符号。static修饰的是局部变量,也就是说超过函数调用的生命周期,不入栈,只会初始化一次。extern声明外部变量。register,寄存器变量,建议编译器把变量放在寄存器中。volatile,告诉编译器不要优化,每次从内存中读取,都不优化寄存器。sizeof找到大小,可以作用于变量、类型和表达式变量参数写一个泛型链表,参考:https://www.cnblogs.com/wangzahngjun/p/5556448.htmlOOP:通过定义一个带有函数指针成员变量的结构体,在运行时为该结构体对象设置一个函数指针,模拟runtimebinding来实现类似OOP多态的感觉。GNUCextensionsGNUCextensions不是标准的C,建议用符合标准C的方式写C代码,但是如果你读linux内核代码,你会发现有很多有趣的和看不懂的语法,它来自于GNUC扩展,它确实带来了一些便利。例如,结构成员可以按照定义顺序进行初始化:structtest_t{inta;intb;};structtest_tt1={.b=1,.a=2};例如,可以通过指定索引来初始化数组:inta[5]={[2]5,[4]9};或inta[5]={[2]=4,[4]=9};相当于inta[5]={0,0,4,0,9};或者inta[100]={[0...9]=1,[10...98]=2,3};例如0长度数组structfoo{inti;chara[0];};例如使用变量作为Arraylengthvoidf(intn){chara[n];...}例如caserange,case'A'...'Z'case1...10例如表达式扩展({...}),比如三个Meta-operator扩展...更多扩展请参考:https://my.oschina.net/LinuxDaxingxing/blog/751319