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]。 

一维数组:数组名表示第一个元素

二维数组:数组名表示第一维数组,行指针;





你可能感兴趣的:(C++,c,细节)