一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - C/C++ - C/C++程序設計的基本概念詳解

C/C++程序設計的基本概念詳解

2022-01-12 14:23weixin_38898944 C/C++

這篇文章主要介紹了C++程序設計的基本概念詳解,文中有非常詳細的C語言使用教程及相關基礎知識,對正在學習c語言的小伙伴們有非常好的幫助,需要的朋友可以參考下

概述

學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()的原型進行了定義。

?
1
2
3
    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命令寫個簡單的文件拷貝程序。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
//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]);
    }
    else
    {
        if((srcFile = fopen(argValue[1],"r")) == 0)
        {
            printf("無法打開原文件!\"%s\"!",argValue[1]);
        }
        else
        {
            if((destFile = fopen(argValue[2],"w")) == 0)
            {
                printf("無法打開目標文件!\"%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

內部名稱

在編寫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中定義同名的函數,甚至在同一個作用域也可以定義同名函數,也就是函數重載。那么轉換為內部名稱的連接規范就要復雜一些了。比如:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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);
};

在其他地方根據這兩個類生成兩個實例,并進行操作:

?
1
2
3
4
5
6
Sample_1 a;
Sample_2 b;
a.foo("aaa");
a.foo(100);
b.foo("bbb");
b.foo(false);

這里有四個函數,但是確是同一個名稱。編譯器應該怎么區分呢?通過各自對象的成員標識符區分?那是在代碼中區分的,但是在連接器看來,所有函數其實都是全局函數,而全局函數是不能重名的。所以為了避免二義,在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”。大約有這么幾種情況:

  • 僅對一個類型、函數、變量或常量指定連接規范;
?
1
2
3
4
extern "C" void WinMainCRTStartup();
extern "C" const CLSID CLSID_DataConverter;
extern "C" struct Student{/*....*/};
extern "C" Student g_Student;
  • 對一段代碼限定連接規范
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#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
  • 當前使用的是C++編譯器,并且使用了extern "C"限定了一段代碼的連接規范,但又想在其中某行或某段代碼保持C++的連接規范。
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#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
  • 某個聲明中指定了某個標識符的連接規范為extern “C”,那么對應的定義也要指定extern “C”:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#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。除非你在創建的時候就提供了初值。這是編譯器對靜態變量的待遇。而對于其他的自動變量,就需要我們給他初始化了。不要指望編譯器自動對它們初始化。

所以,全局變量的聲明和定義應當放在源文件的開頭部位。

變量的初始化和變量的賦值是有區別的。初始化是發生在變量創建的同時,而賦值是在程序中變量創建后干的。
前面說了,對靜態存儲區的變量(比如全局變量,靜態變量什么)的進行初始化是編譯器自動進行的,但是局部變量的初始化確實需要編程人員手動進行。

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

?
1
2
//file1.c
int g_x = 100;
?
1
2
3
//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這些則是在運行時發揮作用的。比如:

?
1
2
3
4
int* pInt = new int[10];
pInt += 100;
cout << *pInt << endl;
*pInt = 1000;

這段代碼一般在編譯階段沒什么問題,但運行時會出錯。所以,我們在程序設計時就要對運行的行為有所預見,通過編譯連接的程序在運行時不見得正確。

總結

本篇文章就到這里了,希望能夠給你帶來幫助,也希望您能夠多多關注服務器之家的更多內容!

原文鏈接:https://blog.csdn.net/weixin_38898944/article/details/120463880

延伸 · 閱讀

精彩推薦
  • C/C++C語言中炫酷的文件操作實例詳解

    C語言中炫酷的文件操作實例詳解

    內存中的數據都是暫時的,當程序結束時,它們都將丟失,為了永久性的保存大量的數據,C語言提供了對文件的操作,這篇文章主要給大家介紹了關于C語言中文件...

    針眼_6702022-01-24
  • C/C++C語言實現電腦關機程序

    C語言實現電腦關機程序

    這篇文章主要為大家詳細介紹了C語言實現電腦關機程序,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下...

    xiaocaidayong8482021-08-20
  • C/C++C++之重載 重定義與重寫用法詳解

    C++之重載 重定義與重寫用法詳解

    這篇文章主要介紹了C++之重載 重定義與重寫用法詳解,本篇文章通過簡要的案例,講解了該項技術的了解與使用,以下就是詳細內容,需要的朋友可以參考下...

    青山的青6062022-01-04
  • C/C++詳解c語言中的 strcpy和strncpy字符串函數使用

    詳解c語言中的 strcpy和strncpy字符串函數使用

    strcpy 和strcnpy函數是字符串復制函數。接下來通過本文給大家介紹c語言中的strcpy和strncpy字符串函數使用,感興趣的朋友跟隨小編要求看看吧...

    spring-go5642021-07-02
  • C/C++深入理解goto語句的替代實現方式分析

    深入理解goto語句的替代實現方式分析

    本篇文章是對goto語句的替代實現方式進行了詳細的分析介紹,需要的朋友參考下...

    C語言教程網7342020-12-03
  • C/C++C/C++經典實例之模擬計算器示例代碼

    C/C++經典實例之模擬計算器示例代碼

    最近在看到的一個需求,本以為比較簡單,但花了不少時間,所以下面這篇文章主要給大家介紹了關于C/C++經典實例之模擬計算器的相關資料,文中通過示...

    jia150610152021-06-07
  • C/C++c++ 單線程實現同時監聽多個端口

    c++ 單線程實現同時監聽多個端口

    這篇文章主要介紹了c++ 單線程實現同時監聽多個端口的方法,幫助大家更好的理解和學習使用c++,感興趣的朋友可以了解下...

    源之緣11542021-10-27
  • C/C++學習C++編程的必備軟件

    學習C++編程的必備軟件

    本文給大家分享的是作者在學習使用C++進行編程的時候所用到的一些常用的軟件,這里推薦給大家...

    謝恩銘10102021-05-08
主站蜘蛛池模板: japanesexxxx在线播放 | 色综合久久98天天综合 | 国内精品在线播放 | 四虎影院精品在线观看 | 特黄特色大片免费影院 | 99精品久久精品一区二区小说 | 国产精品成 | 我把寡妇日出水好爽 | 爱福利视频一区 | 欧洲美女bbbxxxxxx| 欧美免赞性视频 | 美女国内精品自产拍在线播放 | 精品无码一区二区三区中文字幕 | 成人精品一级毛片 | 免费jizz在在线播放国产 | 久久婷婷丁香五月色综合啪免费 | 日韩欧美一区二区三区四区 | 99在线资源 | 9191免费永久观看 | 2021国产精品成人免费视频 | 性bbbb妇女激情 | 亚洲国产在线视频精品 | 国内自拍2020 | 精品国产精品人妻久久无码五月天 | 好男人资源免费观看 | 欧美伦乱| 韩国最新理论片奇忧影院 | 双子母性本能在线观看 | 亚洲成年网站在线观看 | 日韩操比视频 | 日韩欧美中文在线 | 色综合天天综合网看在线影院 | 国产精品自在欧美一区 | 女人肮脏的交易中文字幕未删减版 | 国产精品视频一区二区三区 | 好大好硬好紧太深了受不了 | 天天操精品视频 | 成人免费在线视频观看 | 男同互操 | 98在线视频噜噜噜国产 | tolove第一季动画在线看 |