C语言笔记

C语言笔记

基础知识

  1. 数据类型

序号


类型与描述


1


基本类型:

它们是算术类型,包括:整数类型、浮点类型


2


枚举类型:

也是算术类型,被用来定义只能使用某些整型值的变量,使用时需要程序员先使用eumn关键字来声明定义


3


Void类型:

用于函数,指明函数的返回值或参数。作用于变量会发生编译错误


4


派生类型:

包括:指针类型、数组类型、结构类型、联合体类型、函数类型

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

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



?

?

类型参考

?

整数类型


Char、int、short、long


浮点类型


Float、double、long double


枚举类型


enum typeName{ valueName1, valueName2, valueName3, ...... };


Void类型

?

指针类型


Type * name;


数组类型


Type name[count];


结构类型


Struct name{merber1;merber2;merber3};


联合体类型


Union name{merber1;merber;merber};

补充:假如要说明整数类型的符号,就在类型前面添加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;

}

?

  1. 基本数据对象

变量:

????声明:dataType name ;

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

常量:

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

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

?

  1. 运算符

运算符

?

算术运算


+、-、*、/、%、++、--


关系运算


== 、 != 、 > 、 < 、>= 、 <=


逻辑运算


&& 、 || 、 !


位运算


& 、 | 、^ 、<< 、 >> 、~


赋值运算


= 、+= 、-= 、*= 、/= 、%= 、 <<= 、>>= 、&= 、^= 、|=


杂项运算


sizeof() 、* 、& 、?:

?

  1. 控制流

判断:


if( condition ){

Statement block;

}

else if( condition ){

Statement block;

}

else{

Statement block;

}


?

?

switch( condition ){

case 常量表达式:Statement block;

case 常量表达式:Statement block;

default : Statement block;

}

?

循环:


1.

while( condition ){

Statement block;

}


?

do{

Statement block;

}while( conditon );


2.

for( int I ; I >10 ; I ++ ){

Statement block;

}

?

循环控制:

break : 跳出当前循环,不再执行

continue : 跳出当前循环,并继续

?

  1. 程序组织结构
  1. 预处理语句

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

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


指令


描述


#include


包含一个源代码文件


#define


定义宏


#undef


取消已定义的宏


#ifdef


判断宏的定义情况,如果宏已经定义,返回真


#ifndef


判断宏的定义情况,如果宏没有定义,返回真


#if


如果给定的条件为真,则编译下面的代码


#else

?

#elif

?

#endif

?

#error


当遇到标准错误时,输出错误消息


#pragma


使用标准化方法,向编译器发布特殊的命令到编译器中

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

  1. 函数

    先声明后使用


定义:

return_type function_name( parameter list ){

Statement block;

}

使用:

function_name(parameter list);

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

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

  1. 变量

    先声明后使用

  2. 语句&表达式

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

  3. 注释

    // 或者 /* 内容 */

进阶部分

标准库:

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

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

?


标准库名称


说明


常用库函数


<stdio.h>


标准输入输出:用以提供基于常用硬件的I/O操作(文件读写、屏幕输入输出)。


fclose、fopen、fseek、fwrite、printf、

scanf


<stdlib.h>


定义了四个变量类型、一些宏和各种通用工具函数


NULL、atof(字符串转换为一个浮点数)、atoi、free、malloc、abort、

exit、getenv、


<string.h>


与操作字符串有关


memcmp(将str1与str2的前n个字节进行比较)、strcat、strcpy、strlen、


<stdarg.h>


与函数的参数有关,获取参数个数可变时获取函数中的参数

?

<math.h>


定义各种数学函数


exp(x):返回e的x次幂的值

pow(x,y):返回x的y次幂

fabs(x) :返回x的绝对值


<time.h>

? ?

<stddef.h>


定义各种变量类型与宏


NULL、


<assert.h>


提供一种诊断宏型函数


唯一的库函数:assert


<ctype.h>


提供函数来测试和映射字符


islower、isupper、isxdigit、tolower、toupper …


<errno.h>


定义了一些与错误有关的宏


errno


<float.h>


包含一组与浮点值有关的平台常量


FLT_MAX:平台能表达的的最大浮点数,FLT_MIN:平台能表达的的最小浮点数


<limits.h>


定义在其中的宏限制了各种变量类型的值


CHAR_BIT:定义一个字节的比特数


<locale.h>


定义特定地域的设置,比如日期格式和货币符号


LC_CTYPE:影响所以的字符函数

setlocale : 设置或读取地狱化信息


<setjmp.h>


程序中的长跳转,不局限在函数内部。


setjmp/longjmp的最大用处是错误恢复,类似try ...catch...


<signal.h>


提供与信号有关的宏和函数


signal : 设置一个函数来处理信号

raise() : 产生信号


<raise.h>


用于产生信号

?

?

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

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

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

#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指令构成的(人不就是史前的那个单细胞慢慢演变的吗?)。

时间: 2024-11-04 05:51:41

C语言笔记的相关文章

C语言笔记1--类型、运算符与表达式

 C语言笔记1--类型.运算符与表达式 总论: 变量和常量是程序处理的两种基本的数据对象.声明语句说明变量的名字和类型,也可以指定变量的初值.运算符指定将要进行的操作.表达式则把变量与常量组合起来生成新的值.对象的类型决定该对象可取值的集合以及可以对该对象执行的操作. 一.变量与常量的名字 1.名字是由字母.下划线和数字组成的序列,其第一个字符必须为字母:下划线"_"被看做是字母,但一般不要以下划线"_"开头. 2.名字区分字母大小写,变量名使用小写字母.符号常量

C++语言笔记系列之十二——C++的继承

C++的继承 1.继承方式 public(公有继承) 派生类中的成员可以访问基类的public成员和protected成员,但不能访问基类的private成员. 派生类的对象只能访问基类的public成员. protected(保护继承),private(私有继承) 派生类中的成员可以访问基类的public成员和protected成员,但不能访问基类的private成员. 派生类的对象不能访问基类的任何成员. 2.例子 example 1: #include <iostream.h> clas

C++语言笔记系列之十六——赋值兼容规则&amp;多继承的二义性

1.赋值兼容规则 (1)派生类对象可以给基类对象赋值,这种情况下派生类对象将从基类继承的成员的值赋值给一个基类对象:但是不允许将一个基类的对象赋值给一个派生类. (2)可以将派生类对象的地址赋给基类指针,使基类指针指向派生类对象,通过基类指针引用成员时只可以引用派生类从基类继承而来的成员,而不允许引用派生类的新成员. (3)引用与指针相同. 2.多继承 (1)一个派生类从两个以上的基类中去继承. (2)说明: class 派生类名:继承方式 基类1, 继承方式 基类2,...... {派生类成员

C++语言笔记系列之十七——虚基类

1.虚基类 考虑这样一种情况:当某个类的部分或者全部直接基类是另一个共同基类派生而来,这些直接基类从上一级基类继承而来的成员就一定拥有相同的名称,这样就会产生二义性问题. 解决办法:当派生类和直接基类产生了二义性问题-->加类的作用域. 当派生类和间接基类产生了二义性问题-->虚基类. 2.虚基类的说明: class 派生类名:virtual 访问权限 基类名 {派生类定义}; 注意:在定义派生类时将需要继承的基类进行虚化声明,虚基类的说明在派生类的定义中完成. 作用:将基类说明为虚基类之后,

C++语言笔记系列之十三——派生类构造函数的调用

1.派生类构造函数的调用 (1)一个基类的所有数据成员均被派生类继承.创建一个派生类对象时,系统在为派生类对象分配单元时一定要为其基类数据成员分配子空间. (2)一个派生类对象在创建时不仅要调用派生类构造函数,而且要调用基类构造函数. 派生类中的数据成员在派生类中构造. 基类中的数据成员在基类中构造. 原因: A.构造函数不继承. B.派生类的构造函数必须通过调用基类的构造函数完成基类数据成员的初始化. C.若派生类中含有子对象,必须调用子对象的构造函数. 2.派生类的构造函数 派生类名(总参数

C++语言笔记系列之十八——虚函数(1)

1.C++中的多态 (1)多态性:同一个函数的调用可以进行不同的操作,函数重载是实现多态的一种手段. (2)联编:在编译阶段进行联接,即是在编译阶段将一个函数的调用点和函数的定义点联接起来. A.静态联编:在编译阶段就完成的函数联编--函数重载. B.动态联编:在程序的运行阶段由系统自动选择具体的函数--虚函数. 注:C++的多态主要指的就是动态联编. 2.虚函数 (1)虚函数是在函数的定义时将其声明为虚函数即可. (2)说明:virtual 数据类型 函数名(参数表) {函数体} A.目的:当

C++语言笔记系列之十四——继承后的访问权限

1.析构函数不继承:派生类对象在析构时,基类析构函数的调用顺序与构造函数相反. 注:派生类对象建立时要调用基类构造函数,派生类对象删除时要调用基类析构,顺序与构造函数严格相反. 2.例子 example 1 #include <iostream.h> #include <math.h> class Point { public: Point(double a, double b, doule c) { x = a; y = b; z = c; } double Getx() {re

C++语言笔记系列之十五——派生类、基类、子对象的构造和析构函数调用关系

例子 example 1 注:若一个基类同时派生出两个派生类,即两个派生类从同一个基类继承,那么系统将为每一个简历副本,每个派生类独立地使用自己的基类副本(比如基类中有属于自己类的静态变量等). #include <iostream.h> class Person { public: person() {cout<<"Construction of person."<<endl;} ~person() {cout<<"Destr

C++语言笔记系列之十——静态成员

1.静态成员 (1)由关键字static修饰 静态变量定义语句在编译阶段就执行,运行过程中不再执行. (2)分类:静态数据成员.静态成员函数. (3)静态成员时类的所有对象共享的成员,而不是某一个对象的成员. 2.静态成员的使用 (1)在定义说明前加上static关键字.例如: static int x: (2)静态数据成员必须进行初始化,并且初始化必须在类外完成. (3)静态数据成员的初始化 数据类型 类名::静态数据成员名 = 值://注意这里没有static出现 (4)绝对不能使用对象名来