C++c语言程序设计教程计

C++编程入门系列_图文_百度文库
两大类热门资源免费畅读
续费一年阅读会员,立省24元!
C++编程入门系列
上传于||文档简介
&&鸡​啄​米​系​列​教​程​整​理​P​D​F
阅读已结束,如果下载本文需要使用
想免费下载本文?
下载文档到电脑,查找使用更方便
还剩178页未读,继续阅读
你可能喜欢C和C++混合编程(转) - 原来... - 博客园
在用C++的项目源码中,经常会不可避免的会看到下面的代码:
#ifdef __cplusplus
extern "C" {
#ifdef __cplusplus
它到底有什么用呢,你知道吗?而且这样的问题经常会出现在面试or笔试中。下面我就从以下几个方面来介绍它:
1、#ifdef _cplusplus/#endif _cplusplus及发散
2、extern "C"
2.1、extern关键字
2.3、小结extern "C"
3、C和C++互相调用 4、C和C++混合调用特别之处函数指针
3.1、C++的编译和连接
3.2、C的编译和连接
3.3、C++中调用C的代码
3.4、C中调用C++的代码
1、#ifdef _cplusplus/#endif _cplusplus及发散
在介绍extern "C"之前,我们来看下#ifdef _cplusplus/#endif _cplusplus的作用。很明显#ifdef/#endif、#ifndef/#endif用于条件编译,#ifdef _cplusplus/#endif _cplusplus&&表示如果定义了宏_cplusplus,就执行#ifdef/#endif之间的语句,否则就不执行。
在这里为什么需要#ifdef _cplusplus/#endif _cplusplus呢?因为C语言中不支持extern "C"声明,如果你明白extern "C"的作用就知道在C中也没有必要这样做,这就是条件编译的作用!在.c文件中包含了extern "C"时会出现编译时错误。
既然说到了条件编译,我就介绍它的一个重要应用&&避免重复包含头文件。还记得腾讯笔试就考过这个题目,给出类似下面的代码(下面是我最近在研究的一个开源web服务器&&Mongoose的头文件mongoose.h中的一段代码):
#ifndef MONGOOSE_HEADER_INCLUDED
#define&&& MONGOOSE_HEADER_INCLUDED
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
#ifdef __cplusplus
#endif /* __cplusplus */
#endif /* MONGOOSE_HEADER_INCLUDED */
然后叫你说明上面宏#ifndef/#endif的作用?为了解释一个问题,我们先来看两个事实:
这个头文件mongoose.h可能在项目中被多个源文件包含(#include "mongoose.h"),而对于一个大型项目来说,这些冗余可能导致错误,因为一个头文件包含类定义或inline函数,在一个源文件中mongoose.h可能会被#include两次(如,a.h头文件包含了mongoose.h,而在b.c文件中#include a.h和mongoose.h)&&这就会出错(在同一个源文件中一个结构体、类等被定义了两次)。
从逻辑观点和减少编译时间上,都要求去除这些冗余。然而让程序员去分析和去掉这些冗余,不仅枯燥且不太实际,最重要的是有时候又需要这种冗余来保证各个模块的独立。
为了解决这个问题,上面代码中的
#ifndef MONGOOSE_HEADER_INCLUDED #define&&& MONGOOSE_HEADER_INCLUDED /*&&&&&&&&&&&*/ #endif /* MONGOOSE_HEADER_INCLUDED */
就起作用了。如果定义了MONGOOSE_HEADER_INCLUDED,#ifndef/#endif之间的内容就被忽略掉。因此,编译时第一次看到mongoose.h头文件,它的内容会被读取且给定MONGOOSE_HEADER_INCLUDED一个值。之后再次看到mongoose.h头文件时,MONGOOSE_HEADER_INCLUDED就已经定义了,mongoose.h的内容就不会再次被读取了。
2、extern "C"
首先从字面上分析extern "C",它由两部分组成&&extern关键字、"C"。下面我就从这两个方面来解读extern "C"的含义。
2.1、extern关键字
在一个项目中必须保证函数、变量、枚举等在所有的源文件中保持一致,除非你指定定义为局部的。首先来一个例子:
&&&&int x=1;
&&&&int f(){do something here}
&&&&extern int x;
&&&&int f();
&&&&void g(){x=f();}
在file2.c中g()使用的x和f()是定义在file1.c中的。extern关键字表明file2.c中x,仅仅是一个变量的声明,其并不是在定义变量x,并未为x分配内存空间。变量x在所有模块中作为一种全局变量只能被定义一次,否则会出现连接错误。但是可以声明多次,且声明必须保证类型一致,如:
&&&&int x=1;
&&&&int b=1;
&&&&extern c;
&&&&int x;
&&&&int f();
&&&&extern double b;
&&&&extern int c;
在这段代码中存在着这样的三个错误:
x被定义了两次
b两次被声明为不同的类型
c被声明了两次,但却没有定义
回到extern关键字,extern是C/C++语言中表明函数和全局变量作用范围(可见性)的关键字,该关键字告诉编译器,其声明的函数和变量可以在本模块或其它模块中使用。通常,在模块的头文件中对本模块提供给其它模块引用的函数和全局变量以关键字extern声明。例如,如果模块B欲引用该模块A中定义的全局变量和函数时只需包含模块A的头文件即可。这样,模块B中调用模块A中的函数时,在编译阶段,模块B虽然找不到该函数,但是并不会报错;它会在连接阶段中从模块A编译生成的目标代码中找到此函数。
与extern对应的关键字是 static,被它修饰的全局变量和函数只能在本模块中使用。因此,一个函数或变量只可能被本模块使用时,其不可能被extern &C&修饰。
典型的,一个C++程序包含其它语言编写的部分代码。类似的,C++编写的代码片段可能被使用在其它语言编写的代码中。不同语言编写的代码互相调用是困难的,甚至是同一种编写的代码但不同的编译器编译的代码。例如,不同语言和同种语言的不同实现可能会在注册变量保持参数和参数在栈上的布局,这个方面不一样。
为了使它们遵守统一规则,可以使用extern指定一个编译和连接规约。例如,声明C和C++标准库函数strcyp(),并指定它应该根据C的编译和连接规约来链接:
extern "C" char* strcpy(char*,const char*);
注意它与下面的声明的不同之处:
extern char* strcpy(char*,const char*);
下面的这个声明仅表示在连接的时候调用strcpy()。
extern "C"指令非常有用,因为C和C++的近亲关系。注意:extern "C"指令中的C,表示的一种编译和连接规约,而不是一种语言。C表示符合C语言的编译和连接规约的任何语言,如Fortran、assembler等。
还有要说明的是,extern "C"指令仅指定编译和连接规约,但不影响语义。例如在函数声明中,指定了extern "C",仍然要遵守C++的类型检测、参数转换规则。
再看下面的一个例子,为了声明一个变量而不是定义一个变量,你必须在声明时指定extern关键字,但是当你又加上了"C",它不会改变语义,但是会改变它的编译和连接方式。
如果你有很多语言要加上extern "C",你可以将它们放到extern "C"{ }中。
2.3、小结extern "C"
通过上面两节的分析,我们知道extern "C"的真实目的是实现类C和C++的混合编程。在C++源文件中的语句前面加上extern "C",表明它按照类C的编译和连接规约来编译和连接,而不是C++的编译的连接规约。这样在类C的代码中就可以调用C++的函数or变量等。(注:我在这里所说的类C,代表的是跟C语言的编译和连接方式一致的所有语言)
3、C和C++互相调用
我们既然知道extern "C"是实现的类C和C++的混合编程。下面我们就分别介绍如何在C++中调用C的代码、C中调用C++的代码。首先要明白C和C++互相调用,你得知道它们之间的编译和连接差异,及如何利用extern "C"来实现相互调用。
3.1、C++的编译和连接
C++是一个面向对象语言(虽不是纯粹的面向对象语言),它支持函数的重载,重载这个特性给我们带来了很大的便利。为了支持函数重载的这个特性,C++编译器实际上将下面这些重载函数:
void print(int i);
void print(char c);
void print(float f);
void print(char* s);
_print_int
_print_char
_print_float
_pirnt_string
这样的函数名,来唯一标识每个函数。注:不同的编译器实现可能不一样,但是都是利用这种机制。所以当连接是调用print(3)时,它会去查找_print_int(3)这样的函数。下面说个题外话,正是因为这点,重载被认为不是多态,多态是运行时动态绑定(&一种接口多种实现&),如果硬要认为重载是多态,它顶多是编译时&多态&。
C++中的变量,编译也类似,如全局变量可能编译g_xx,类变量编译为c_xx等。连接是也是按照这种机制去查找相应的变量。
3.2、C的编译和连接
C语言中并没有重载和类这些特性,故并不像C++那样print(int i),会被编译为_print_int,而是直接编译为_print等。因此如果直接在C++中调用C的函数会失败,因为连接是调用C中的print(3)时,它会去找_print_int(3)。因此extern "C"的作用就体现出来了。
3.3、C++中调用C的代码
假设一个C的头文件cHeader.h中包含一个函数print(int i),为了在C++中能够调用它,必须要加上extern关键字(原因在extern关键字那节已经介绍)。它的代码如下:
#ifndef C_HEADER
#define C_HEADER
extern void print(int i);
#endif C_HEADER
相对应的实现文件为cHeader.c的代码为:
#include &stdio.h&
#include "cHeader.h"
void print(int i)
&&&&printf("cHeader %d\n",i);
现在C++的代码文件C++.cpp中引用C中的print(int i)函数:
extern "C"{
#include "cHeader.h"
int main(int argc,char** argv)
&&&&print(3);
&&&&return 0;
执行程序输出:
3.4、C中调用C++的代码
现在换成在C中调用C++的代码,这与在C++中调用C的代码有所不同。如下在cppHeader.h头文件中定义了下面的代码:
#ifndef CPP_HEADER
#define CPP_HEADER
extern "C" void print(int i);
#endif CPP_HEADER
相应的实现文件cppHeader.cpp文件中代码如下:
#include "cppHeader.h"
#include &iostream&
using namespace
void print(int i)
&&&&cout&&"cppHeader "&&i&&
在C的代码文件c.c中调用print函数:
extern void print(int i);
int main(int argc,char** argv)
&&&&print(3);
&&&&return 0;
注意在C的代码文件中直接#include "cppHeader.h"头文件,编译出错。而且如果不加extern int print(int i)编译也会出错。
4、C和C++混合调用特别之处函数指针
当我们C和C++混合编程时,有时候会用一种语言定义函数指针,而在应用中将函数指针指向另一中语言定义的函数。如果C和C++共享同一中编译和连接、函数调用机制,这样做是可以的。然而,这样的通用机制,通常不然假定它存在,因此我们必须小心地确保函数以期望的方式调用。
而且当指定一个函数指针的编译和连接方式时,函数的所有类型,包括函数名、函数引入的变量也按照指定的方式编译和连接。如下例:
typedef int (*FT) (const void* ,const void*);
extern "C"{
&&&&typedef int (*CFT) (const void*,const void*);
&&&&void qsort(void* p,size_t n,size_t sz,CFT cmp);
void isort(void* p,size_t n,size_t sz,FT cmp);
void xsort(void* p,size_t n,size_t sz,CFT cmp);
extern "C" void ysort(void* p,size_t n,size_t sz,FT cmp);
int compare(const void*,const void*);
extern "C" ccomp(const void*,const void*);
void f(char* v,int sz)
&&&&qsort(v,sz,1,&compare);
&&&&qsort(v,sz,1,&ccomp);
&&&&isort(v,sz,1,&compare);
&&&&isort(v,sz,1,&ccopm);
注意:typedef int (*FT) (const void* ,const void*),表示定义了一个函数指针的别名FT,这种函数指针指向的函数有这样的特征:返回值为int型、有两个参数,参数类型可以为任意类型的指针(因为为void*)。
最典型的函数指针的别名的例子是,信号处理函数signal,它的定义如下:
typedef void (*HANDLER)(int);
HANDLER signal(int ,HANDLER);
上面的代码定义了信函处理函数signal,它的返回值类型为HANDLER,有两个参数分别为int、HANDLER。 这样避免了要这样定义signal函数:
void (*signal (int ,void(*)(int) ))(int)
比较之后可以明显的体会到typedef的好处。
随笔 - 210& 鸡啄米:C++编程入门系列之目录和总结
鸡啄米:C++编程入门系列之目录和总结
& & & &目前该教程可以到去学习,阅读体验更好,更适合在线学习。&&&&&&&鸡啄米的这套C++编程入门教程到上一讲--C++编程入门系列之五十(异常处理)为止,就算圆满完成了。大家学到这里应该对C++的基础知识和程序设计都有了一定的认识了,以后要做的就是多练习多积累经验了。&&&&&&&一.目录&&&&&& 之前鸡啄米都是一节一节的讲,也没有明确给出本套教程的组织结构,大家只能一节一节的跟着学。此教程共有五十二讲:前言、五十讲入门知识和这一节的总结。下面鸡啄米就概括出这套C++编程入门教程的目录:&&&&&& 第一部分:C++编程概述&&&&&& &&&&&&&&&&&&& &&&&&& 第二部分:C++简单程序设计&&&&&& &&&&&& &&&&&& &&&&&& &&&&&&&&&&&&&&&&&&&& 第三部分:函数&&&&&& &&&&&&&&&&&&& &&&&&& 第四部分:类与对象&&&&&&&&&&&&& &&&&&&&&&&&&& &&&&&&&&&&&&& &&&&&& 第五部分:C++程序设计必知&&&&&& &&&&&&&&&&&&&&&&&&&& &&&&&& &&&&&&&&&&&&&&&&&&&& 第六部分:数组、指针和字符串&&&&&& &&&&&&&&&&&&& &&&&&&&&&&&&& &&&&&& &&&&&& &&&&&& &&&&&& &&&&&& &&&&&& 第七部分:继承与派生&&&&&& &&&&&& &&&&&&&&&&&&& &&&&&&&&&&&&& &&&&&& &&&&&&&&&&&&& &&&&&& 第八部分:多态性&&&&&& &&&&&&&&&&&&&&&&&&&& &&&&&&&&&&&&& &&&&&&&第九部分:异常处理&&&&&&&&&&&&&二.总结&&&&&& 平时写程序总会用到C++语言的各种基础知识,但从来没有这样根据以前自己的学习,将C++语法知识和编程方法及思想等总结之后写下来。&&&&&& 在工作之余写下这套教程,不但让我自己对C++基础知识掌握的更牢固,对C++编程思想有了更深入的认识,而且更重要的是能与很多想步入C++编程殿堂的朋友分享这些知识。&&&&&&&大家学完这套教程以后可能还是感觉不太自信,认为自己还是写不了程序。没关系,看看别人写的程序,可以是稍微复杂点的,自己稍加修改再测试运行看看效果,经过这样的练习以后就可以自己试着设计并编写程序了。谁都是这时候过来的,要有耐心和勇于尝试。&&&&& &记住,学C++就是在练内功,练好了内功再就是招式上的学习和熟悉了。学好了C++,可以做VC界面开发,也可以做Linux下的嵌入式开发,又或者是网络方面等等,但C++是基础(当然也不是只能用C++语言进行这些开发)。&&&&& &建议大家可以再学学MFC(微软基础类库),做一些简单的界面,这样能清楚的看到C++能够实现的功能,增加自己的成就感,还能学习微软的代码风格和设计方式。&&&&&& 完成了此套C++编程入门教程以后,鸡啄米认为还有很多知识有必要跟大家分享,有时间我还会为大家带来MFC、网络编程、数据库等方面的入门知识。希望大家继续关注鸡啄米博客,继续支持鸡啄米,谢谢大家!&
除非特别注明,文章均为原创
转载请标明本文地址:
作者:鸡啄米
&&( 22:51:7)&&( 22:15:11)&&( 22:12:14)&&( 22:50:53)&&( 22:13:7)&&( 21:53:54)&&( 21:25:32)&&( 22:23:2)&&( 22:4:45)&&( 22:22:20)
真是很系统啊,如果学习一定来这里。鸡啄米 于
22:16:15 回复呵呵,刚发布你就过来了,缘分啊
这个收藏起来慢慢学
太强悍了真的是非常喜欢这个博客呢
博主您好.请问你的这个博客主题是什么主题.能告诉我一下吗?谢谢
c++比较难吧,我是学java和C#入门的。鸡啄米 于
22:19:32 回复实际上C++跟java和C#在编程思想上和语法上都很相似。
你这导航很有用!鸡啄米 于
22:20:15 回复是啊,给想学C++的朋友一个更好的入口吧。
咱没时间弄导航 呼呼
代码我比较不懂
情人节你过得好吗
导航很有用,收藏了鸡啄米 于
22:22:10 回复目的就是给大家提供方便
把这套教程做个PDF或者WORD版本的吧
我什么编程基础都没有,学C++可以吗?鸡啄米 于
00:02:41 回复可以,只要有耐心,并不是多难的事情。
我觉得C++是最难学的,到现在还不是很明白呢
我觉得C++是最难学的呢,到现在还不是很明白
看来博主技术很牛呀
讲得太好了,通俗易懂,比有的老师讲得都好鸡啄米 于
18:46:26 回复谢谢,你们的认同让我有更大的动力分享更多的知识。
博主工作啦?鸡啄米 于
23:35:45 回复是的捣乱 于
12:28:32 回复哈哈,多多指教啊
学习教程汇总文,太好了
哇,真多啊,有得学习了。
能打包个CHM就更好了。。不知道博主有没有这个考虑呢?
特别有用,值得学习啊
博主有才!
博主写的总结真有意思。如“谁都是这时候过来的,要有耐心和勇于尝试。”看了蛮欣慰的。呵呵。
这个有必要收藏起来慢慢学
很全面,收藏起来慢慢学
以前学vb的,c++好学么?鸡啄米 于
00:04:04 回复其实不难学的
这个貌似不好学哦,一般人看不懂的
值得学习值得收藏的文章
真的很感谢博主!耐心!强大!还贴心附上这个!一定好好学习!之前有看过孙鑫老师的MFC几集,但是不适合VS2010,有些老,看了博主的讲得很好,和孙老师的能够呼应起来,但是又是针对VS2010的,非常感谢!
浩大的工程啊。敬佩鸡啄米 于
23:44:40 回复呵呵,确实花了不少时间
不是吧,这么全面的总结啊。学习中。
不当老师亏了……比大学里那些老师强到天了。不是说懂得比他们多,就是讲的比他们好,现在学校里的老师,问啥都是我不知道,你回去查吧。讲课就挑简单的说,老师轻松学生开心,坑死了鸡啄米 于
23:28:45 回复呵呵,是吗?老师应该负责任些的刘泽森 于
23:49:41 回复恩恩,我还在看,看了一天了,感觉是对课本里面mfc和孙鑫mfc的一个很好的补充,课本的东西太少,孙鑫的太老。
感谢作者。鸡啄米
请问一下 用MFC创建好了一个文档后怎么进行最简单的编程啊?
要是有多线程的知识点就很全了
前段时间上不了急死我啦
感谢博主分享经验,对C++编程又有了很多新的认识!
#include &iostream&
long fac(int n)
cout&&&n&0,data error!&&&
else if (n==0)
f=fac(n-1)*n;
return(f);
int _tmain(int argc, _TCHAR* argv[])
long fac(int n);
cout&&&Enter a positive integer:&;
cout&&n&&&!=&&&y&&
}代码里面为什么要加个 return 0?这样不是结果就是0了 前面计算出来的值不是作废了?
感谢博主,不错的教程,收藏了慢慢学习
支持楼主吧..
最近几天把这个看完了虽然有的地方还是不怎么懂,但是,整体的效果很好,很有帮助希望大神继续更新
鸡神,你太牛逼了,你就是我的偶像。
点一下我,我就陪你玩!
你大舅你二舅都是你舅!高桌子低板凳都是木头!金疙瘩银疙瘩你还不够!天在上地在下你娃包牛!
看完了楼主的这套入门教程,太通俗易懂,太给力了!楼主好样了~期待楼主继续跟新教程~~~~~~
出书了吗?
谢谢楼主,太有用了!
通读了个遍,特别喜欢你的博客!
博主啥时候开始讲连接数据库
另外一种风格的一套教程,一口气看完了,真心觉得不错,点赞!对C++,更加了解了一点……
大神,你这套教程是C98还是C11呀?
博主你好,我刚接触MFC,能不能加我qq,请教博主。qq啊
博主急需你的指导,如果可以我愿意给点报酬,学生×,望博主体谅。qq希望博主能指导指导我
出本书吧,我一定买
没有容器,没有模板,没有多线程。为啥不讲讲呢?
完全随机文章

我要回帖

更多关于 c语言程序设计教程 的文章

 

随机推荐