C语言笔记

版权声明:此文章转载自_infocool

原文链接:http://www.infocool.net/kb/CPlus/201609/191391.html

如需转载请联系听云College团队成员小尹 邮箱:yinhy#tingyun.com

基础知识

数据类型

1.jpg

补充:1.函数类型是指函数返回值的类型,数组类型与结构类型统称为聚会类型。

2.除了基本类型,其他的类型都是程序员使用相关关键词或通过基本数据类型构造的自定义类型。

2.jpg

补充:假如要说明整数类型的符号,就在类型前面添加signed/unsigned

参考连接:

    枚举类型:http://c.biancheng.net/cpp/html/99.html

    整型、浮点类型:http://www.runoob.com/cprogramming/c-data-types.html

    联合体类型:http://www.runoob.com/cprogramming/c-unions.html

    结构体类型:http://www.runoob.com/cprogramming/c-structures.html

例子:

#include <stdio.h>
#include <string.h>
enum week{ Mon = 'm', Tues, Wed, Thurs, Fri, Sat, Sun }; //枚举,值只能是整数类型
struct stu{ char name[8];
char sex;
int age;
}; //结构体
union school{
    char name[20];
    int peopleNumber;
    int phone;
    week day;
}; //联合体,成员同时只能存在一个
int main(void){
    int I = 1;
    int * P; //指针
    P = &I;
    union school mySchool;
    struct stu student;
    char name[10] = "john";
    int number[3] = { 1, 2, 3 };
 
 
    strcpy_s(student.name, "john");
    student.sex = 'm';
    student.age = 20;
 
    printf("name:%s, sex : %c, age : %d\n", student.name, student.sex, student.age);
    printf("i = %d, p = %d, name = %s\n", I, *P, name);
 
    strcpy_s(mySchool.name, "*****\n");
    printf("mySchool name : %s\n", mySchool.name);
    mySchool.peopleNumber = 2000;
    printf("mySchool peopleNumber : %d\n", mySchool.peopleNumber);
    mySchool.day = Mon;
    printf_s("day : %c", mySchool.day);
 
    getchar();
    return 0;
}

基本数据对象

变量:

    声明:dataType name ;

    特别的是数组变量的声明:dataType name[count] ;

常量:

    声明:#define name value //属于预处理阶段

         const dataType name= value; //属于编译阶段

运算符

运算符

3.jpg

控制流

判断:

4.jpg

循环:

5.jpg

程序组织结构

预处理语句

C 预处理器不是编译器的组成部分,但是它是编译过程中一个单独的步骤。简言之,C 预处理器只不过是一个文本替换工具而已,它们会指示编译器在实际编译之前完成所需的预处理。

所有的预处理器命令都是以井号(#)开头。

6.jpg

补充:#pragma comment(linker, "/entry:main2") 可以用来改变入口函数

函数

先声明后使用

7.jpg

补充:对应一个可执行文件的源代码,始终要存在一个主函数,才能让编译器正确编译。

默认main 函数,但不一定是main,可以通过设置编译器的参数来进行更改。

变量

先声明后使用

语句&表达式

语句是对基本数据对象的处理步骤,使用 ; 分割;表达式是对基本数据对象进行运算,处理步骤成为了语句。

注释

// 或者 /* 内容 */

进阶部分

标准库:

应该知道的:标准库并不是c语言本身的构成部分,只是将一些非常非常常用的功能进行了标准化,让程序员能够更好的进行程序的开发和移植。并且提供一些系统调用的封装,这样程序员就可以不用去考虑程序与系统与硬件的关系,只要调用相关的库函数就能进行与硬件的交互了。

标准库中包含了大量的宏定义和使用typedef关键字定义的新的类型,以及函数的声明与实现。

8.jpg

9.jpg

共享库与静态库、静态调用与动态调用、静态链接与动态链接

静态库与动态库的编译方式不同。属于编译部分。

静态链接与动态链接是属于链接部分的,是告诉链接器使用的是什么类型的库,静态库就是静态链接,动态库就是动态链接。

#pragma comment( lib, "..\\debug\\libTest.lib" ) //告诉连接器使用静态库进行静态链接

静态调用和动态调用是属于动态库的一部分,是关于如何将动态库加载到内存的。

静态调用时,操作系统的加载程序会先为进程创建虚拟地址空间,接着把可执行模块映射到进程的地址空间中,之后加载程序会检查可执行模块的导入段,试图对所需的DLL进行定位并将它们映射到进程的地址空间内。(windows 核心编程第五版 19章中的运行可执行模块)。#pragma comment(lib,"dllTest.lib") 这个宏告诉编译器使用静态调用。

动态调用时,由程序员通过loadLibrary这个API将DLL映射到进程的地址空间中,再由程序员通过GetProcAddress这个API获取想要的导出函数的地址(在使用返回的函数指针来调用函数之前,需要转型为与函数的签名相匹配的正确类型)来调用所需函数。

外部函数:

由其他源文件或者库提供的函数。程序员使用时,需要它人提供的头文件或者使用extern或者extern "c"__declspec(dllimport)来进行函数声明,表明该函数来至外部文件。

编译:

链接:

软件的运行过程:

操作系统与软件的关系:

系统调用:

低层c与应用层的c的关系:

 

系统调用与标准库的关系:

系统调用就是操作系统提供的一组API,通过这些API函数就可以通过系统与硬件进行交流了,这些API函数需要到操作系统的开发者处获得。而语言的标准库是语言的标准化组织为了让程序员在各种平台或者系统上进行少量的程序修改就可以编译运行所制定的一系列的函数。也就是说标准库是脱离了操作系统或者平台的,属于是抽象层,具有平台无关性。

但是每个操作系统能够提供的API(系统调用)并不是一致的,也就是函数名呀、函数的参数呀、返回值类型呀会有所不同。

但为了让程序能够运行在某种特定的系统上,就需要去实现该系统上的标准库中的库函数(标准函数)。而这部分会由编译器来提供,就是说编译器的开发者会提供该编译器能编译的语言的标准库部分(标准库的实现)。他们通过阅读操作系统开发者提供的API文档来编写标准库的实现代码。

到此处就可以了解到程序员只需调用对应的库函数就能摆脱操作系统,来编写自己的可移植的程序代码了(标准库消除平台的差异性,给程序员提供一种统一体验的编码方式,让程序更方便的移植)。

库代码解析:

补充:在vs2013中开发没有使用纯c的,只有使用c++的源代码了。但是c++是c的升级,向下兼容c,所有差别不大,只是在语法上有些出入。

windows中的vc:
time.h
#include <time.inl> //采用了c++的内联语法
 
time.inl //内联
static __inline time_t __CRTDECL time(time_t * _Time) //32位
{
return _time32(_Time);
}
 
time.c //库函数的实现部分
#include <#include <time.h>
#include <windows.h>> //操作系统提供的系统调用的头文件
__time32_t __cdecl _time32 (
__time32_t *timeptr
)
{
__time64_t tim;
FT nt_time;
 
GetSystemTimeAsFileTime( &(nt_time.ft_struct) ); //系统调用
 
tim = (__time64_t)((nt_time.ft_scalar - EPOCH_BIAS) / 10000000i64);
 
if (tim > (__time64_t)(_MAX__TIME32_T))
tim = (__time64_t)(-1);
 
if (timeptr)
*timeptr = (__time32_t)(tim); /* store time if requested */

 

return (__time32_t)(tim);
}

可以看出库函数 time 的实现其实是调用了windows.h中的GetSystemTimeAsFileTime系统调用。

补充:由于库的实现不是应用程序员所需要关心的事情,程序员只关心提供的.h文件中的函数声明(也就是标准库提供的接口的方式),所以编译器开发者可以使用高超的技术来进行各种优化与数据操作(也就是我们看不懂的方式来进行库的实现)。所以不必在意库的实现。

glibc
 
time.h
# include <bits/time.h>
extern time_t time (time_t *__timer) __THROW; //使用liunx提供的time
 
time.c
time_t
time (timer)
time_t *timer;
{
__set_errno (ENOSYS);
 
if (timer != NULL)
*timer = (time_t) -1;
return (time_t) -1;
}
 
<sys/time.h>
extern int gettimeofday (struct timeval *__restrict __tv,
             __timezone_ptr_t __tz) __THROW __nonnull ((1));

由于能力有限不能找到具体的函数实现,但是可以通过网络了解到liunx上的c标准库的实现使用了linux提供的time系统调用,采用的是函数映射方式实现的。

liunx上获取时间的系统调用为 #include <sys/time.h>中的gettimeofday。

再次补充:一般地,操作系统为了考虑实现的难度和管理的方便,它只提供一少部分的系统调用,这些系统调用一般都是由C和汇编混合编写实现的,其接口用C来定义,而具体的实现则是汇编,这样的好处就是执行效率高,而且,极大的方便了上层调用。

写在最后:一种编程语言只是抽象层的东西,要让计算机能够识别该种语言所编写的程序,就需要编译器或者解释器,将程序员编写的属于字符类型的源代码文件转换成计算机(cpu)所能懂的指令。由于计算机分成了很多的平台与架构(操作系统的不同,cpu的差异),所以就要编写各种的编译器或解释器来进行该平台的语言实现,实现语言中的语法和语义,并且提供语言标准组织所制定的标准部分(要提供什么标准库和其他编程方面的东东)(但不一定非要这样,也可以增删部分语法和库)。所以编译器或解释器是最终得boss,它拥有语言实现的决定权。说白了程序员就是一个给编译器或解释器打工的苦劳力,最终你的工作成果还的它来说了算。管他语言标准呀,可移植的语言,没有实现,都是空话。还有就是罗马不是一天建成的,现在的一切程序都是由最开始的cpu指令构成的(人不就是史前的那个单细胞慢慢演变的吗?)。


想阅读更多技术文章,请访问听云技术博客,访问听云官方网站感受更多应用性能优化魔力。

关于作者

coco秋洁

我爱学习,学习使我快乐

我要评论

评论请先登录,或注册