摘要:C语言作为编程的入门语言,学习者如何快速掌握其核心知识点,面对茫茫书海,似乎有点迷茫。为了让各位快速地掌握C语言的知识内容,在这里对相关的知识点进行了归纳。

引言

笔者有十余年的C++开发经验,相比而言,我的C经验只有一两年,C比较简单,简单到《The C Programming Language》(C程序设计语言)只有区区的200多页,相比上千页的C++大部头,不得不说真的很人性化了。

C语言精简的语法集和标准库,让我们可以把精力集中到设计等真正重要的事情上来,而不是迷失在语法的海洋里,这对于初学者尤其重要。虽然C语言有抽象不足的缺点,但我更喜欢它的精巧,只需要花少量的时间,研究清楚它每一个知识点,看任何C源码就不会存在语法上的障碍,大家需要建立的知识共识足够少,少即是多,少好于多。

我教过6个人编程,教过HTML,教过JAVA,也教过C++。最近,我在教我小孩编程,他只有十岁,很多人建议我选择Python,但我最终选择了C语言,因为C语言简单且强大,现在看来,好像是个不错的选择。

类型

C是强类型语言,有short、long、int、char、float、double等build-in数据类型,类型是贯穿c语言整个课程的核心概念。

struct、union、enum属于c的构造类型,用于自定义类型,扩充类型系统。

变量

变量用来保存数据,数据是操作的对象,变量的变字意味着它可以在运行时被修改。

变量由类型名+变量名决定,定义变量需要为变量分配内存,可以在定义变量的同时做初始化。

int i;

float f1 = 0.5, f2= 0.8;

常量

const int i = 100;

const char* p = "hello world";

运行中恒定、不可变,编译期便可确定。

数组

光有简单变量显然不够,我们需要数组,它模拟现实中相同类型的多个元素,这些对象是紧密相邻的,通过数组名+位置索引便能访问每个元素。

二维、三维、高纬数组本质上还是线性的,二维数组通过模拟行列给人平面的感觉,实际存储上还是连续内存的方式。

数组是静态的,在定义的时候,数组的长度就已经确认,运行中无法伸缩,所以有时候我们不得不为应付扩充多分配一些空间。数组元素不管用多用少,它都在哪里,有时候,我们会用一个int n去界定数组实际被使用的元素个数。

函数

函数封装行为,是模块化的最小单元,函数使得逻辑复用变得可能。

C语言是过程式的,现实世界都可以封装为一个个过程(函数),通过过程串联和编排模拟世界。

用C语言编程,行为和数据是分离的。调用函数的时候,调用者通过参数向函数传递信息,函数通过返回值向调用者反馈结果。

函数最好是无副作用的,函数内应该尽量避免修改全局变量或者静态局部变量,更好的方式是通过参数传递进来,这样的函数只是逻辑的盒子,它满足线程安全的要求。

有了变量和函数,就可以编写简单的程序了。

控制语句

  • 分支:if 、else、else if、switch case、?:
  • 循环:while、do while、for
  • break、continue、goto、default

结构体

build-in数据类型不足以描绘现实世界,或者用build-in类型描述不够直接,结构体用来模拟复合类型,它赋予了我们扩充类型系统的能力,我们把类型组合到一起构建更复杂的类型,而每个被组合的成分就叫成员变量。

结构体内的成分,对象通过点(.)运算符,指针通过箭头(->)访问成员。

指针

C语言的灵魂是指针,指针带来弹性,指针的本质是地址。

需要区分指针和指针指向的对象,多个指针变量可指向同一个对象,一个指针不能同时指向多个对象。

指针相关的基本操作包括:赋值(修改指针指向),解引用(访问指针指向的对象),取地址(&variable),指针支持加减运算。

因为指针变量要能覆盖整个内存空间,所以指针变量的长度等于字长,32位系统下32位4字节,64位系统下64位8字节。

指针的含义远比上述丰富,指针跟数组结合便有了指针数组(int* p[n])和数组指针(int (*p)[n]),指针跟函数结合便有了函数指针(ret_type (*pf)(param list)),指针跟const结合便有了const char*/char* const/const char* const,还有指向指针的指针(int **p)。

既可以定义指向build-in数据类型的指针,也可以定义指向struct的指针,void*表示通用(万能)指针,它不能被解引用,也不能做指针算术运算。

函数指针与回调(callback)

c source code被编译链接后,函数被转换到可执行程序文件的text节,进程启动的时候,会把text节的内容装载到进程的代码段,代码段是c进程内存空间的一部分,所以任何c函数都会占一块内存空间,函数指针就是指向函数在代码段的第一行汇编指令,函数调用就会跳转到函数的第一个指令处执行。

函数指针经常被用来作为回调(callback),c语言也会用包含函数指针成员的结构体模拟OOP,本质上是把C++编译器做的事情,转给程序员来做(C++为包含虚函数的类构建虚函数表,为包含虚函数的类对象附加虚函数表的指针)。

字符串

char*是一类特殊的指针,它被称为c风格字符串,因为它总是以‘\0’作为结尾的标识,所以要标识一个字符串,有一个char*指针就够了,字符串的长度被0隐式指出,跟字符串相关的STD C API大多以str打头,比如strlen/strcpy/strcat/strcmp/strtok。

内存和内存管理

指针提供了c语言直接操作底层内存的能力,c程序区分栈内存和堆内存,栈内存是函数内的局部变量,它随程序执行而动态伸缩,所以不要返回临时变量的指针,栈内存容量有限(8/16M),所以我们要避免在函数内创建过大的局部变量,要警惕递归爆栈。

堆内存也叫动态内存,它由一个叫动态内存配置器的标准库组件管理,glibc的默认动态内存配置器叫ptmalloc,初始版本有性能问题,但后面用线程私有解决了竞争改善了性能。动态内存配置器是介于kernel与应用层的一个层次,从内核视角看ptmalloc是应用程序,从应用层来看ptmalloc又是系统库。malloc跟free必须配对,这是程序员的职责,动态分配的内存丢失引用就会导致内存泄漏,指向已释放的内存块俗称野(悬垂)指针。

预处理

从c source file到可执行程序需要经过预处理-编译-汇编-链接多个阶段,预处理阶段做替换、消除和扩充,预处理语句以#打头。

宏定义,#define,宏定义可以用\做行连接,#用来产生字符串,##用来拼接,宏定义的时候要注意加()避免操作符优先级干扰,可以用do while(0)来把定义作为单独语句,#undef是define的反操作。

#if #ifdef #ifndef #else #elif #endif用来条件编译,为了避免头文件重复包含,经常用#ifndef #define #endif。

#include用来做头文件包含;#pragma用来做行为控制;#error用来在编译的时候输出错误信息。

__FILE__、__LINE__、_DATE_、_TIME_、_STDC_等标准预定义宏可以被用来做一些debug用途。

#typedef用来定义类型别名。比如typedef int money_t;money_t比int更有含义。

typedef也能用来为结构体取别名,有时候会这样写:

typedef struct
{
int a;
int b;
} xyz_t;

这样在定义结构体变量的时候就可以少敲几下键盘。

typedef也可以用来重定义函数指针类型,比如 typedef void (*PF) (int a, int b); PF是函数指针类型,而非函数指针变量。

枚举

枚举能增加代码可读性和可维护性,枚举本质上是int,只是为了更有含义,将有限取值的几个int值放在一组,比如定义性别:enum sex { male = 1, female };

可以在定义的时候赋值,比如male=1,后面的值依次递增1,如果不赋值则从0开始。

联合体(union)

结构体和联合体(共用体)的区别在于:结构体的各个成员会占用不同的内存,互相之间没有影响;而共用体的所有成员占用同一段内存,修改一个成员会影响其余所有成员。

union u_data
{
int n;
char ch;
double f;
};

其实本质上,联合体就是对一块内存的多种解释,大小按最大的来。

位域(bitfield)

struct SNField
{
unsigned char seq:7 ; // frame sequnce
unsigned char startbit:1 ; // indicate if it's starting frame 1 for yes.
};

节省空间,在面向底层的编码,或者编写处理网络等程序时候用的比较多,注意这个语法特征是跟机器架构相关的。

位操作

  1. 位与 &
  2. 位或 |
  3. 位取反 ~
  4. 位异或 ^
  5. 位移 << >>

static、extern、register、volatile、sizeof

  1. static修饰全局函数,表示模块内(编译单元)内可用,不需要导出全局符号。
  2. static修饰局部变量,意味超越函数调用的生命周期,不存储在栈上,只会被初始化1次。
  3. extern声明外部变量。
  4. register,寄存器变量,建议编译器将变量放在寄存器里。
  5. volatile,告诉编译器不要做优化,每次从内存读取,不做寄存器优化。
  6. sizeof求大小,可以作用于变量,类型,表达式

可变参数

void simple_printf(const char* fmt, ...)

va_list、va_start、va_arg、va_end

C的高级感

  1. 泛型:linux内核链表,通过offset和内嵌node,写出泛型链表,参考:https://www.cnblogs.com/wangzahngjun/p/5556448.html
  2. OOP:通过定义带函数指针成员变量的结构体,在运行中,为结构体对象设置上函数指针,模拟运行时绑定,实现类似OOP多态的感觉。

GNU C扩展

GNU C扩展不是标准C,建议以符合标准C的方式编写C代码,但如果你阅读linux kernel code,你会发现有很多有趣看不懂的语法,它来自GNU C扩展,它确实也带来了一些便利性。

比如结构体成员可以不按定义顺序初始化:

struct test_t { int a; int b; };
struct test_t t1 = { .b = 1, .a = 2 };

比如可以通过指定索引初始化数组:

int a[5] = {[2] 5,[4] 9};
或 int a[5] = { [2] = 4, [4] = 9 };
相当于int a[5] = {0, 0, 4, 0, 9}; 或者int a[100] = {[0 ... 9] = 1, [10 ... 98] = 2, 3}; 比如0长度数组
struct foo
{
int i;
char a[0];
}; 比如用变量作为数组长度
void f(int n)
{
char a[n];
...
}

比如case范围,case 'A' ... 'Z' case 1 ... 10

比如表达式扩展({...}),比如三元运算符扩展...

点击关注,第一时间了解华为云新鲜技术~

C语言编程需要掌握的核心要点有哪些? 编程大神为你总结了这20个的更多相关文章

  1. C语言入门编程需要掌握的核心要点有哪些? 为你总结了这20个!

    摘要: C语言作为编程的入门语言,学习者如何快速掌握其核心知识点,面对茫茫书海,似乎有点迷茫.为了让各位快速地掌握C语言的知识内容,在这里对相关的知识点进行了归纳. 引言 C语言精简的语法集和标准库, ...

  2. Spring框架系列(4) - 深入浅出Spring核心之面向切面编程(AOP)

    在Spring基础 - Spring简单例子引入Spring的核心中向你展示了AOP的基础含义,同时以此发散了一些AOP相关知识点; 本节将在此基础上进一步解读AOP的含义以及AOP的使用方式.@pd ...

  3. Java 的核心目的和并发编程

    读一本书,最好能从它的前言开始.那么我们就来看看<Java编程思想>作者 Bruce Eckel 在前言里都说了些什么吧. 01.Java 的核心目的是"为程序员减少复杂性&qu ...

  4. Java生鲜电商平台-SpringCloud微服务架构中核心要点和实现原理

    Java生鲜电商平台-SpringCloud微服务架构中核心要点和实现原理 说明:Java生鲜电商平台中,我们将进一步理解微服务架构的核心要点和实现原理,为读者的实践提供微服务的设计模式,以期让微服务 ...

  5. 《Java多线程编程实战指南(核心篇)》阅读笔记

    <Java多线程编程实战指南(核心篇)>阅读笔记 */--> <Java多线程编程实战指南(核心篇)>阅读笔记 Table of Contents 1. 线程概念 1.1 ...

  6. 提高C#编程水平的50个要点

    下面的文章转载于 提高C#编程水平的50个要点 1.总是用属性 (Property) 来代替可访问的数据成员 2.在  readonly 和 const 之间,优先使用 readonly 3.在 as ...

  7. 提高C#编程水平的50个要点 你掌握了多少呢?

    提高C#编程水平的50个要点,程序员都是追求极致的完美主义者,下面的这些注意点和要点,你都掌握运用了多少呢? 总是用属性(Property)来代替可访问的数据成员 在 readonly 和 const ...

  8. dubbo核心要点及下载(dubbo二)

    一.dubbo核心要点 1):服务是围绕服务提供方和服务消费方的,服务提供方实现服务,服务消费方调用服务. 2):服务注册 对于服务提供方它需要发布服务,而由于应用系统的复杂性,服务的数量.类型不断的 ...

  9. [转载] JAVA面试题和项目面试核心要点精华总结(想进大公司必看)

    JAVA面试题和项目面试核心要点精华总结(想进大公司必看) JAVA面试题和项目面试核心要点精华总结(想进大公司必看)

  10. Page Object页面设计模式核心要点

      Page Object,页面对象.一种设计模式,实施selenium的最佳实践,体现了web应用与页面显示之间的关系.为什么需要Page Object?测试代码维护的需要:减少代码的编码量,减少代 ...

随机推荐

  1. log4j2同步日志引发的性能问题

    1 问题回顾 1.1 问题描述 在项目的性能测试中,相关的接口的随着并发数增加,接口的响应时间变长,接口吞吐不再增长,应用的CPU使用率较高. 1.2 分析思路 谁导致的CPU较高,阻塞接口TPS的增 ...

  2. Decorator 装饰者模式简介与 C# 示例【结构型4】【设计模式来了_9】

    〇.简介 1.什么是装饰者模式 一句话解释:   通过继承统一的抽象类来新增操作,再在使用时通过链式添加到对象中,达到与原有设定无关联可灵活附加. 装饰者模式是一种行为设计模式,它允许向一个现有的对象 ...

  3. LangChain实战

    1.概述 最近,在研究LangChain时,发现一些比较有意思的点,今天笔者将给大家分享关于LangChain的一些内容. 2.内容 2.1 什么是LangChain? LangChain是一项旨在赋 ...

  4. CF1766B [Notepad#]

    Problem 题目简述 给你一个整数 \(n\) 和字符串 \(s\),问:能不能在小于 \(n\) 次操作的情况下,输出字符串 \(s\). 有两次操作可供使用: 在已打出内容的最后添加一个字符. ...

  5. QT(9)-QStyleOption及其子类

    1 QStyleOption QStyleOption及其子类包含QStyle函数绘制图形元素所需的所有信息. 出于性能考虑,成员函数很少,对成员变量的访问是直接的(即使用.或者->运算符).这 ...

  6. Intellij13 IDEA常用快捷键 (mac 10.5 +),优化,使用出现的问题汇总

    http://blog.csdn.net/xiaohulunb/article/details/20729261

  7. [Python急救站课程]圆的面积计算

    利用公式计算圆的面积程序 radius = 25 # 设置圆的面积 area = 3.1415926 * radius * radius # 输入计算公式 print(area) # 输出计算值 pr ...

  8. 使用MVVM Toolkit简化WPF开发

    最近. NET 8 的 WPF 推出了 WPF File Dialog改进,这样无需再引用 Win32 命名空间就可以实现文件夹的选择与存储了,算是一个很方便的改进了.顺手写了一个小的 WPF 程序, ...

  9. Numpy理解

    目录 什么是numpy numpy的安装 numpy数组 定义numpy数组 numpy数组的相关功能 基本操作 0数组和1数组 随机数组 二维数组 numpy的数组操作 我们再平常学习python和 ...

  10. Java笔记——常用类

    一.API概述 JDK中提供的各种功能的Java类 二.Object类 概述 类层次结构的根类 所有类都直接或间接的继承自该类 Class Object是类object结构的根.每个Class都有ob ...