当前位置:文档之家› c语言new C++的new

c语言new C++的new

c语言new C++的new
c语言new C++的new

c语言new:C++的new

“new”是C++的一个关键字,同时也是操作符。关于new的话题非常多,因为它确实比较复杂,也非常神秘,下面我将把我了解到的与new有关的内容做一

个总结。

new的过程

当我们使用关键字new在堆上动态创建一个对象时,它实际上做了三件事:获得一块内存空间、调用构造函数、返回正确的指针。当然,如果我们创建的是简单类型的变量,那么第二步会被省略。假如我们定义了如下一个类A:

class A

{

int i;

public:

A(int _i) :i(_i*_i) {}

void Say() { printf("i=%dn", i); }

};

//调用new:

A* pa = new A(3);

那么上述动态创建一个对象的过程大致相当于以下三句话(只是大致上):

A* pa = (A*)malloc(sizeof(A));

pa->A::A(3);

return pa;

虽然从效果上看,这三句话也得到了一个有效的指向堆上的A对象的指针pa,但区别在于,当malloc失败时,它不会调用分配内存失败处理程序

new_handler,而使用new的话会的。因此我们还是要尽可能的使用new,除非

有一些特殊的需求。

new的三种形态

到目前为止,本文所提到的new都是指的“new operator”或称为“new expression”,但事实上在C++中一提到new,至少可能代表以下三种含义:new operator、operator new、placement new。

new operator就是我们平时所使用的new,其行为就是前面所说的三个步骤,

我们不能更改它。但具体到某一步骤中的行为,如果它不满足我们的具体要求时,我们是有可能更改它的。三个步骤中最后一步只是简单的做一个指针的类

型转换,没什么可说的,并且在编译出的代码中也并不需要这种转换,只是人

为的认识罢了。但前两步就有些内容了。

new operator的第一步分配内存实际上是通过调用operator new来完成的,这里的new实际上是像加减乘除一样的操作符,因此也是可以重载的。operator new默认情况下首先调用分配内存的代码,尝试得到一段堆上的空间,如果成功就返回,如果失败,则转而去调用一个new_hander,然后继续重复前

面过程。如果我们对这个过程不满意,就可以重载operator new,来设置我

们希望的行为。例如:

class A

{

public:

void* operator new(size_t size)

{

printf("operator new calledn");

return ::operator new(size);

}

};

A* a = new A();

这里通过::operator new调用了原有的全局的new,实现了在分配内存之前输

出一句话。全局的operator new也是可以重载的,但这样一来就不能再递归的使用new来分配内存,而只能使用malloc了:

void* operator new(size_t size)

{

printf("global newn");

return malloc(size);

}

相应的,delete也有delete operator和operator delete之分,后者也是可

以重载的。并且,如果重载了operator new,就应该也相应的重载operator

delete,这是良好的编程习惯。

new的第三种形态——placement new是用来实现定位构造的,因此可以实现new operator三步操作中的第二步,也就是在取得了一块可以容纳指定类型对

象的内存后,在这块内存上构造一个对象,这有点类似于前面代码中的“p->A::A(3);”这句话,但这并不是一个标准的写法,正确的写法是使用

placement new:

#include

void main()

{

char s[sizeof(A)];

A* p = (A*)s;

new(p) A(3); //p->A::A(3);

p->Say();

}

对头文件的引用是必须的,这样才可以使用placement new。

这里“new(p) A(3)”这种奇怪的写法便是placement new了,它实现了在指定内存地址上用指定类型的构造函数来构造一个对象的功能,后面A(3)就是对构

造函数的显式调用。这里不难发现,这块指定的地址既可以是栈,又可以是堆,placement对此不加区分。但是,除非特别必要,不要直接使用placement new ,这毕竟不是用来构造对象的正式写法,只不过是new operator的一个步骤而已。使用new operator地编译器会自动生成对placement new的调用的代码,因此也会相应的生成使用delete时调用析构函数的代码。如果是像上面那样在栈上使用了placement new,则必须手工调用析构函数,这也是显式调用

析构函数的唯一情况:

p->~A();

当我们觉得默认的new operator对内存的管理不能满足我们的需要,而希望自己手工的管理内存时,placement new就有用了。STL中的allocator就使用了这种方式,借助placement new来实现更灵活有效的内存管理。

处理内存分配异常

正如前面所说,operator new的默认行为是请求分配内存,如果成功则返回此内存地址,如果失败则调用一个new_handler,然后再重复此过程。于是,想要从operator new的执行过程中返回,则必然需要满足下列条件之一:

l 分配内存成功

l new_handler中抛出bad_alloc异常

l new_handler中调用exit()或类似的函数,使程序结束于是,我们可以假设默认情况下operator new的行为是这样的:

void* operator new(size_t size)

{

void* p = null

while(!(p = malloc(size)))

{

if(null == new_handler)

throw bad_alloc();

try

{

new_handler();

}

catch(bad_alloc e)

{

throw e;

}

catch(…)

{}

}

return p;

}

在默认情况下,new_handler的行为是抛出一个bad_alloc异常,因此上述循环只会执行一次。但如果我们不希望使用默认行为,可以自定义一个new_handler,并使用std::set_new_handler函数使其生效。在自定义的new_handler中,我们可以抛出异常,可以结束程序,也可以运行一些代码使

得有可能有内存被空闲出来,从而下一次分配时也许会成功,也可以通过set_new_handler来安装另一个可能更有效的new_handler。例如:

void MyNewHandler()

{

printf(“New handler called!n”);

throw std::bad_alloc();

}

std::set_new_handler(MyNewHandler);

这里new_handler程序在抛出异常之前会输出一句话。应该注意,在

new_handler的代码里应该注意避免再嵌套有对new的调用,因为如果这里调用new再失败的话,可能会再导致对new_handler的调用,从而导致无限递归调用。——这是我猜的,并没有尝试过。

在编程时我们应该注意到对new的调用是有可能有异常被抛出的,因此在new 的代码周围应该注意保持其事务性,即不能因为调用new失败抛出异常来导致不正确的程序逻辑或数据结构的出现。例如:

class SomeClass

{

static int count;

SomeClass() {}

public:

static SomeClass* GetNewInstance()

{

count++;

return new SomeClass();

}

};

静态变量count用于记录此类型生成的实例的个数,在上述代码中,如果因new分配内存失败而抛出异常,那么其实例个数并没有增加,但count变量的值却已经多了一个,从而数据结构被破坏。正确的写法是:

static SomeClass* GetNewInstance()

{

SomeClass* p = new SomeClass();

count++;

return p;

}

这样一来,如果new失败则直接抛出异常,count的值不会增加。类似的,在处理线程同步时,也要注意类似的问题:

void SomeFunc()

{

lock(someMutex); //加一个锁

delete p;

p = new SomeClass();

unlock(someMutex);

}

此时,如果new失败,unlock将不会被执行,于是不仅造成了一个指向不正确地址的指针p的存在,还将导致someMutex永远不会被解锁。这种情况是要注意避免的。(参考:C++箴言:争取异常安全的代码)

STL的内存分配与traits技巧

在《STL原码剖析》一书中详细分析了SGI STL的内存分配器的行为。与直接使用new operator不同的是,SGI STL并不依赖C++默认的内存分配方式,而是使用一套自行实现的方案。首先SGI STL将可用内存整块的分配,使之成为当前进程可用的内存,当程序中确实需要分配内存时,先从这些已请求好的大内存块中尝试取得内存,如果失败的话再尝试整块的分配大内存。这种做法有效的避免了大量内存碎片的出现,提高了内存管理效率。

为了实现这种方式,STL使用了placement new,通过在自己管理的内存空间上使用placement new来构造对象,以达到原有new operator所具有的功能。

template

inline void construct(T1* p, const T2& value)

{

new(p) T1(value);

}

此函数接收一个已构造的对象,通过拷贝构造的方式在给定的内存地址p上构

造一个新对象,代码中后半截T1(value)便是placement new语法中调用构造

函数的写法,如果传入的对象value正是所要求的类型T1,那么这里就相当于

调用拷贝构造函数。类似的,因使用了 placement new,编译器不会自动产生

调用析构函数的代码,需要手工的实现:

template

inline void destory(T* pointer)

{

pointer->~T();

}

与此同时,STL中还有一个接收两个迭代器的destory版本,可将某容器上指

定范围内的对象全部销毁。典型的实现方式就是通过一个循环来对此范围内的对象逐一调用析构函数。如果所传入的对象是非简单类型,这样做是必要的,

但如果传入的是简单类型,或者根本没有必要调用析构函数的自定义类型(例如只包含数个int成员的结构体),那么再逐一调用析构函数是没有必要的,

也浪费了时间。为此,STL使用了一种称为“type traits”的技巧,在编译器就判断出所传入的类型是否需要调用析构函数:

template

inline void destory(ForwardIterator first, ForwardIterator last)

{

__destory(first, last, value_type(first));

}

其中value_type()用于取出迭代器所指向的对象的类型信息,于是:

template

inline void __destory(ForwardIterator first, ForwardIterator last, T*)

{

typedef typename __type_traits::has_trivial_destructor

trivial_destructor;

__destory_aux(first, last, trivial_destructor());

}

//如果需要调用析构函数:

template

inline void __destory_aux(ForwardIterator first, ForwardIterator last,

__false_type)

{

for(; first < last; ++first)

destory(&*first); //因first是迭代器,*first取出其真正内容,然后再用

&取地址

}

//如果不需要,就什么也不做:

tempalte

inline void __destory_aux(ForwardIterator first, ForwardIterator last,

__true_type)

{}

因上述函数全都是inline的,所以多层的函数调用并不会对性能造成影响,最终编译的结果根据具体的类型就只是一个for循环或者什么都没有。这里的关键在于__type_traits这个模板类上,它根据不同的T类型定义出不同的has_trivial_destructor的结果,如果T是简单类型,就定义为__true_type

类型,否则就定义为 __false_type类型。其中__true_type、__false_type只不过是两个没有任何内容的类,对程序的执行结果没有什么意义,但在编译器看来它对模板如何特化就具有非常重要的指导意义了,正如上面代码所示的那样。__type_traits也是特化了的一系列模板类:

struct __true_type {};

struct __false_type {};

template

struct __type_traits

{

public:

typedef __false _type has_trivial_destructor;

……

};

template<> //模板特化

struct __type_traits //int的特化版本

{

public:

typedef __true_type has_trivial_destructor;

……

};

…… //其他简单类型的特化版本

如果要把一个自定义的类型MyClass也定义为不调用析构函数,只需要相应的定义__type_traits的一个特化版本即可:

template<>

struct __type_traits

{

public:

typedef __true_type has_trivial_destructor;

……

};

模板是比较高级的C++编程技巧,模板特化、模板偏特化就更是技巧性很强的东西, STL中的type_traits充分借助模板特化的功能,实现了在程序编译期通过编译器来决定为每一处调用使用哪个特化版本,于是在不增加编程复杂性的前提下大大提高了程序的运行效率。更详细的内容可参考《STL源码剖析》

第二、三章中的相关内容。

带有“[]”的new和delete

我们经常会通过new来动态创建一个数组,例如:

char* s = new char[100];

……

delete s;

严格的说,上述代码是不正确的,因为我们在分配内存时使用的是new[],而并不是简单的new,但释放内存时却用的是delete。正确的写法是使用

delete[]:

delete[] s;

但是,上述错误的代码似乎也能编译执行,并不会带来什么错误。事实上,new

与new[]、delete与delete[]是有区别的,特别是当用来操作复杂类型时。假如针对一个我们自定义的类MyClass使用new[]:

MyClass* p = new MyClass[10];

上述代码的结果是在堆上分配了10个连续的MyClass实例,并且已经对它们依次调用了构造函数,于是我们得到了10个可用的对象,这一点与Java、C#有区别的,Java、C#中这样的结果只是得到了10个null。换句话说,使用这种写法时MyClass必须拥有不带参数的构造函数,否则会发现编译期错误,因为

编译器无法调用有参数的构造函数。

当这样构造成功后,我们可以再将其释放,释放时使用delete[]:

delete[] p;

当我们对动态分配的数组调用delete[]时,其行为根据所申请的变量类型会有所不同。如果p指向简单类型,如int、char等,其结果只不过是这块内存被回收,此时使用delete[]与delete没有区别,但如果p指向的是复杂类型,delete[]会针对动态分配得到的每个对象调用析构函数,然后再释放内存。因此,如果我们对上述分配得到的p指针直接使用delete来回收,虽然编译期不报什么错误(因为编译器根本看不出来这个指针p是如何分配的),但在运行时(DEBUG情况下)会给出一个Debug assertion failed提示。

到这里,我们很容易提出一个问题——delete[]是如何知道要为多少个对象调用析构函数的?要回答这个问题,我们可以首先看一看new[]的重载。

class MyClass

{

int a;

public:

MyClass() { printf("ctorn"); }

~MyClass() { printf("dtorn"); }

};

void* operator new[](size_t size)

{

void* p = operator new(size);

printf("calling new[] with size=%d address=%pn", size, p);

return p;

}

// 主函数

MyClass* mc = new MyClass[3];

printf("address of mc=%pn", mc);

delete[] mc;

运行此段代码,得到的结果为:(VC2005)

calling new[] with size=16 address=003A5A58

ctor

ctor

ctor

address of mc=003A5A5C

dtor

dtor

dtor

虽然对构造函数和析构函数的调用结果都在预料之中,但所申请的内存空间大小以及地址的数值却出现了问题。我们的类MyClass的大小显然是4个字节,并且申请的数组中有3个元素,那么应该一共申请12个字节才对,但事实上系统却为我们申请了16字节,并且在operator new[]返后我们得到的内存地址是实际申请得到的内存地址值加4的结果。也就是说,当为复杂类型动态分配数组时,系统自动在最终得到的内存地址前空出了 4个字节,我们有理由相信这4个字节的内容与动态分配数组的长度有关。通过单步跟踪,很容易发现这4个字节对应的int值为0x00000003,也就是说记录的是我们分配的对象的个数。改变一下分配的个数然后再次观察的结果证实了我的想法。于是,我们也有理由认为new[] operator的行为相当于下面的伪代码:

template

T* New[](int count)

{

int size = sizeof(T) * count + 4;

void* p = T::operator new[](size);

*(int*)p = count;

T* pt = (T*)((int)p + 4);

for(int i = 0; i < count; i++)

new(&pt) T();

return pt;

}

上述示意性的代码省略了异常处理的部分,只是展示当我们对一个复杂类型使

用new[] 来动态分配数组时其真正的行为是什么,从中可以看到它分配了比预

期多4个字节的内存并用它来保存对象的个数,然后对于后面每一块空间使用placement new来调用无参构造函数,这也就解释了为什么这种情况下类必须有无参构造函数,最后再将首地址返回。类似的,我们很容易写出相应的

delete[]的实现代码:

template

void Delete[](T* pt)

{

int count = ((int*)pt)[-1];

for(int i = 0; i < count; i++)

pt.~T();

void* p = (void*)((int)pt – 4);

T::operator delete[](p);

}

由此可见,在默认情况下operator new[]与operator new的行为是相同的,operator delete[]与operator delete也是,不同的是new operator与new[] operator、delete operator与delete[] operator。当然,我们可以根据不同的需要来选择重载带有和不带有“[]”的operator new和delete,以满足不

同的具体需求。

把前面类MyClass的代码稍做修改——注释掉析构函数,然后再来看看程序的

输出:

calling new[] with size=12 address=003A5A58

ctor

ctor

ctor

address of mc=003A5A58

这一次,new[]老老实实的申请了12个字节的内存,并且申请的结果与new[] operator返回的结果也是相同的,看来,是否在前面添加4个字节,只取决于这个类有没有析构函数,当然,这么说并不确切,正确的说法是这个类是否需要调用构造函数,因为如下两种情况下虽然这个类没声明析构函数,但还是多申请了4个字节:一是这个类中拥有需要调用析构函数的成员,二是这个类继承自需要调用析构函数的类。于是,我们可以递归的定义“需要调用析构函数

的类”为以下三种情况之一:

1 显式的声明了析构函数的

2 拥有需要调用析构函数的类的成员的

3 继承自需要调用析构函数的类的

类似的,动态申请简单类型的数组时,也不会多申请4个字节。于是在这两种情况下,释放内存时使用delete或delete[]都可以,但为养成良好的习惯,我们还是应该注意只要是动态分配的数组,释放时就使用delete[]。

释放内存时如何知道长度

但这同时又带来了新问题,既然申请无需调用析构函数的类或简单类型的数组时并没有记录个数信息,那么operator delete,或更直接的说free()是如何来回收这块内存的呢?这就要研究malloc()返回的内存的结构了。与new[]类似的是,实际上在 malloc()申请内存时也多申请了数个字节的内容,只不过这与所申请的变量的类型没有任何关系,我们从调用malloc时所传入的参数也可以理解这一点——它只接收了要申请的内存的长度,并不关系这块内存用来保存什么类型。下面运行这样一段代码做个实验:

char *p = 0;

for(int i = 0; i < 40; i += 4)

{

char* s = new char;

printf("alloc %2d bytes, address=%p distance=%dn", i, s, s - p);

p = s;

}

我们直接来看VC2005下Release版本的运行结果,DEBUG版因包含了较多的调

试信息,这里就不分析了:

alloc 0 bytes, address=003A36F0 distance=3815152

alloc 4 bytes, address=003A3700 distance=16

alloc 8 bytes, address=003A3710 distance=16

alloc 12 bytes, address=003A3720 distance=16

alloc 16 bytes, address=003A3738 distance=24

alloc 20 bytes, address=003A84C0 distance=19848

alloc 24 bytes, address=003A84E0 distance=32

alloc 28 bytes, address=003A8500 distance=32

alloc 32 bytes, address=003A8528 distance=40

alloc 36 bytes, address=003A8550 distance=40

每一次分配的字节数都比上一次多4,distance值记录着与上一次分配的差值,第一个差值没有实际意义,中间有一个较大的差值,可能是这块内存已经被分配了,于是也忽略它。结果中最小的差值为16字节,直到我们申请16字节时,这个差值变成了24,后面也有类似的规律,那么我们可以认为申请所得的内

存结构是如下这样的:

从图中不难看出,当我们要分配一段内存时,所得的内存地址和上一次的尾地

址至少要相距8个字节(在DEBUG版中还要更多),那么我们可以猜想,这8

个字节中应该记录着与这段所分配的内存有关的信息。观察这8个节内的内容,

得到结果如下:

图中右边为每次分配所得的地址之前8个字节的内容的16进制表示,从图中红

线所表示可以看到,这8个字节中的第一个字节乘以8即得到相临两次分配时的距离,经过试验一次性分配更大的长度可知,第二个字节也是这个意义,并且代表高8位,也就说前面空的这8个字节中的前两个字节记录了一次分配内存的长度信息,后面的六个字节可能与空闲内存链表的信息有关,在翻译内存时用来提供必要的信息。这就解答了前面提出的问题,原来C/C++在分配内存时已经记录了足够充分的信息用于回收内半

用c语言编写一个成绩管理系统

程序说明:有N个学生,每个学生的数据包含学号(不重复)、姓名、三门课的成绩及平均成绩,试设计一学生成绩管理系统,使之能提供以下功能:(1)主菜单学生成绩管理系统 1、成绩录入 2、成绩查询 3、成绩统计 4、退出(2)各菜单项功能① 成绩录入:输入学生的学号、姓名及三门课的成绩;② 成绩查询:(至少一种查询方式)。 v 按学号查询学生记录。 v 查询不及格学生的记录。③成绩统计: v 计算学生的平均分; v 根据学生的平均分高低,对学生的数据进行排序后输出; v 对学生单科成绩排序,输出学生姓名与该科成绩;④退出系统:退出整个系统(即主菜单)。(3)结构体数组: #define N 30 struct student {int num; /* 定义学号*/ char name[20]; /* 定义姓名*/ float score[3]; /* 定义存贮三门课成绩的数组*/ float average; /* 定义平均成绩*/ };struct student stu[N]; /* 定义结构体数组,存贮多个学生的记录*/ . #include #include #include struct student { int num; char name[20]; float score[4]; float average; } stu[10000]; long t,max; bool unpass[1000]; FILE *fstu=fopen("stud.dat","at+"); int init() { int no,i; float s[4],ave; char nam[20]; while (!feof(fstu)) { fscanf(fstu,"%d",&no); fscanf(fstu,"%s",nam); fscanf(fstu,"%f%f%f%f",&s[1],&s[2],&s[3],&ave);

C语言基础知识_测试题

C语言程序设计基础测试题 一、单选 [1] 下面叙述中错误的是____。 A. 复合语句中定义的函数只在该复合语句中有效 B. return( )语句中的括号中,可以是变量,常量或有确定值的表达式 C. 形式参数也是局部变量 D. 主函数中定义的变量在整个程序中都是有效的 [2]下列说法中正确的是____。 A.带参数的宏定义中的参数是没有类型的 B.宏展开将占用程序的运行时间 C.宏定义命令是C语言中的一种特殊语句 D.使用#include命令包含的头文件必须以“.h"为后缀 [3.] 若函数的形参为一维数组,则下列说法中正确的是____。 A.调用函数时的对应实参必为数组名 B.形参数组可以不指定大小 C.形参数组的元素个数必须等于实参数组的元素个数 D.形参数组的元素个数必须多于实参数组的元素个数 [4]. 系统的标准输出设备是____。 A.键盘 B.硬盘 C.内存 D.显示器 [5] 下面叙述中正确的是____。 A.全局变量在定义它的文件中的任何地方都是有效的 B.全局变量在程序的全部执行过程中一直占用内存单元 C. C语言的switch语句中case后可为常量或表达式或有确定值的变量及表达式 D. 说明函数时必须明确其参数类型和返回类型 [6]. C程序的基本结构单位是____。 A.文件 B.语句 C.函数 D.表达式 [7] 对于定义,char *aa[2]={"abcd","ABCD"},选项中说法正确的是____。 A.aa数组元素的值分别是"abcd"和"ABCD" B.aa是指针变量,它指向含有两个数组元素的字符型一维数组 C.aa数组的两个元素分别存放的是含有4个字符的一维字符数组的首地址 D.aa数组的两个元素中各自存放了字符'a'和'A'的地址 [8]. 任何一个C语言的可执行程序都是从____开始执行的。 A.程序中的第一个函数 B.main( )函数的入口处

用c语言写乘法表

#include int main(void) { int i,j; for (i=1;i<=9;i++) { for(j=1;j<=i;j++) printf("%2dx%d=%2d",j,i,i*j); printf("\n"); } } 结果: 1x1= 1 1x2= 2 2x2= 4 1x3= 3 2x3= 6 3x3= 9 1x4= 4 2x4= 8 3x4=12 4x4=16 1x5= 5 2x5=10 3x5=15 4x5=20 5x5=25 1x6= 6 2x6=12 3x6=18 4x6=24 5x6=30 6x6=36 1x7= 7 2x7=14 3x7=21 4x7=28 5x7=35 6x7=42 7x7=49 1x8= 8 2x8=16 3x8=24 5x8=40 6x8=48 7x8=56 8x8=64 1x9= 9 2x9=18 3x9=27 4x9=36 5x9=45 6x9=54 7x9=63 8x9=72 9x9=81 3.19 #include int main(void) { int a=0; printf("%8s%10s%12s\n","a","b","c"); while(a<=33) { int b=0; while(b<=50) { int c; c=100-a-b; if(3*a+2*b+c/2.0==100) printf("%8d%10d%12d\n",a,b,c); b++; } a++;

} } a b c 2 30 68 5 25 70 8 20 72 11 15 74 14 10 76 17 5 78 20 0 80

谭浩强C语言知识点总结

谭浩强C语言知识点总 结 文件编码(GHTU-UITID-GGBKT-POIU-WUUI-8968)

C语言最重要的知识点总体上必须清楚的: 1)程序结构是三种: 顺序结构、选择结构(分支结构)、循环结构。 2)读程序都要从main()入口, 然后从最上面顺序往下读(碰到循环做循环,碰到选择做选择),有且只有一个main函数。 3)计算机的数据在电脑中保存是以二进制的形式. 数据存放的位置就是他的地址. 4)bit是位是指为0 或者1。 byte 是指字节, 一个字节 = 八个位.概念常考到的: 1、编译预处理不是C语言的一部分,不占运行时间,不要加分号。C语言编译的程序称为源程序,它以ASCII数值存放在文本文件中。 2、define PI ; 这个写法是错误的,一定不能出现分号。 3、每个C语言程序中main函数是有且只有一个。 4、在函数中不可以再定义函数。 5、算法:可以没有输入,但是一定要有输出。 6、break可用于循环结构和switch语句。 7、逗号运算符的级别最低,赋值的级别倒数第二。 第一章 C语言的基础知识 第一节、对C语言的基础认识 1、C语言编写的程序称为源程序,又称为编译单位。 2、C语言书写格式是自由的,每行可以写多个语句,可以写多行。 3、一个C语言程序有且只有一个main函数,是程序运行的起点。

第二节、熟悉vc++ 1、VC是软件,用来运行写的C语言程序。 2、每个C语言程序写完后,都是先编译,后链接,最后运行。(.c---?.obj---?.exe)这个过程中注意.c和.obj文件时无法运行的,只有.exe文件才可以运行。(常考!) 第三节、标识符 1、标识符(必考内容): 合法的要求是由字母,数字,下划线组成。有其它元素就错了。 并且第一个必须为字母或则是下划线。第一个为数字就错了 2、标识符分为关键字、预定义标识符、用户标识符。 关键字:不可以作为用户标识符号。main define scanf printf 都不是关键字。迷惑你的地方If是可以做为用户标识符。因为If中的第一个字母大写了,所以不是关键字。 预定义标识符:背诵define scanf printf include。记住预定义标识符可以做为用户标识符。 用户标识符:基本上每年都考,详细请见书上习题。 第四节:进制的转换 十进制转换成二进制、八进制、十六进制。 二进制、八进制、十六进制转换成十进制。 第五节:整数与实数 1)C语言只有八、十、十六进制,没有二进制。但是运行时候,所有的进制都要转换成二进制来进行处理。(考过两次)

如何用C语言编写小游戏

如何用C语言编写小游戏

纯真童趣的《泡泡堂》,还有武林情仇,笑傲江湖的《剑侠情缘on line》.它是e时代常谈的话题,是交互式娱乐的主力军,是一种 高层次的综合艺术,更是一个民族的文化,世界观的全新传播方式 .作为游戏玩家的我们,是不是想设计一个属于自己的游戏呢? 爱玩是人的天性,而C语言是我们计算机专业都要学习的一门基础 学科.一般来说,是比较枯燥的.那么,我们能不能通过编一些小 游戏来提高它的趣味性呢?这样学习程序设计,就不会是一件艰苦 ,枯燥的事,它变得象电脑游戏一样充满

好奇,富有乐趣.这正是 我发贴的目的. 1, 总是从Hello,world开始 学习编程的第一个程序,一般就是打印一个亲切的词语——"Hell o,world!".让我们来看看这个最简单的C程序: #incolude /*把输入输出函数的头文件包含进来*/ int main() { printf("Hello, world!");/*在屏幕上输出字符串"Hell

o,world!"*/ return 0;/*退出main函数,并返回0*/ } 下面我们发现几个值得改进的地方,1,程序的运行结果一闪而过 .2,每执行这个程序一次都能看见上次运行留下的字符.3,我们 还希望屏幕输出一个笑脸来欢迎我们. (大家不要小看了这个笑脸 曾经有人发贴专门问呢)让我们来改进一下这个程序吧! 1,在return语句的前面加一句:getch ();,表示按任意键结

束.2,在printf语句前用clrscr函数清屏,要使用这个函数和get ch函数,需要在程序开头再包含头文件conio.h.3,ASCII码也有 许多非常好玩的字符,比如ASCII码值为2的就是一个笑脸,我们可 以用printf("%c", 2)来输出一个笑脸. 现在我们把Hello,world程序改成一个更好看的Hello,world了.下 面让我们开始做游戏吧! 2, 心动的开始,一个运动中的笑脸 大家小时侯喜欢看动画片吗?哈哈,我猜你们都喜欢吧!下面就让

C语言文本编辑器

附件二116755043.doc 【学生用】 西北农林科技大学信息工程学院数据结构与C语言综合训练实习报告 题目:文本编辑器 学号2011013228 姓名马跃 专业班级电子商务112 指导教师景旭 实践日期2010年7月5日-7月16日

目录 一、综合训练目的与要求 (1) 二、综合训练任务 (1) 三、总体设计 (1) 四、详细设计说明 (2) 五、调试与测试 (9) 六、实习日志 (14) 七、实习总结 (15) 八、附录:核心代码清单 (15)

一、综合训练目的与要求 正文本综合训练是计算机科学与技术、信息管理与信息系统、软件工程专业重要的实践性环节之一,是在学生学习完《程序设计语言(C)》、《数据结构》课程后进行的一次全面的综合练习。本课综合训练的目的和任务: 1 巩固和加深学生对C语言、数据结构课程的基本知识的理解和掌握 2 掌握C语言编程和程序调试的基本技能 3利用C语言进行基本的软件设计 4 掌握书写程序设计说明文档的能力 5提高运用C语言、数据结构解决实际问题的能力 二、综合训练任务 本综合训练是计算机科学与技术、信息管理与信息系统、软件工程专业重要的实践性环节之一,是在学生学习完《程序设计语言(C)》、《数据结构》课程后进行的一次全面的综合练习。本课综合训练的目的和任务: 1巩固和加深学生对C语言、数据结构课程的基本知识的理解和掌握 2掌握C语言编程和程序调试的基本技能 3利用C语言进行基本的软件设计 4掌握书写程序设计说明文档的能力 5提高运用C语言、数据结构解决实际问题的能力 三、总体设计 1.设计题目------文本编辑系统 2.已知技术参数和设计要求 1分别统计出其中英文字母数和空格数及整篇文章总字数; 2统计某一字符串在文章中出现的次数,并输出该次数;(采用模式匹配算法) 3删除某一子串,并将后面的字符前移; 4实现某一个字符的替换; 5实现某一个字符串的替换;

计算机应用专业c语言编程基础科试卷及答案

职业学校计算机应用专业C语言编程基础科试卷及答案 一、填空(共35分) 1、Unix系统诞生于年,是由实验室的K T和DMR用汇编语言开发成功的。 2、在C语言中,标识符主要为,,及的名字使用。 3、C语言中中的基本数据类型包括、和 三种。 4、数组是有序是的并且有的数据的集合。 5、C语言中,二维数组元素在内存中的存储顺序 是。 6、C语言函数的实参、形参二者类型,一般个数。 7、在C语言中,变量的存储类别有四种,它们是,,和。 8、根据函数能否被其他源文件调用,函数分为函数和函数两类。 9、文件是存储在外部存储设备上的。 10、定义文件指针变量的格式为。 11、feof( )函数是检测函数,当文件位置指针处于时,它返回一个值。 12、用逻辑运算符将或连接起来就是逻辑表达式。 13、结构化程序设计方法,采用的设计原则和的来构造程序。 14、程序结构一般采用、和结构。 15、第三代高级语言是,注重的是, 之间的关系是从属性的层层调用关系。 二、单项选择题(共15分) 1、C语言程序由组成。A. 子程序 B. 主程序和子程序 C. 函数 D. 过程 2、源程序要正确地运行,必须要有什么函数? A. printf函数 B. 自定义的函数 C. main函数 D. 不需要函数 3、若a为整型变量,且有以下语句 a=-017L; printf("%d\n",a); 则下面___说法是正确的? A. 赋值不合法 B. 输出值为-17 C. 输出为不确定值 D. 输出值为-15 4、下面表达式的值为4. A. 11/3 B. 11.0/3 C. (float)11/3 D. (int)(11.0/3+0.5) 5、在C语言的if语句中,用作判断的表达式为___。 A. 关系表达式 B. 逻辑表达式 C. 算术表达式 D. 任意表达式 6、下面哪一项是不正确的字符串赋值或赋初值的方式。 A. char *str; str="string"; B. char str[7]={'s','t','r','i','n','g'}; C. char str1[10];str1="string"; D. char str1[]="string",str2[]="12345678"; 7、若有以下说明和语句,则输出结果是哪一项? (strlen(s)为求字符串s的长度的函数) char s[12]="a book!"; printf("%d",strlen(s)); A. 12 B. 8 C. 7 D. 11 8、C语言可执行程序从什么地方开始执行? A. 程序中第一条可执行语句 B. 程序中第一个函数 C. 程序中的main函数 D. 包含文件中的第一个函数 9、有一个函数原型如下所示: abc(float x,float y); 则该函数的返回类型为___。 A. void B. double C. int D. float 10、在C语言程序中,下面哪一个是正确的? A. 函数的定义可以嵌套,但函数的调用不可以嵌套 B. 函数的定义不可以嵌套,但函数的调用可以嵌套 C. 函数的定义可以嵌套,函数的调用也可以嵌套 D. 函数的定义和函数的调用都不可以嵌套 11、对于类型相同的指针变量,不能进行哪种运算? A. + B. - C. = D. == 12、若有以下说明和语句,且0<=i<10, 则下面是 对数组元素的错误引用? int a[]={1,2,3,4,5,6,7,8,9,0}, *p, i; p=a; A. *(a+i) B. a[p-a] C. p+i D. *(&a[i]) 13、如果想把一些新的数据添加到文件xh.txt中,则 应该以下面方式打开文件? A. fp=fopen(xh.txt,"a"); B.fp=fopen("xh.txt",a); C. fp=fopen("xh.txt","a"); D.fp=fopen("xh.txt",'a'); 14、下面哪条语句是从文件中读取一个字符? A. ch=getc(); B. fputc(ch,fp); C. fscanf("%c",&ch); D. scanf("%c",&ch); 15、C语言中的文件类型如何划分? A. 索引文件和文本文件两种 B. ASCII文件和二进制文件两种 C. 只有文本文件一种 D. 只有二进制文件一种 三、多选题(共20分) 1、下面是定义局部变量储存类别的保留字。

C语言基础知识

常量和变量 1.常量: 程序执行过程中,值不变的量。 3 ,'a' 变量:值可以改变的量。 一个变量有一个名字,在内存中有一定的存储单元,存放变量的值。 2.常量类型: a.整型:12,0,-3 b.实型:4.6,-1.2 c.字符型: 'a','d' d.符号常量: #define PRICE 30 (PRICE不能再被赋值且要大写) 3.变量: 先定义,后使用。一个变量只能被指定为一确定类型。 4.标识符:标识变量名,符号常量名,函数名,数组名,类型名,文件名的有效字符数列。 a.由字母、数字、下划线三种字符组成,第一个字符必须为字母或下划线。 b.大写字母、小写字母被认为是两个不同的字符。 c.长度一般小于8个。 数据类型 一.整型: 1.整型常量 a.十进制:12,-3,0 b.八进制:以0开头。 c.十六进制:以0x开头。 2.整型变量 a. int -32768——32767 b. short int -32768——32767 c. long int d. unsigned int 0——65535 e. unsigned short 0——65535 f. unsigned long int、short int、long int 第一位为符号位 0000001 (0为正,1为负) unsigned 第一位不是符号位 0000001 所以int型和unsigned型的000001不是同一个值。 二.实型: 1.实型常量:

a.十进制数:数字和小数点组成。0.12,.12,12.0,0.0 b.指数:e之前必须有数字,e后面必须为整数。12e3 2.实型变量: a.单精度:float 7位有效数字 111111.1可,111111.11不可。 b.双精度:double 15—16位有效数字。 三.字符型: 1.字符常量: a. 'a' , 'x' , '*' ,'$' 。 b. 转义字符:‘\n'换。 '\t'从第九列开始。'\r'回车。 '\b'退一格。 2.字符变量: char char='a' 一个字符变量在内存占一个字节。 。将一个字符常量放到一个字符变量中,并不是把该字符本身放到内存单元中去,而是将该字符的ASC码 放到存储单元中,所以字符型数据和整型数据之间可以通用。一个字符型数据既可以以字符形式输出, 又可以以整数形式输出。 四.字符串常量: "how are you", "a","&12" 。不能把一个字符串赋给一个字符变量。 char c='a'对,char c="how" 错。 。'a' :在内存中存a。 “a”:在内存中存a\0。 ‘\0’是C语言中判断字符串是否结束的标志。 变量赋初值 a. int a=3; float f=7.2; char c='a'; b. int a,b,c=5; 相当于 int a,b,c; c=5; c. int a=3;b=3;c=3; 不可写: int a=b=c=3;

用C语言编写程序建立一个pipe

1、用C语言编写程序,建立一个pipe, 同时父进程生成一个子进程,子进程向pipe写入一 个字符串”Hello.”,父进程从pipe中读取该字符串。5' #include #include #include #include #include #include int main() { char buf[20]; int piledes[2]; pid_t pid; int ret; if(ret = pipe(piledes) == -1) { perror("error on pipe:"); exit(-1); } else { pid = fork(); if(pid < 0) { perror("error on fork:"); exit(-1); } else if(pid == 0) { close(piledes[0]); printf("to fu:"); fgets(buf,sizeof(buf)-1,stdin); if((ret = write(piledes[1],buf,strlen(buf))) < 0) { perror("error on writing:"); exit(-1); } close(piledes[1]);

} else { close(piledes[1]); if((ret = read(piledes[0],buf,sizeof(buf)-1)) < 0) { perror("error on reading:"); exit(-1); } buf[ret] = '\0'; printf("from zi:%s\n",buf); close(piledes[0]); } } return 0; } 2、编写一个 C语言程序lswc,使之功能能和ls | wc 等价。(也就是利用,fork,exec,以及ls,wc,以及重定向,或者是管道的系统调用) 13’ #include #include #include #include #include #include #include #include #include #include #include #include #include #include int main(int argc,char *argv[]) { int pildes[2]; int ret; char *argument[3]; pid_t pid;

大学c语言必背基础知识_c语言基础知识大全

大学c语言必背基础知识_c语言基础知识大全 对于刚学计算机编程的同学来说,没一个编程知识都觉得很重要,其实不是的。下面小编为大家整理了相关大学c语言必背基础知识,希望大家喜欢。 大学c语言必背基础知识举例说明: printf(“-”,123 ); 第二部分有三位,大于指定的两位,原样输出123 printf(“]”,123 ); 第二部分有三位,小于指定的五位,左边补两个空格123 printf(“f”,1.25 ); 小数要求补足6位的,没有六位的补0,。结果为1.250000 printf(“%5.3f”,125 ); 小数三位,整个五位,结果为1.250(小数点算一位) printf(“%3.1f”,1.25 );小数一位,整个三位,结果为1.3(要进行四舍五入) 第三节数据输入1、scanf(“a=%d,b=%d”,">2、scanf(“%d,%d”,x,y);这种写法绝对错误,scanf的第二个部分一定要是地址!scanf(“%d,%d”,注意写成这样才可以! 3、特别注意指针在scanf的考察例如:int x=2;int *p=scanf(“%d”,x); 错误scanf(“%d”,p);正确scanf(“%d”,错误scanf(“%d”,*p)错误 4、指定输入的长度(考试重点)终端输入:1234567scanf(“-M%d”,x为12,y为3456,z为7终端输入:1 234567 由于1和2中间有空格,所以只有1位给xscanf(“-M%d”,x 为1,y为2345,z为67 5、字符和整型是近亲:int x=97;printf(“%d”,x); 结果为97printf(“%c”,x); 结果为a 6、输入时候字符和整数的区别(考试超级重点) scanf(“%d”,这个时候输入1,特别注意表示的是整数1 scanf(“%c”,这个时候输入1,特别注意表示的是字符‘1’ASCII为整数48。 补充说明: 1)scanf函数的格式考察: 注意该函数的第二个部分是scanf(“%d%d%*d%d”,跳过输入的第三个数据。 2)putchar ,getchar 函数的考查:

C语言期末复习文档.docx

期末考试题型: 程序设计基础题型举例 ?、选择题 1、 C 语言规定:在一个源程序中,main 函数的位置—B ________ 。 A. 只能在程序头部; B.可以在程序任何位 置; C.必须在#include ^Uttdefine 语句后面; D.只能在程序最后面。 2、 在使用TurboC2.0环境编辑程序时,运行某程序使用的菜单项为_A_。 A. RUN B. FILE C. EDIT D. OPTION 3、 下面标识符中止确的是—B _______ o A. $a#b B. sum C. 1A23BC D.Y ?M ?D 4、 在C 语言屮(以16位PC 机为例),存储一个整型、字符型、双精度实型变量 所需的字节数是 ____ C_0 A.4、1、4 B.4、1、4 C.2、1、8 D. 2、2、8 5、 下面的变量说明中 _____ B ________ 是止确的。 A. Char : a, b, c ; B ? Char a, b, c ; C. Char a ; b ; c ; D. Char a, b, c 6、判断char 型变量ch 是否为大写字母的止确表达式是—C ________ C. (ch>=/A ,)&&(ch<=T) D. (W<= ch)AND(/Z ,>= ch) 7、已知int x=10,y=20,z=30;以下语句执行后x,y,z 的值是 ____ D _____ if(x>y) z=x; x=y; y=z; 、 选择题 30分 二、 填空题或程序填空 三、 判断题 10分 四、 分析题 20分 五、 编程题 20— 20分 30分 A. /A ,<=ch<=7, B. (ch>=W)&(ch<=T)

C语言基础知识(详细版)

C语言程序的结构认识 用一个简单的c 程序例子,介绍c 语言的基本构成、格式、以及良好的书写风格,使小伙伴对 c 语言有个 初步认识。 例1:计算两个整数之和的c 程序: #include main() { int a,b,sum; /* 定义变量a,b ,sum 为整型变量*/ a=20; /* 把整数20 赋值给整型变量a*/ b=15; /* 把整数15 赋值给整型变量b*/ sum=a+b; /* 把两个数之和赋值给整型变量sum*/ printf( “ a=%d,b=%d,sum=%d\n” ,a,b,sum); /* 把计算结果输出到显示屏上*/ } 重点说明: 1、任何一个c 语言程序都必须包括以下格式: main() { } 这是c 语言的基本结构,任何一个程序都必须包含这个结构。括号内可以不写任何内容,那么该程序将不执行任何结果。 2、main() - 在c 语言中称之为“主函数” ,一个c 程序有且仅有一个main 函数,任何一个c 程序总是从 main 函数开始执行,main 函数后面的一对圆括号不能省略。 3、被大括号{ }括起来的内容称为main 函数的函数体,这部分内容就是计算机要执行的内容。 4、在{ }里面每一句话后面都有一个分号(; ),在c 语言中,我们把以一个分号结尾的一句话叫做一个 c 语 言的语句,分号是语句结束的标志。 5、printf( “ a=%d,b=%d,sum=%d\n” ,a,b,sum); 通过执行这条c 语言系统提供给我们直接使用的屏幕输出 函数,用户即可看到运行结果,本程序运行后,将在显示器上显示如下结果: a=20,b=15,sum=35 6、#include 注意:(1)以#号开头 (2)不以分号结尾这一行没有分号,所以不是语句,在c 语言中称之为命令行,或者叫做“预编译处理命令” 。 7、程序中以/* 开头并且以*/ 结尾的部分表示程序的注释部分,注释可以添加在程序的任何位置,为了提高程序的可读性而添加,但计算机在执行主函数内容时完全忽略注释部分,换而言之就是计算机当做注释部分不存在于主函数中。 C程序的生成过程 C程序是先由源文件经编译生成目标文件,然后经过连接生成可执行文件。 源程序的扩展名为.c ,目标程序的扩展名为.obj , 可执行程序的扩展名为.exe 。

用C语言编写简单的接口程序

用C语言编写简单的接口程序 北京理工大学(100081) 张俊 在通信中,为了保证运行安全可靠,标准的串行口必须具有许多握手信号和状态信息。这是因为通信的各个计算机CPU速度不一样(这会导致?错帧敚?以及发送机发送数据速度比接收机接收速度快(这会导致?过冲敚?为解决这个问题,我们采用一个简单的握手信号,即发送机每次仅发送半个字节(低4位)的数据,而另外半个字节(高4位)则用来传送信息。我们可以对信息位(高4位)进行如下简单的编码: 0H:发送的是新的半个字节数据 1H:重新发送上次传送错误的数据 2H:文件名结束 3H:文件结束 这样,每当发送机发送一个字节以后,就等待接受机发回送信号,这回送信号就是发送机发送过来的那个字节。发送机接收到回送信号后,把它与刚发送的字节相比较,如果相同,就发送新的半个字节,否则就重新发送。新数据与旧数据通过信息位来区分。下面就是我用C语言编写控制串行口的程序。以一个发送文件的程序为例,介绍一下用C语言实现对接口的控制。 源程序为: #include “dos.h" #include “stdlib.h" #include “stdio.h" #define PORT 0 void SendFile(char fname); / *发送文件*/ void Send(int s); / *发送一个字节*/ void SendFileName(char fname); / *发送文件名*/ void ReceiveFile(); / *接收文件*/ void GetFileName(char f); / *接收文件名*/ void InitPort(int port,unsigned char para); / *初始化端口*/ void SendPort(int port,char c); / *端口发送*/ int ReadPort(int port); / *读端口字节*/ int CheckState(int port); / *检查端口状态*/ int Receive(int port,int G); / *接收一个字节*/

C语言基础知识归纳

C 语言基础知识 1. 每个C 程序有且只有一个主函数main() ,且程序必须从main() 函 数开始执行,并在 main() 函数中结束。 2. 在 C 语言中,用 e 来表示科学计数法时,规定在 e 的前面必须有 数字,后面必须为整数。 3. 用单引号括起来的一个字符常量只能存放一个字符;C 语言中没有 字符串变量,只能用字符数组来存储字符串。 4. 外部变量在编译时由系统分配永久的内存空间,所以外部变量的类 型不是自动存储类别。 5. 在一个函数内的复合语句中定义的变量,只能在这个复合语句范围 内有效。 6. 用 sizeof(int) 计算 int 类型数据的字节数。 7. C 语言运行时,首先系统检查语法的正误,再运行程序的语法; C语言中,可以在一个函数中嵌套一个函数,但是不能在一 个函数中定义一个函数;只有在函数外部定义的函数才是外部函数; C语言的子程序有过程和函数两种。 8. 预处理命令行的位置没有规定,只是习惯放在前面;在源文件中的 可以有多条预处理命令,但一行只能写一条;宏名的大写只是习惯性的表示;宏替换不仅不占用运行时间还不分配内存空间。 9. feo f函数的作用是检查文件是否结束,当结束时返回的值为非零, 否则为零。 10. 当定义了数组后,在给其赋值时,不能直接把字符串赋给数

组名。 11. 在赋值表达式中,赋值运算符“=”右侧不能为表达式;在求余运 算符中的两侧都必须为整型;在强制类型转换时,类型名应用括号括起来。 12. 静态局部变量,只有当程序结束后,其值才释放。 13. 当调用函数时,实参是一个数组名,则向函数传送的是数组每一个 元素的地址。 14. 算法的特点为有零个或多个输入,有一个或多个输出,当相同的输 入时,其结果相同;算法正确的程序最终一定会结束。 15. 在 C 语言中,预处理命令行都以“ # ”开头;当需要时才用 #in clude;预处理的作用就是实现宏定义和条件编译。16. 当数组元素的下标超出了定义的下标范围时,系统不给出“下标 越界”的字样,而是得出错误的答案,因此在编程时务必检查下标是否越界。 17. 共用体变量不能作为函数的参数,也不能使函数带回共用体变量。 18. 指向函数的指针变量的定义形式为:数据类型(* 指针变量) ();,因此其为指向函数的指针变量。 19. 用 C 语言编写的代码程序是源程序,只有通过编译、连接才能进 行。 20. 在说明为static时,其生存期得到延长,而其作用域并没有改 变,在定义后系统会自动帮它赋值为 0, static 为关键字不能 被用作它用。

C语言与数据结构实验指导(完整版)

Harbin Institute of Technology C语言与数据结构 实验指导书 刘梅索莹田文龙 哈工大电子与信息工程学院 电子工程系

实验1实验平台 一、实验目的 1.掌握Microsoft Visual C++ 6.0集成环境的使用方法。 2.掌握C程序在Microsoft Visual C++ 6.0开发环境中的编辑、编译、链接和运行全过程 二、实验内容 1)启动Microsoft Visual C++ 6.0开发环境 双击桌面应用程序图标或云兄“开始”菜单程序组中的Microsoft Visual C++ 6.0应用程序,启动VC++,如图所示 图1.1 VC++初始界面 2)建立C源程序文件 方法1:单机工具栏的“新建文本文件”按钮,打开文本文件编辑界面如下图所示 图1.2文本文件编辑界面 方法2:执行“文件”->“新建”命令,在“文件”选项卡下选择C++ Source File 文件类型,然后输入C源程序文件名和保存文职,如图所示,然后单击“确定”按钮,打开源程序文件编辑界面,如图1.4所示。

注意:输入C源程序文件名时必须带上扩展“.c”,否则默认创建的是扩展名为“.cpp”的C++文件。 3)编辑源文件 方法1:在如图1.2所示的文本文件编辑界面中输入源程序代码,如图1.5所示。方法2:在如图1.4所示的C源程序文件编辑界面中编辑源程序代码,如图1.6所示。 图1.3新建文件 图1.4 C源程序文件编辑界面

图1.5文本文件编辑界面编辑源文件 图1.6 C源程序编辑界面编辑源文件 4)保存源文件 源文件编辑结束后,执行“文件”->“保存”命令保存文件,文本文件编辑界面中编辑的源文件保存时必须在文件名后加上扩展名“.c”,否则保存的是扩展名为txt的文本文件,不能编译运行。 5)组件文件 执行“组建”->“组建”命令或直接按F7功能键或单机工具栏Build按钮,可以对源文件进行编译、链接而不运行改程序。当然也可以先执行“组建”->“编译”(快捷键Ctrl+F7)命令编译文件,再执行“组建”->“组建”(快捷键F7)命令链接文件。 由于VC++有工作区的要求,所以组建时,系统提示需要建立工作区,如图1.7所示。单机“是”按钮,系统会自动建立工作区,组建后的结果如图1.8所示。

C语言基础知识总复习(考前突击专用)

C语言基础知识总复习(考前突击专用) 总体上必须清楚的: 1)程序结构是三种: 顺序结构、选择结构(分支结构)、循环结构。 2)读程序都要从main()入口, 然后从最上面顺序往下读(碰到循环做循环,碰到选择做选择),有且只有一个main函数。 3)计算机的数据在电脑中保存是以二进制的形式. 数据存放的位置就是他的地址. 4)bit是位是指为0 或者1。byte是指字节, 一个字节= 八个位. 概念常考到的: 1、编译预处理不是C语言的一部分,不占运行时间,不要加分号。C语言编译的程序称为源程序,它以ASCII数值存放在文本文件中。 2、define PI 3.1415926; 这个写法是错误的,一定不能出现分号。 3、每个C语言程序中main函数是有且只有一个。 4、在函数中不可以再定义函数。 5、算法:可以没有输入,但是一定要有输出。 6、break可用于循环结构和switch语句。 7、逗号运算符的级别最低,赋值的级别倒数第二。 第一章C语言的基础知识 第一节、对C语言的基础认识 1、C语言编写的程序称为源程序,又称为编译单位。 2、C语言书写格式是自由的,每行可以写多个语句,可以写多行。 3、一个C语言程序有且只有一个main函数,是程序运行的起点。 第二节、熟悉vc++ 1、VC是软件,用来运行写的C语言程序。

2、每个C语言程序写完后,都是先编译,后链接,最后运行。(.c---→.obj---→.exe)这个过程中注意.c和.obj文件时无法运行的,只有.exe文件才可以运行。(常考!) 第三节、标识符 1、标识符(必考内容): 合法的要求是由字母,数字,下划线组成。有其它元素就错了。 并且第一个必须为字母或则是下划线。第一个为数字就错了 2、标识符分为关键字、预定义标识符、用户标识符。 关键字:不可以作为用户标识符号。main define scanf printf 都不是关键字。迷惑你的地方If是可以做为用户标识符。因为If中的第一个字母大写了,所以不是关键字。 预定义标识符:背诵define scanf printf include。记住预定义标识符可以做为用户标识符。 用户标识符:基本上每年都考,详细请见书上习题。 第四节:进制的转换 十进制转换成二进制、八进制、十六进制。 二进制、八进制、十六进制转换成十进制。 第五节:整数与实数 1)C语言只有八、十、十六进制,没有二进制。但是运行时候,所有的进制都要转换成二进制来进行处理。(考过两次) a、C语言中的八进制规定要以0开头。018的数值是非法的,八进制是没有8的,逢8进1。 b、C语言中的十六进制规定要以0x开头。 2)小数的合法写法:C语言小数点两边有一个是零的话,可以不用写。 1.0在C语言中可写成1. 0.1在C语言中可以写成.1。 3)实型数据的合法形式:

C语言编译全过程介绍

C语言的编译链接过程要把我们编写的一个c程序(源代码)转换成可以在硬件上运行的程序(可执行代码),需要进行编译和链接。编译就是把文本形式源代码翻译为机器语言形式的目标文件的过程。链接是把目标文件、操作系统的启动代码和用到的库文件进行组织,形成最终生成可执行代码的过程。过程图解如下: 从图上可以看到,整个代码的编译过程分为编译和链接两个过程,编译对应图中的大括号括起的部分,其余则为链接过程。 1.编译过程 编译过程又可以分成两个阶段:编译和汇编。 编译 编译是读取源程序(字符流),对之进行词法和语法的分析,将高级语言指令转换为功能等效的汇编代码,源文件的编译过程包含两个主要阶段: 编译预处理 读取c源程序,对其中的伪指令(以# 开头的指令)和特殊符号进行处理。 伪指令主要包括以下四个方面:

1)宏定义指令,如# define Name TokenString,# undef等。 对于前一个伪指令,预编译所要做的是将程序中的所有Name用TokenString替换,但作为字符串常量的 Name则不被替换。对于后者,则将取消对某个宏的定义,使以后该串的出现不再被替换。 2)条件编译指令,如# ifdef,# ifndef,# else,# elif,# endif等。 这些伪指令的引入使得程序员可以通过定义不同的宏来决定编译程序对哪些代码进行处理。预编译程序将根据有关的文件,将那些不必要的代码过滤掉。 3)头文件包含指令,如# include "FileName" 或者# include < FileName> 等。 在头文件中一般用伪指令# define定义了大量的宏(最常见的是字符常量),同时包含有各种外部符号的声明。 采用头文件的目的主要是为了使某些定义可以供多个不同的C源程序使用。因为在需要用到这些定义的C源程序中,只需加上一条# include语句即可,而不必再在此文件中将这些定义重复一遍。预编译程序将把头文件中的定义统统都加入到它所产生的输出文件中,以供编译程序对之进行处理。 包含到c源程序中的头文件可以是系统提供的,这些头文件一般被放在/ usr/ include目录下。在程序中# include它们要使用尖括号(< >)。另外开发人员也可以定义自己的头文件,这些文件一般与c源程序放在同一目录下,此时在# include中要用双引号("")。 4)特殊符号,预编译程序可以识别一些特殊的符号。 例如在源程序中出现的LINE标识将被解释为当前行号(十进制数),FILE则被解释为当前被编译的C源程序的名称。预编译程序对于在源程序中出现的这些串将用合适的值进行替换。 预编译程序所完成的基本上是对源程序的“替代”工作。经过此种替代,生成一个没有宏定义、没有条件编译指令、没有特殊符号的输出文件。这个文件的含义同没有经过预处理的源文件是相同的,但内容有所不同。下一步,此输出文件将作为编译程序的输入而被翻译成为机器指令。 编译、优化阶段 经过预编译得到的输出文件中,只有常量;如数字、字符串、变量的定义,以及C语言的关键字,如main, if , else , for , while , { , } , + , - , * , \ 等等。 编译程序所要作得工作就是通过词法分析和语法分析,在确认所有的指令都符合语法规则之后,将其翻译成等价的中间代码表示或汇编代码。 优化处理是编译系统中一项比较艰深的技术。它涉及到的问题不仅同编译技术本身有关,而且同机器的硬件环境也有很大的关系。优化一部分是对中间代码的优化。这种优化不依赖于具体的计算机。另一种优化则主要针对目标代码的生成而进行的。 对于前一种优化,主要的工作是删除公共表达式、循环优化(代码外提、强度削弱、变换循环控制条件、已知量的合并等)、复写传播,以及无用赋值的删除,等等。 后一种类型的优化同机器的硬件结构密切相关,最主要的是考虑是如何充分利用机器的各个硬件寄存器存放有关变量的值,以减少对于内存的访问次数。另外,如何根据机器硬件执行指令的特点(如流水线、RISC、CISC、VLIW等)而对指令进行一些调整使目标代码比较短,执行的效率比较高,也是一个重要的研究课题。 经过优化得到的汇编代码必须经过汇编程序的汇编转换成相应的机器指令,方可能被机器执行。

相关主题
文本预览
相关文档 最新文档