天天看點

C/C++程式設計細節(二)——堆棧、數組、指針

1、堆與棧、靜态資料區

【堆棧的動态靜态配置設定】     

     A,靜态配置設定是指在編譯階段确定大小,由編譯器進行配置設定。

           棧的申請就是靜态配置設定的。

     B,動态配置設定是指在執行階段确定大小。

           堆的申請都是在執行過程中進行的,堆隻能進行動态配置設定。堆在動态配置設定時,要申請連續的記憶體空間,釋放後會産生碎片。

           棧也可以使用動态配置設定。

     C,堆是使用malloc()、calloc()、realloc()等函數動态配置設定的,而棧使用alloca()函數動态配置設定的記憶體空間,釋放的時候由編譯器自己釋放。

#include<stdio.h>
char *myString()
{
    char buffer[6] = {0};
    char *s = "Hello World!";
    for (int i = 0; i < sizeof(buffer) - 1; i++)
    {
        buffer[i] = *(s + i);
    }
    return buffer;
}
int main(int argc, char **argv)
{
    printf("%s\n", myString());
    return 0;
}
           
結果:空。buffer存在棧中,随着函數的的結束而消失。應該使用new。
int main()
{
    char *p = "hello,world";
    return 0;
}
           
p是局部變量,存在棧中;hello,world存在靜态資料區

【new、malloc】

a、動态配置設定。都是在堆(heap)上進行動态配置設定記憶體。

b、初始化。用malloc不進行初始化,

                   new 進行初始化,取決于變量定義的位置,在函數體外定義的變量都初始化為0,在函數體内定義的内置類型變量都不進行初始化。

c、delete 會調用對象的destructor,而free 不會調用對象的destructor。

new使用方法

1、new單個對象
new在自由空間配置設定記憶體,但其無法為其配置設定的對象命名,因次是無名的,配置設定之後傳回一個指向該對象的指針。
     int *pi = new int; // pi指向一個動态配置設定的,未初始化的無名對象
此new表達式在自由空間構造一個int類型對象,并傳回指向該對象的指針。

       new是預設初始化的,這意味着内置類型或組合類型的對象的值是無定義的,而類類型對象将用預設構造函數進行初始化。

2、new(多個對象)數組

new配置設定的多個對象也是預設初始化。但可以對數組進行值初始化,方法就是:在大小之後添加一對空括号。
int *pia = new int[10]; // 10個未初始化int
int *pia2 = new int[10](); // 10個值初始化為0的int

delete使用方法

1、回收用 new 配置設定的單個對象的記憶體空間的時候用 delete,回收用 new[] 配置設定的一組對象的記憶體空間的時候用 delete[]。

2、關于 new[] 和 delete[],其中又分為兩種情況:

(1) 基本資料類型配置設定和回收空間;基本類型的對象沒有析構函數,是以回收基本類型組成的數組空間用 delete 和 delete[] 都是應該可以的;

(2) 自定義類型配置設定和回收空間。但是對于類對象數組,隻能用 delete[],告訴編譯器,釋放的是一塊記憶體,逐一調用析構函數,然後釋放空間。

malloc系列函數

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)
動态配置設定一個長度為size的記憶體空間,并把記憶體空間的首位址指派給ptr,把ptr記憶體空間調整為size。
申請的記憶體空間不會進行初始化。

【靜态區】

        static:靜态局部變量一般在聲明處初始化,如果沒有顯式初始化,會被程式自動初始化為0;存放在已初始化區;

2、數組、指針的加減運算

int a[5]={1,2,3,4,5};
int *ptr=(int*)(&a+1);
printf("%d,%d",*(a+1),*(ptr-1));
           

輸出:2,5

分析:(1)a:數組名,是一個指針,也就是數組第一個元素的位址。*(a+1)等同于a[1],*(a+1)=2。

          (2)&a+1:a是數組名,是一個指針,那麼&a是什麼呢?指針的位址,其指向a[5]數組這個記憶體塊,是以 &a+1 指向數組最後一個元素的                    下一個位置,故*(ptr-1)指向數組的最後一個元素。

char *ptr;
char myString[] = "abcdefg";
ptr = myString;
ptr += 5;
           

代碼執行之後ptr指向的内容是?

fg

int a[3];
a[0] = 0; a[1] = 1; a[2] = 2;
int *p, *q;
p = a;
q = &a[2];
           

         則a[q - p] = 2

#include<stdio.h>
 
int main(void)
{
 int n;
 char y[10] = "ntse";
 char *x = y;
 n = strlen(x);
 *x = x[n];
 x++;
 printf("x=%s\n",x);
 printf("y=%s\n",y);
 return 0;
}
           

輸出:x=tse,y=

分析:n = strlen(x),此時n=4,因為x指向y數組,是以x[4]就是y[4]='\0',那麼*x=x[n]就是把x指向的字元串首元素改為'\0',

            x++之後x指向第二個字元t,是以第一個輸出x=tse,而y遇到第一個字元就是'\0',是以結束,y輸出為空

           本題關鍵是*x = x[n],把最後一個元素指派給首元素。

int main()
{
    printf("\n");
    int a[5] = {1, 3, 4, 5, 6};
    int *p, **k;
    p = a;
    k = &p;
    printf("%d", *(p++));
    printf("%d", **k);
    return 0;
}
           

           輸出:13

          分析:注意*(p++),它與*p++一樣,都是先運算*p,再運算p++,使得p的位址移動一位,是以是3。

【數組、指針大小】

void Func(char str_arg[100])
{
    printf("%d\n", sizeof(str_arg));
}
int main(void)
{
    char str[] = "Hello";
    printf("%d\n", sizeof(str));
    printf("%d\n", strlen(str));
    char *p = str;
    printf("%d\n", sizeof(p));
    Func(str);
}
           

程式輸出:6 5 4 4

分析:str數組名,sizeof(str)為數組的大小,注意這裡不能把str當做指針;這一點也可以從strlen(str)看出;

            指針的大小就是4;

            函數參數為數組的話,數組會退化為指針,是以Func(str)=4。 

在32位系統中:

char arr[] = {4, 3, 9, 9, 2, 0, 1, 5};
char *str = arr;
           

sizeof(arr) = 8

sizeof(str) = 4

strlen(str) = 5

char *p1= “123”, *p2 = “ABC”, str[50]= "xyz";
strcpy(str+2,strcat(p1,p2));
cout << str;
           

序的輸出結果是?

           出錯

           分析:p1,p2指向的是靜态資料區,大小已經配置設定好了,無法使用strcat連接配接,會出錯。

【指針的加減】

unsigned char *p1;
unsigned long *p2;
p1=(unsigned char *)0x801000;
p2=(unsigned long *)0x810000;
           

請問p1+5= 什麼?

p2+5= 什麼?

程式輸出:801010 810014

分析:p1+5=p1+5*1=p1+5*sizeof(unsigned char)=p1+5*1=0x801000+ox5=0x801005

            p2+5=p2+5*1=p2+5*sizeof(unsigned long)=p1+5*4=0x810000+20=0x810000+0x14=0x810014(進制轉換)

#include <stdio. h>
main()
{ 
    int c[6] = {10,20,30,40,50,60}, * p, * s;
    p = c; 
    s = &c[5];
    printf("%d\n", s-p );
}
           

程式運作後的輸出結果是?

答案:5

4、數組指針與二維數組

(一)int a[3][4],下面哪個不能表示 a[1][1]?

A、*(&a[0][0]+5)

B、*(*(a+1)+1)

C、*(&a[1]+1)

D、*(a[1]+1)

答案:C

解析:

在二維數組中a[1]表示的是a[1][0]的位址,數組在記憶體中連續存儲,是以a[1]+1表示的是a[1][1]的位址,是以D可以取得正确的值;

指針操作*(a+1)與a[1]等價,是以B也可以取得正确的值;

二維數組在記憶體中是行優先存儲的,是以A中a[0][0]的位址加5可以取得正确值;

C選項錯誤,應改為*(&a[1][0]+1),否則,則指向a[2][0]。

(二) 要使指針變量p指向2維數組A的第1個元素,正确的指派表達式是()

p=A或p=A[0]     

p=A[0]或p=A[0][0]

p=A[0]或p=&A[0][0]        

p=A或p=&A[0][0]  

答案:第三個選項

分析:  A、p = A:它是行指針。在多元數組中,A表示第一維數組,不是第一個元素;

            B、p = A[0]:它是指針。在二維數組中,指向的是二維數組的第一個元素;

            C、p = &A[0][0]:A[0][0],它是數組元素,表示二維數組的第一個元素,是以可以p = &A[0][0]。 

一維數組:數組名表示第一個元素

二維數組:數組名表示第一維數組,行指針;