欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C/C++程序設(shè)計(jì)的基本概念詳解

 更新時(shí)間:2021年09月27日 11:19:42   作者:weixin_38898944  
這篇文章主要介紹了C++程序設(shè)計(jì)的基本概念詳解,文中有非常詳細(xì)的C語(yǔ)言使用教程及相關(guān)基礎(chǔ)知識(shí),對(duì)正在學(xué)習(xí)c語(yǔ)言的小伙伴們有非常好的幫助,需要的朋友可以參考下

概述

學(xué)C語(yǔ)言有很長(zhǎng)一段時(shí)間了,想做做筆記,把C和C++相關(guān)的比較容易忽視的地方記下來(lái),也希望可以給需要的同學(xué)一些幫助。

我的這些文章不想對(duì)C和C++的語(yǔ)法進(jìn)行講解和羅列,這些東西隨便找一本書(shū)就講的比我清楚,我只是想把一般人忽視的地方盡自己所能描述一下。權(quán)當(dāng)班門(mén)弄斧,貽笑大方了。

首先我想先從C和C++的一些基本概念入手。

main()函數(shù)

稍微學(xué)過(guò)C和C++的人都知道m(xù)ain()函數(shù)市所有C和C++程序必不可少的東西。叫做主函數(shù)。所有的程序都應(yīng)該從main()函數(shù)開(kāi)始執(zhí)行。但是你們又對(duì)這個(gè)函數(shù)了解多少呢?

我們都知道C和C++是一種函數(shù)語(yǔ)言,幾乎絕大多數(shù)的功能都是通過(guò)各種函數(shù)的調(diào)用來(lái)實(shí)現(xiàn)的,C和C++也提供了豐富的函數(shù)庫(kù)供編程人員調(diào)用??呻m然main()函數(shù)每個(gè)C程序都必須有的函數(shù),在C或者C++的函數(shù)庫(kù)里卻沒(méi)有叫做main()的函數(shù),它是需要程序設(shè)計(jì)人員實(shí)現(xiàn)的函數(shù)。

而且,你們發(fā)現(xiàn)了沒(méi)有,main并不是C和C++的保留字。因此理論上,你可以在其他地方使用main這個(gè)名字,比如變量名、類(lèi)名字、名字空間的名字甚至成員函數(shù)的名字。但是,即使這樣,你也不能修改main()函數(shù)本身的函數(shù)名,否則連接器就會(huì)報(bào)告錯(cuò)誤。

main()函數(shù)是C和C++程序的入口,這是因?yàn)镃和C++語(yǔ)言實(shí)現(xiàn)會(huì)有一個(gè)啟動(dòng)函數(shù),比如MS-C++的啟動(dòng)函數(shù)就叫做

mainCRTStartup()或者WinMainCRT-Startup()。在這個(gè)啟動(dòng)函數(shù)的最后會(huì)調(diào)用main()函數(shù),然后再調(diào)用exit()函數(shù)結(jié)束程序。如果沒(méi)有main()函數(shù),當(dāng)然會(huì)報(bào)錯(cuò)了。所以再C和C++開(kāi)發(fā)環(huán)境中main()函數(shù)其實(shí)是一個(gè)回調(diào)函數(shù)。它是需要我們來(lái)實(shí)現(xiàn)的。

有些同學(xué)可能學(xué)過(guò)一些應(yīng)用程序框架,比如MFC什么的。這些程序代碼中往往找不到main()函數(shù),這是因?yàn)槟切?yīng)用程序框架把main()函數(shù)的實(shí)現(xiàn)給隱藏起來(lái)了,main()函數(shù)在它們這里有固定的實(shí)現(xiàn)模式,所以不需要我們編寫(xiě)。在連接階段,框架會(huì)自動(dòng)將包含main()實(shí)現(xiàn)的庫(kù)加進(jìn)來(lái)連接。

main()函數(shù)也是有原型的。這個(gè)原型已經(jīng)是一種標(biāo)準(zhǔn)了,在ISO/IEC14882中對(duì)main()的原型進(jìn)行了定義。

	int main(){/*......*/}
和
	int main(int argc, char *argv[]){/*......*/}

上面這兩種形式是最具有可移植性的正確寫(xiě)法。當(dāng)然不同的編譯器可能會(huì)允許出現(xiàn)一些擴(kuò)展。比如允許main()返回void,或者有第三個(gè)參數(shù)char *env[]什么的。這個(gè)就要看具體的編譯器文檔了。

關(guān)于返回值,我們知道m(xù)ain()返回的是int類(lèi)型的。到底返回什么是有不同含義的。一般情況下,返回0,表示程序正常結(jié)束,返回任何非0表示錯(cuò)誤或非正常退出。前面講到了,啟動(dòng)函數(shù)最后還會(huì)調(diào)用exit()函數(shù)。那么main()函數(shù)的返回值就會(huì)作為exit()函數(shù)的操作數(shù)來(lái)返回操作系統(tǒng)。

在C++當(dāng)中對(duì)main()函數(shù)還有一些特殊的限制。比如:

  • 不能重載
  • 不能內(nèi)聯(lián)
  • 不能定義為靜態(tài)的
  • 不能取其地址
  • 不能由用戶(hù)自己調(diào)用

關(guān)于main()函數(shù)的參數(shù),它可以讓編譯好的執(zhí)行程序具有處理命令行參數(shù)的能力。這里需要注意,不要把“命令行參數(shù)”和main()函數(shù)的“函數(shù)實(shí)參”混淆,這是兩個(gè)不同的概念。命令行參數(shù)由啟動(dòng)程序截獲并打包成字符串?dāng)?shù)組傳遞給main()的形參argv[],而包括命令字(也就是執(zhí)行文件自己的名字)在內(nèi)的所有參數(shù)的個(gè)數(shù)則被傳遞給形參argc。試一下吧,咱們來(lái)模擬copy命令寫(xiě)個(gè)簡(jiǎn)單的文件拷貝程序。

//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 原文件 目標(biāo)文件\n",argValue[0]);
    }
    else
    {
        if((srcFile = fopen(argValue[1],"r")) == 0)
        {
            printf("無(wú)法打開(kāi)原文件!\"%s\"!",argValue[1]);
        }
        else
        {
            if((destFile = fopen(argValue[2],"w")) == 0)
            {
                printf("無(wú)法打開(kāi)目標(biāo)文件!\"%s\"!",argValue[2]);
                fclose(srcFile);
            }
            else
            {
                while((ch = fgetc(srcFile)) != EOF)
                {
                    fputc(ch,destFile);
                }
                fclose(srcFile);
                fclose(destFile);
                return 0;
            }
        }
    }
    return 1;
}
//用法:mycopy C:\file1.dat D:\newfile.dat

內(nèi)部名稱(chēng)

在編寫(xiě)C程序的時(shí)候如果沒(méi)有main()函數(shù),連接器會(huì)報(bào)錯(cuò)。一般報(bào)錯(cuò)信息會(huì)提示“unresolved external symbol_main”。這里的"_main"其實(shí)就是編譯器為main生成的內(nèi)部名稱(chēng)。其實(shí)C和C++語(yǔ)言在編譯過(guò)程中都會(huì)按照特定的規(guī)則把用戶(hù)定義的標(biāo)識(shí)符(函數(shù)、變量、類(lèi)型、名字空間什么的)轉(zhuǎn)換為相應(yīng)的內(nèi)部名稱(chēng)。而這些規(guī)則還跟指定的連接規(guī)范有關(guān)。比如,在C語(yǔ)言中,main的內(nèi)部名稱(chēng)就叫做_main。

C語(yǔ)言這么做,是告訴連接器,這個(gè)東西是個(gè)函數(shù)。實(shí)際上,C語(yǔ)言在所有函數(shù)的函數(shù)名前其實(shí)都是加了前綴“_”的,以此來(lái)區(qū)別函數(shù)名和其他標(biāo)識(shí)符名稱(chēng)。

這種規(guī)范在C++又是另一種樣子。這是因?yàn)樵贑中,所有函數(shù)只要不是局部于編譯單元(文件作用域)的static函數(shù),就會(huì)是具有extern連接類(lèi)型的和global作用域的全局函數(shù)。全局函數(shù)是不可以有同名的。但是在C++里面,可以在不同的作用域,比如class,struct,union,namespace中定義同名的函數(shù),甚至在同一個(gè)作用域也可以定義同名函數(shù),也就是函數(shù)重載。那么轉(zhuǎn)換為內(nèi)部名稱(chēng)的連接規(guī)范就要復(fù)雜一些了。比如:

class Sample_1
{
	char m_name[16];
public:
	void foo(char *newName);
	void foo(int age);
};
class Sample_2
{
	char m_name[16];
public:
	void foo(char *newName);
	void foo(bool sex);
};

在其他地方根據(jù)這兩個(gè)類(lèi)生成兩個(gè)實(shí)例,并進(jìn)行操作:

	Sample_1 a;
	Sample_2 b;
	a.foo("aaa");
	a.foo(100);
	b.foo("bbb");
	b.foo(false);

這里有四個(gè)函數(shù),但是確是同一個(gè)名稱(chēng)。編譯器應(yīng)該怎么區(qū)分呢?通過(guò)各自對(duì)象的成員標(biāo)識(shí)符區(qū)分?那是在代碼中區(qū)分的,但是在連接器看來(lái),所有函數(shù)其實(shí)都是全局函數(shù),而全局函數(shù)是不能重名的。所以為了避免二義,在C++中有一個(gè)名字修飾規(guī)則。也就是在函數(shù)名前面添加各級(jí)作用域的名稱(chēng)以及重載函數(shù)經(jīng)過(guò)編碼的參數(shù)信息。比如上面四次調(diào)用foo函數(shù),其實(shí)它們會(huì)調(diào)用四個(gè)具有全局名稱(chēng)的函數(shù),分別是Sample_1_foo@pch@1,Sample_1_foo@int@1,Sample_2_foo@pch@1,Sample_2_foo@int@1。

然而,這種標(biāo)準(zhǔn)并不是強(qiáng)制的,所以不同廠(chǎng)家開(kāi)發(fā)的C++編譯器有可能會(huì)有些許不同,而這也正是導(dǎo)致不同廠(chǎng)家的C++編譯器和連接器不能兼容的原因。

那么好了,當(dāng)使用不同編程語(yǔ)言聯(lián)合開(kāi)發(fā)時(shí)候,就要定義一個(gè)統(tǒng)一的規(guī)范,這個(gè)規(guī)范叫做連接規(guī)范。這個(gè)很好理解了吧,因?yàn)槿绻粋€(gè)標(biāo)識(shí)符在不同編譯單元中用不同的連接規(guī)范,就會(huì)產(chǎn)生不一致的內(nèi)部名稱(chēng),連接肯定會(huì)失敗。

所以,在開(kāi)發(fā)程序庫(kù)的時(shí)候就一定要明確你要用那條連接規(guī)范。比如,編寫(xiě)C程序是就要規(guī)定C連接規(guī)范:extern “C”。大約有這么幾種情況:

  • 僅對(duì)一個(gè)類(lèi)型、函數(shù)、變量或常量指定連接規(guī)范;
    extern "C" void WinMainCRTStartup();
    extern "C" const CLSID CLSID_DataConverter;
    extern "C" struct Student{/*....*/};
    extern "C" Student g_Student;
  • 對(duì)一段代碼限定連接規(guī)范
    #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 memset(void*, int, size_t);
    #ifdef __cplusplus
    }
    #endif
  • 當(dāng)前使用的是C++編譯器,并且使用了extern "C"限定了一段代碼的連接規(guī)范,但又想在其中某行或某段代碼保持C++的連接規(guī)范。
    #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++"{
    #endif
    int __cdecl memcmp(const void*, const void*, size_t);
    void* __cdecl memcpy(void*, const void*, size_t);
    #if __SUPPORT_EXTERN_CPP_
    }
    #endif
    void* __cdecl memset(void*, int, size_t);
    #ifdef __cplusplus
    }
    #endif
  • 某個(gè)聲明中指定了某個(gè)標(biāo)識(shí)符的連接規(guī)范為extern “C”,那么對(duì)應(yīng)的定義也要指定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)
    {
        //功能實(shí)現(xiàn)
    }
    #ifdef __cplusplus
    }
    #endif

其實(shí)如果是面向接口的編程,就不用考慮這么多了。因?yàn)榧词菇涌趦啥说膬?nèi)部名稱(chēng)不同,只要使用了一致的成員對(duì)其和排列方式,并遵守一致的調(diào)用規(guī)范,一致的函數(shù)實(shí)現(xiàn)方式。也就是C++一致的對(duì)象模型,那么基本不會(huì)有什么問(wèn)題的。

變量和它的初始化

在C和C++中,全局變量(extern或static的)存放在程序的靜態(tài)數(shù)據(jù)區(qū)里面。這些變量在程序進(jìn)入main()之前就被創(chuàng)建了,并在main()結(jié)束后銷(xiāo)毀,C和C++提供了一個(gè)默認(rèn)的全局初始化器0。也就是編譯器會(huì)默認(rèn)的用0來(lái)初始化它們。函數(shù)內(nèi)部的static變量和類(lèi)的static成員也是在靜態(tài)存儲(chǔ)區(qū),因此也會(huì)默認(rèn)初始化為0。除非你在創(chuàng)建的時(shí)候就提供了初值。這是編譯器對(duì)靜態(tài)變量的待遇。而對(duì)于其他的自動(dòng)變量,就需要我們給他初始化了。不要指望編譯器自動(dòng)對(duì)它們初始化。

所以,全局變量的聲明和定義應(yīng)當(dāng)放在源文件的開(kāi)頭部位。

變量的初始化和變量的賦值是有區(qū)別的。初始化是發(fā)生在變量創(chuàng)建的同時(shí),而賦值是在程序中變量創(chuàng)建后干的。
前面說(shuō)了,對(duì)靜態(tài)存儲(chǔ)區(qū)的變量(比如全局變量,靜態(tài)變量什么)的進(jìn)行初始化是編譯器自動(dòng)進(jìn)行的,但是局部變量的初始化確實(shí)需要編程人員手動(dòng)進(jìn)行。

還有,在一個(gè)編程單元中,全局變量的初始值不要依賴(lài)另一個(gè)編譯單元的全局變量。什么意思?比如:

//file1.c
int g_x = 100;
//file2.c
extern int g_x;
double g_d = g_x + 10;

這兩個(gè)編譯單元編譯完成后進(jìn)行連接,兩個(gè)全局變量到底先初始化哪個(gè)并不確定,連接器也不能保證這一點(diǎn)。先初始化g_x,那g_d也就能順利初始化,而反之,g_d就不一定是多少了。

另外,C和C++都會(huì)有現(xiàn)成的庫(kù)。就是文件開(kāi)頭包含的那些*.h文件。注意哦,C的庫(kù)可是有多線(xiàn)程版和單線(xiàn)程版,開(kāi)發(fā)多線(xiàn)程程序應(yīng)該使用多線(xiàn)程版本的庫(kù)。另外,在多人開(kāi)發(fā)軟件是,庫(kù)的版本一定要統(tǒng)一。

編譯時(shí)和運(yùn)行時(shí)

源代碼文件編寫(xiě)的功能有些時(shí)運(yùn)行時(shí)起作用,有些編譯時(shí)就起作用的。這件事需要區(qū)分的。比如預(yù)編譯偽指令、類(lèi)定義、外部對(duì)象聲明、函數(shù)原型、修飾符號(hào)(const,static那些)、類(lèi)成員訪(fǎng)問(wèn)說(shuō)明符號(hào)(public、private那些)以及連接規(guī)范是在編譯階段發(fā)揮作用的,可執(zhí)行程序里是不存在這些東西的。而容器越界訪(fǎng)問(wèn)、虛函數(shù)動(dòng)態(tài)決議、動(dòng)態(tài)連接、動(dòng)態(tài)內(nèi)存分配、異常處理、RTTI這些則是在運(yùn)行時(shí)發(fā)揮作用的。比如:

int* pInt = new int[10];
pInt += 100;
cout << *pInt << endl;
*pInt = 1000;

這段代碼一般在編譯階段沒(méi)什么問(wèn)題,但運(yùn)行時(shí)會(huì)出錯(cuò)。所以,我們?cè)诔绦蛟O(shè)計(jì)時(shí)就要對(duì)運(yùn)行的行為有所預(yù)見(jiàn),通過(guò)編譯連接的程序在運(yùn)行時(shí)不見(jiàn)得正確。

總結(jié)

本篇文章就到這里了,希望能夠給你帶來(lái)幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!

相關(guān)文章

  • C語(yǔ)言中格式化輸出符號(hào)%d、%c、%p、%x等詳解

    C語(yǔ)言中格式化輸出符號(hào)%d、%c、%p、%x等詳解

    格式化輸出在C語(yǔ)言中非常常用,提供了多種用法來(lái)控制輸出的格式,下面這篇文章主要給大家介紹了關(guān)于C語(yǔ)言中格式化輸出符號(hào)%d、%c、%p、%x等的相關(guān)資料,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2024-06-06
  • 如何讓Dev-C++支持auto關(guān)鍵字呢

    如何讓Dev-C++支持auto關(guān)鍵字呢

    這篇文章主要介紹了如何讓Dev-C++支持auto關(guān)鍵字問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-12-12
  • c語(yǔ)言 跳臺(tái)階問(wèn)題的解決方法

    c語(yǔ)言 跳臺(tái)階問(wèn)題的解決方法

    本篇文章是對(duì)c語(yǔ)言中跳臺(tái)階問(wèn)題的解決方法進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下
    2013-05-05
  • 淺析C++ 數(shù)據(jù)類(lèi)型

    淺析C++ 數(shù)據(jù)類(lèi)型

    這篇文章主要介紹了C++ 數(shù)據(jù)類(lèi)型的相關(guān)資料,幫助大家更好的理解和學(xué)習(xí)c++,感興趣的朋友可以了解下
    2020-08-08
  • 手把手教你實(shí)現(xiàn)漂亮的Qt?登錄界面

    手把手教你實(shí)現(xiàn)漂亮的Qt?登錄界面

    最近在使用Qt5,Qt?Creator做一個(gè)管理系統(tǒng)類(lèi)的項(xiàng)目,需要用到登錄界面,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-11-11
  • C++動(dòng)態(tài)數(shù)組類(lèi)的封裝實(shí)例

    C++動(dòng)態(tài)數(shù)組類(lèi)的封裝實(shí)例

    這篇文章主要介紹了C++動(dòng)態(tài)數(shù)組類(lèi)的封裝,很重要的概念,需要的朋友可以參考下
    2014-08-08
  • C語(yǔ)言入門(mén)篇--充分理解操作符

    C語(yǔ)言入門(mén)篇--充分理解操作符

    本篇文章是基礎(chǔ)篇,適合c語(yǔ)言剛?cè)腴T(mén)的朋友,本文主要介紹了c語(yǔ)言的操作符基礎(chǔ)理論,希望可以幫助大家快速入門(mén)c語(yǔ)言的世界,更好的理解c語(yǔ)言
    2021-08-08
  • C語(yǔ)言實(shí)現(xiàn)通訊管理系統(tǒng)設(shè)計(jì)

    C語(yǔ)言實(shí)現(xiàn)通訊管理系統(tǒng)設(shè)計(jì)

    這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)通訊管理系統(tǒng)設(shè)計(jì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-01-01
  • 手把手教你如何優(yōu)化C語(yǔ)言程序

    手把手教你如何優(yōu)化C語(yǔ)言程序

    程序進(jìn)行優(yōu)化,通常是指優(yōu)化程序代碼或程序執(zhí)行速度。優(yōu)化代碼和優(yōu)化速度實(shí)際上是一個(gè)予盾的統(tǒng)一,一般是優(yōu)化了代碼的尺寸,就會(huì)帶來(lái)執(zhí)行時(shí)間的增加,如果優(yōu)化了程序的執(zhí)行速度,通常會(huì)帶來(lái)代碼增加的副作用,很難魚(yú)與熊掌兼得,只能在設(shè)計(jì)時(shí)掌握一個(gè)平衡點(diǎn)
    2013-07-07
  • opencv實(shí)現(xiàn)棋盤(pán)格檢測(cè)

    opencv實(shí)現(xiàn)棋盤(pán)格檢測(cè)

    這篇文章主要為大家詳細(xì)介紹了opencv實(shí)現(xiàn)棋盤(pán)格檢測(cè),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-08-08

最新評(píng)論