CC++程序设计的基本概念详解

更新时间:2023-05-03 13:09:17 阅读: 评论:0

CC++程序设计的基本概念详解
⽬录
概述
main()函数
内部名称
变量和它的初始化
编译时和运⾏时
总结
概述
学C语⾔有很长⼀段时间了,想做做笔记,把C和C++相关的⽐较容易忽视的地⽅记下来,也希望可怎么自动填充序号 以给需要的同学⼀些帮助。
我的这些⽂章不想对C和C++的语法进⾏讲解和罗列,这些东西随便找⼀本书就讲的⽐我清楚,我只是想把⼀般⼈忽视的地⽅尽⾃⼰所能描述⼀下。权当班门弄斧,贻笑⼤⽅了。
⾸先我想先从C和C++的⼀些基本概念⼊⼿。
main()函数
稍微学过C和C++的⼈都知道main()函数市所有C和C++程序必不可少的东西。叫做主函数。所有的程序都应该从main()函数开始执⾏。但是你们⼜对这个函数了解多少呢?
我们都知道C和C++是⼀种函数语⾔,⼏乎绝⼤多数的功能都是通过各种函数的调⽤来实现的,C和C++也提供了丰富的函数库供编程⼈员调⽤。可虽然main()函数每个C程序都必须有的函数,在C或者C++的函数库⾥却没有叫做main()的函数,它是需要程序设计⼈员实现的函数。
⽽且,你们发现了没有,main并不是C和C++的保留字。因此理论上,你可以在其他地⽅使⽤main这个名字,⽐如变量名、类名字、名字空间的名字甚⾄成员函数的名字。但是,即使这样,你也不能修改main()函数本⾝的函数名,否则连接器就会报告错误。
main()函数是C和C++程序的⼊⼝,这是因为C和C++语⾔实现会有⼀个启动函数,⽐如MS-C++的启动函数就叫做mainCRTStartup()或者WinMainCRT-Startup()。在这个启动函数的最后会调⽤main()函
数,然后再调⽤exit()函数结束程序。如果没有main()函数,当然会报错了。所以再C和C++开发环境中main()函数其实是⼀个回调函数。它是需要我们来实现的。
有些同学可能学过⼀些应⽤程序框架,⽐如MFC什么的。这些程序代码中往往找不到main()函数,这是因为那些应⽤程序框架把main()函数的实现给隐藏起来了,main()函数在它们这⾥有固定的实现模式,所以不需要我们编写。在连接阶段,框架会⾃动将包含main()实香草是什么 现的库加进来连接。
main()函数也是有原型的。这个原型已经是⼀种标准了,在ISO/IEC14882中对main()的原型进⾏了定义。
int main(){/*......*/}
int main(int argc, char *argv[]){/*......*/}
上⾯这两种形式是最具有可移植性的正确写法。当然不同的编译器可能会允许出现⼀些扩展。⽐如允许main()返回void,或者有第三个参数char *env[]什么的。这个就要看具体的编译器⽂档了。
关于返回值,我们知道main()返回的是int类型的。到底返回什么是有不同含义的。⼀般情况下,返回0属兔几月出生最好 ,
表⽰程序正常结束,返回任何⾮0表⽰错误或⾮正常退出。前⾯讲到了,启动函数最后还会调⽤exit()函数。那么main()函数的返回值就会作为exit()函数的操作数来返回操作系统。
在C++当中对main()函数还有⼀些特殊的限制。⽐如:
不能重载
不能内联
不能定义为静态的
不能取其地址
不能由⽤户⾃⼰调⽤
关于main()函数的参数,它可以让编译好的执⾏程序具有处理命令⾏参数的能⼒。这⾥需要注意,不要把“命令⾏参数”和main()函数的“函数实参”混淆,这是两个不同的概念。命令⾏参数由启动程序截获并打包成字符串数组传递给main()的形参argv[],⽽包括命令字(也就是执⾏⽂件⾃⼰的名字)在内的所有参数的个数则被传递给形参argc。试⼀下吧,咱们来模拟copy命令写个简单的⽂件拷贝程序。
//mycopy.c:⽂件拷贝程序。
#include <stdio.h>
int main(int argCount, char* argValue[])
{
FILE *srcFile = 0;
FILE *destFile = 0;
int ch = 0;
if(argCount != 3)
{
printf("使⽤⽅法:%s 原⽂件⽬标⽂件\n",argValue[0]);
}
el
{
if((srcFile = fopen(argValue[1],"r")) == 0)
{
printf("⽆法打开原⽂件!\"%s\"!",argValue[1]);
}
el
{
if((destFile = fopen(argValue[2],"w")) == 0)
{
printf("⽆法打开⽬标⽂件!\"%s\"!",argValue[2]);
fclo(srcFile);
}
el
{
while((ch = fgetc(srcFile)) != EOF)
{
fputc(ch,destFile);
}
fclo(srcFile);
fclo(destFile);
return 0;
}
}
}
return 1;
}
//⽤法:mycopy C:\file1.dat D:\newfile.dat
内部名称
在编写C程序的时候如果没有main()函数,连接器会报错。⼀般报错信息会提⽰“unresolved external symbol_main”。这⾥的"_main"其实就是编译器为main⽣成的内部名称。其实C和C++语⾔在编译过程中都会按照特定的规则把⽤户定义的标识符(函数、变量、类型、名字空间什么的)转换为相应的内部名称。⽽这些规则还跟指定的连接规范有关。⽐如,在C语⾔
中,main的内部名称就叫做_main。
C语⾔这么做,是告诉连接器,这个东西是个函数。实际上,C语⾔在所有函数的函数名前其实都是加了前缀“_”的,以此来区别函数名和其他标识符名称。
这种规范在C++⼜是另⼀种样⼦。这是因为在C中,所有函数只要不是局部于编译单元(⽂件作⽤域)的static函数,就会是具有extern连接类型的和global作⽤域的全局函数。全局函数是不可以有同名的。但是在C++⾥⾯,可以在不同的作⽤域,⽐如class,struct,union,namespace中定义同名的函数,甚⾄在同⼀个作⽤域也可以定义同名函数,也就是函数重载。那么转换为内部名称的连接规范就要复杂⼀些了。⽐如:
class Sample_1
{
char m_name[16];
public:
void foo(char *newName);
void foo(int age);
};
class Sample_2
{
char m_name[16];
void foo(bool x);
};
在其他地⽅根据这两个类⽣成两个实例,并进⾏操作:
Sample_1 a;
Sample_2 b;
a.foo("aaa");
a.foo(100);
b.foo("bbb");
b.foo(fal);
这⾥有四个函数,但是确是同⼀个名称。编译器应该怎么区分呢?通过各⾃对象的成员标识符区分?那是在代码中区分的,但是在连接器看来,所有函数其实都是全局函数,⽽全局函数是不能重名的。所以为了避免⼆义,在C++中有⼀个名字修饰规则。也就是在函数名前⾯添加各级作⽤域的名称以及重载函数经过编码的参数信息。⽐如上⾯四次调⽤foo函数,其实它们会调⽤四个具有全局名称的函数,分别是
Sample_1_foo@pch@1,Sample_1_foo@int@1,Sample_2_foo@pch@1,Sample_2_foo@int@1。
然⽽,这种标准并不是强制的,所以不同⼚家开发的C++编译器有可能会有些许不同,⽽这也正是导致不同⼚家的C++编译器和连接器不能兼容的原因。
那么好了,当使⽤不同编程语⾔联合开发时候,就要定义⼀个统⼀的规范,这个规范叫做连接规范。这个很好理解了吧,因为如果同⼀个标识符在不同编译单元中⽤不同的连接规范,就会产⽣不⼀致的内部名称,连接肯定会失败。
所以,在开发程序库的时候就⼀定要明确你要⽤那条连接规范。⽐如,编写C程序是就要规定C连接规范:extern “C”。⼤约有这么⼏种情况:
仅对⼀个类型、函数、变量或常量指定连接规范;
extern "C" void WinMainCRTStartup();
extern "C" const CLSID CLSID_DataConverter;
extern "C" struct Student{/*....*/};
extern "C" Student g_Student;
对⼀段代码限定连接规范
#ifdef __cplusplus
extern "C" {
#endif
const int MAX_AGE = 200;
#pragma pack(push,4)
typedef struct _Person
{
char *m_Name;
int m_Age;
} Person, *PersonPtr;
#pragma pack(pop)
Person g_Me;
int __cdecl memcmp(const void*, const void*, size_t);
void* __cdecl memcpy(void*, const void*, size_t);
void* __cdecl memt(void*, int, size_t);
#ifdef __cplusplusonu设备是什么意思
}
#endif
当前使⽤的是C++编译器,并且使⽤了extern "C"限定了⼀段代码的连接规范,但⼜想在其中某⾏或某段代码保持C++的连接规范。
#ifdef __cplusplus
extern "C" {
#endif
const int MAX_AGE = 200;
#pragma pack(push,4)
typedef struct _Person
{
char *m_Name;
int m_Age;
} Person, *PersonPtr;
#pragma pack(pop)
Person g_Me;
#if __SUPPORT_EXTERN_CPP_
extern "C++"{
int __cdecl memcmp(const void*, const void*, size_t);
void* __cdecl memcpy(void*, const void*, size_t);
#if __SUPPORT_EXTERN_CPP_
}
#endif
void* __cdecl memt(void*, int, size_t);
#ifdef __cplusplus
}
#endif
某个声明中指定了某个标识符的连接规范为extern “C”,那么对应的定义也要指定extern “C”:
#ifdef __cplusplus
extern "C" {
#endif
memcmp(const void*, const void*, size_t);
#ifdef __cplusplus
}
#endif
#ifdef __cplusplus
extern "C" {
#endif
memcmp(const void *p, const void *a, size_t len)
{
//功能实现
}
#ifdef __cplusplus
}
#endif
其实如果是⾯向接⼝的编程,就不⽤考虑这么多了。因为即使接⼝两端的内部名称不同,只要使⽤了
⼀致的成员对其和排列⽅式,并遵守⼀致的调⽤规范,⼀致的函数实现⽅式。也就是C++⼀致的对象模型,那么基本不会有什么问题的。
变量和它的初始化
在C和C++中,全局变量(extern或static的)存放在程序的静态数据区⾥⾯。这些变量在程序进⼊main()之前就被创建了,并在main()结束后销毁,C和C++提供了⼀个默认的全局初始化器0。也就是编译器会默认的⽤0来初始化它们。函数内部的static 变量和类的static成员也是在静态存储区,因此也会默认初始化为0。除⾮你在创建的时候就提供了初值。这是编译器对最有诗意的网名 静态变量的待遇。⽽对于其他的⾃动变量,就需要我们给他初始化了。不要指望编译器⾃动对它们初始化。
所以,全局变量的声明和定义应当放在源⽂件的开头部位。
变量的初始化和变量的赋值是有区别的。初始化是发⽣在变量创建的同时,⽽赋值是在程序中变量创建后⼲的。
前⾯说了,对静态存储区的变量(⽐如全局变量,静态变量什么)的进⾏初始化是编译器⾃动进⾏的,但是局部变量的初始化确实需要编程⼈员⼿动进⾏。
还有,在⼀个编程单元中,全局变量的初始值不要依赖另⼀个编译单元的全局变量。什么意思?⽐如:
//file1.c
int g_x = 100;
//file2.c
extern int g_x;
double g电源选择 _d = g_x + 10;
这两个编译单元编译完成后进⾏连接,两个全局变量到底先初始化哪个并不确定,连接器也不能保证这⼀点。先初始化g_x,那g_d也就能顺利初始化,⽽反之,g_d就不⼀定是多少了。
另外,C和C++都会有现成的库。就是⽂件开头包含的那些*.h⽂件。注意哦,C的库可是有多线程版和单线程版,开发多线程程序应该使⽤多线程版本的库。另外,在多⼈开发软件是,库的版本⼀定要统⼀。
编译时和运⾏时
源代码⽂件编写的功能有些时运⾏时起作⽤,有些编译时就起作⽤的。这件事需要区分的。⽐如预编
译伪指令、类定义、外部对象声明、函数原型、修饰符号(const,static那些)、类成员访问说明符号(public、private那些)以及连接规范是在编译阶段发挥作⽤的,可执⾏程序⾥是不存在这些东西的。⽽容器越界访问、虚函数动态决议、动态连接、动态内存分配、异常处理、RTTI这些则是在运⾏时发挥作⽤的。⽐如:
int* pInt = new int[10];
cout << *pInt << endl;
*pInt = 1000;
这段代码⼀般在编经常腰疼是什么原因女 译阶段没什么问题,但运⾏时会出错。所以,我们在程序设计时就要对运⾏的⾏为有所预见,通过编译连接的程序在运⾏时不见得正确。
总结
本篇⽂章就到这⾥了,希望能够给你带来帮助,也希望您能够多多关注的更多内容!

本文发布于:2023-05-03 13:09:17,感谢您对本站的认可!

本文链接:https://www.wtabcd.cn/fanwen/fan/82/520822.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:函数   连接   程序   编译   规范
相关文章
留言与评论(共有 0 条评论)
   
验证码:
推荐文章
排行榜
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图