1. 程式人生 > >C/C++基礎知識總結

C/C++基礎知識總結

1. 分配記憶體的方法

面試中比較見問的一個問題。
注意:
1. 只有calloc函式會將分配記憶體初始化為0
2. newC++獨有的,其是運算子,而不是函式。

下面對其分別進行介紹:

1) malloc 函式:

void *malloc(unsigned int size)

在記憶體的動態分配區域中分配一個長度為size的連續空間,如果分配成功,則返回所分配記憶體空間的首地址,否則返回NULL,申請的記憶體不會進行初始化。

2)calloc 函式:

void *calloc(unsigned int num, unsigned int size)

按照所給的資料個數和資料型別所佔位元組數,分配一個 num * size 連續的空間。calloc申請記憶體空間後,會自動初始化記憶體空間為 0,但是malloc不會進行初始化,其記憶體空間儲存的是一些隨機資料。

3)realloc 函式:

void *realloc(void *ptr, unsigned int size)

增加或減少以前分配區的長度。當增加長度時,可能需要將以前分配區的內容移到另一個足夠大的區域,以便在尾端提供增加的儲存區,而新增的區域內的初始值不確定。

4)new是動態分配記憶體的運算子

自動計算需要分配的空間,在分配類型別的記憶體空間時,同時呼叫類的建構函式,對記憶體空間進行初始化,即完成類的初始化工作。動態分配內建型別是否自動初始化取決於變數定義的位置,在函式體外定義的變數都初始化為0,在函式體內定義的內建型別變數都不進行初始化。

2. 儲存空間的分佈

程式的空間分佈通常分為(地址從下到上):
1. 程式碼段
2. 初始化資料段
3. 未初始化資料段
4. 堆
5. 棧

這裡寫圖片描述

堆地址是從下到上增長,棧地址從上到下增長。

一個例題:判斷a,b,c,d的儲存空間?

int a=0;
class someClass{
   int b;
   static int c;
};
int main(){
   int d=0;
   someClass *p=new someClass();
   return 0;
}

解析:
變數a 全域性變數 存放在全域性變數區
變數b 類的成員變數 由類的定義決定 在main函式中類A動態分配 因此b在堆區

,這裡一定要注意,b的儲存區域的變化
變數c 靜態成員 靜態儲存區
變數d 區域性變數 棧區

3. 關於大小端的問題

關於大小端的問題,一定要記住:小端是低地址存低位,而大端是低地址存高位,這點非常的重要。

例題:unsigned int a= 0x1234; unsigned char b=*(unsigned char *)&a; 在32位大端模式處理器上變數b等於()?

解析:
unsigned int a= 0x1234的32位完全表示是0x00001234
由低地址到高地址依次為(假設低地址為0x4000),則其在大端的分佈如下:

0x4000        0x4001     0x4002      0x4003
00            00          12          34

則a的地址&a0x400,char型別佔一位,因此b的值為:0x00
同理如果是小端則值為:0x34

4. 關於指標的問題

如下例題的輸出結果是:

void foobar(int a, int *b, int **c)
{
    int *p = &a;
    *p = 101;
    *c = b;
    b = p;
}

int main()
{
    int a = 1;
    int b = 2;
    int c = 3;
    int *p = &c;
    foobar(a, &b, &p);
    printf("a=%d, b=%d, c=%d, *p=%d\n", a, b, c, *p);
    return (0);
}

解析:
函式foobar中的a是按值傳遞,因此在函式中的修改不會引起主函式中的變化。
函式中b傳遞的是主函式中b的指標,語句b = p ,其中p指向的是函式foobar內區域性變數a的地址,讓傳遞過去的指標換了指向的資料,原來指向的資料(主函式中的b)不會有影響。如果這裡是*b = *p那麼主函式中的b也要相應變化。
函式中的c傳遞的是雙重指標,*c = b,也就是讓主函式中的p指標指向了主函式中的b的地址
在函式foobar中對指標的變化沒有影響到主函式,只是讓雙重指標更換了指向而已
因此答案為:a=1, b=2, c=3, *p=2

5. memmove函式的實現

如果有兩段記憶體重疊,進行memcpy的話則會出現未定義的行為,因此重新寫此函式為memmove,用來處理記憶體重疊式的複製。

#include <iostream>
#include <string.h>
using namespace std;

void* memmove(void *dst,const void *src,size_t n) {
    if (dst == NULL || src == NULL)
        return NULL;
    char* pdst = (char*)dst;
    const char* psrc=(const char*)src;
    if (pdst<psrc) {
        for (size_t i = 0;i<n;i++)
            *(pdst++)=*(psrc++);
    }
    else {
        pdst += n-1;
        psrc += n-1;
        for (size_t i = 0;i<n;i++)
            *(pdst--)=*(psrc--);
    }   
    return dst;
} 
int main(){
    char c1[]="hello,world";
    memmove(c1+3,c1,8);
    cout<<c1<<endl;
}

上述程式編譯執行的結果為:

#include <iostream>
#include <string.h>
using namespace std;

void* memmove(void *dst,const void *src,size_t n) {
    //判斷合法性
    if (dst == NULL || src == NULL)
        return NULL;
    char* pdst = (char*)dst;
    const char* psrc=(const char*)src;
    //防止記憶體重疊的處理
    if (pdst<psrc) {
        for (size_t i = 0;i<n;i++)
            *(pdst++)=*(psrc++);
    }
    else {
        pdst += n-1;
        psrc += n-1;
        for (size_t i = 0;i<n;i++)
            *(pdst--)=*(psrc--);
    }   
    return dst;
} 
int main(){
    char c1[]="hello,world";
    memmove(c1+3,c1,8);
    cout<<c1<<endl;
}

上述程式編譯執行的結果為:

helhello,wo

6. 關於對static變數的理解

static變數是在實際的程式設計中常遇到的,在面試中也會常被問到,下面對其進行一些總結。

  1. 靜態全域性變數:適用於這個全域性變數僅在單個檔案中進行訪問,別的檔案無法對其訪問時使用,可以降低模組間的耦合度。

  2. 靜態區域性變數 : 適用於在單個函式內使用,即使在同一個檔案中,別的函式也不能對其進行訪問,可以降低模組間的耦合度。

  3. 需要注意的一種說法就是,靜態全域性變數過大,可能會導致堆疊溢位,這句話看似正確,其實不然,因為靜態變數放在靜態區,不放在堆疊或棧區域,因此其不會導致堆疊溢位的錯誤。

  4. 如果想宣告一個class的專屬常量,可以,必須為其宣告一個成員,而為了保證期只有一份實體,則將其為static成員:
    如下所示實現方式 (參考:《effective c++》)

class game {
    static const int num = 5;
    int scores[num];
}

這裡注意其初始化的方式,單個const,或者static都不能在類內進行初始化的操作,這點尤為重要。