14. C语言&数据结构与算法(99道)***3***

3.内存管理&编程题(20道)

3.1gcc编译的C语言程序占用的内存分为哪几个部分?

栈区(stack)

存放函数的参数、局部变量。

堆区(heap)

提供程序员动态申请的内存空间。

全局(静态)区(static)

存放全局变量和静态变量,初始化不为0的全局变量和静态变量、const型常量在一块区域(.data段),未初始化的、初始化为0的全局变量和静态变量在相邻的另一块区域(.bss段)。

程序代码区

存放函数体的二进制代码和字符串常量。


3.2小端:一个数据的低位字节数据存储在低地址
   大端:一个数据的高位字节数据存储在低地址
   例如:int a=0x12345678;  //a首地址为0x200,大端存储格式如下:

如何判读一个系统的大小端存储模式?

(1)方法一:int *强制类型转换为char *,用“[]”解引用

void checkCpuMode(void)  
{  
    int c = 0x12345678;  
    char *p = (char *)&c;  
    if(p[0] == 0x12)  
        printf("Big endian.\n");  
    else if(p[0] == 0x78)  
        printf("Little endian.\n");  
    else  
        printf("Uncertain.\n");  
}  

(2)方法二:int *强制类型转换为char *,用“*”解引用

void checkCpuMode(void)  
{  
    int c = 0x12345678;  
    char *p = (char *)&c;  
    if(*p == 0x12)  
        printf("Big endian.\n");  
    else if(*p == 0x78)  
        printf("Little endian.\n");  
    else  
        printf("Uncertain.\n");  
}  

(3)方法三:包含short跟char的共用体

void checkCpuMode(void)  
{  
    union Data  
    {  
        short a;  
        char b[sizeof(short)];  
    }data;  
    data.a = 0x1234;  
  
    if(data.b[0] == 0x12)  
        printf("Big endian.\n");  
    else if(data.b[0] == 0x34)  
        printf("Little endian.\n");  
    else  
        printf("uncertain.\n");  
}  


3.3全局变量和局部变量的区别?

(1)全局变量储存在静态区,进入main函数之前就被创建,生命周期为整个源程序。

(2)局部变量在栈中分配,在函数被调用时才被创建,在函数退出时销毁,生命周期为函数内。


3.4以下程序中,主函数能否成功申请到内存空间?

#include<stdio.h>  
#include<stdlib.h>  
#include<string.h>  
void getmemory(char *p)  
{  
    p = (char *)malloc(100);  
    strcpy(p, "hello world");  
}  
int main()  
{  
    char *str = NULL;  
    getmemory(str);  
    printf("%s\n", str);  
    free(str);  
    return 0;  
}  

答案:不能。

解读:getmemory(str)没能改变str的值,因为传递给子函数的只是str的复制值NULL,main函数中的str一直都是 NULL。正确的getmemory()如下:

①传递的是二重指针,即str的指针
void getmemory(char **p)   
{  
    *p = (char *)malloc(100);  
    strcpy(*p, "hello world");  
}  
②传递的是指针别名,即str的别名,C++中
void getmemory(char * &p)   
{  
    p = (char *)malloc(100);  
    strcpy(p, "hello world");  
}  

3.5 请问运行下面的Test()函数会有什么样的后果?
void GetMemory(char **p, int num)  
{  
    *p = (char *)malloc(num);  
}  
void Test(void)  
{  
    char *str = NULL;  
    GetMemory(&str, 100);  
    strcpy(str, "hello");   
    printf("%s\n", str);   
}  

答案:内存泄漏。

解读:调用malloc()申请内存空间,使用完毕之后没有调用free()释放内存空间并使指针指向NULL。


3.6 请问运行下面的Test()函数会有什么样的后果?

char *GetMemory(void)  
{   
    char p[] = "hello world";  
    return p;  
}  
void Test(void)  
{  
    char *str = NULL;  
    str = GetMemory();   
    printf("%s\n", str);  
}  

答案:打印野指针内容,可能是乱码。

解读:GetMemory()返回的是指向栈内存的指针,但该栈内存已被释放,该指针的地址不是 NULL,成为野指针,新内容不可知。


3.7 请问运行下面的Test()函数会有什么样的后果?

void Test(void)  
{  
    char *str = (char *) malloc(100);  
    strcpy(str,"hello");  
    free(str);       
    if(str != NULL)  
    {  
        strcpy(str, "world");   
        printf("%s\n", str);  
    }  
}   

答案:篡改堆区野指针指向的内容,后果难以预料,非常危险。

解读:

(1)free(str);之后,str成为野指针,没有置为NULL,if(str != NULL)语句不能阻止篡改操作。

(2)野指针不是NULL指针,是指向被释放的或者访问受限的内存的指针。

(3)造成野指针原因:①指针变量没有被初始化,任何刚创建的指针不会自动成为NULL;②指针被free或delete之后,没有置NULL;③指针操作超越了变量的作用范围,比如要返回指向栈内存的指针或引用,因为栈内存在函数结束时会被释放。


3.8C语言中memcpymemmove是一样的吗?

答案:
(1)memcpy()与memmove()一样都是用来拷贝src所指向内存内容前n个字节到dest所指的地址上。

(2)不同的是,当src和dest所指的内存区域重叠时,memcpy可能无法正确处理,而memmove()仍然可以正确处理,不过执行效率上略慢些。

解读:

(1)memcpy()无论什么情况下,都是从前往后拷贝内存。当源地址在前,目的地址在后,且两个区域有重叠时,会造成拷贝错误,达不到理想中的效果。

void *memcpy(void *dest, const void *src, size_t count)  
{  
    if(dest == NULL || src == NULL || count <= 0)  return NULL;  
    char *d = (char *)dest;  
    char *s = (char *)src;  
    while(count--)  
    {  
        *d++ = *s++;  
    }  
    return dest;  
}  

(2)mem

剩余60%内容,订阅专栏后可继续查看/也可单篇购买

包含241道笔试面试真题详解,还有岗位及校招介绍,简历制作、笔试准备、面试准备的经验分享,以及嵌入式软件工程师技能树解读。你想知道的所有关于嵌入式求职的问题,几乎都可以在这里找到答案。 订阅即赠送学习笔记、简历模板、面试提纲模板、内推机会,需要的同学点击我头像私信即可!

全部评论
在题目3.4中,如果要传递str的指针 主函数的getmemory(str)应该加一个取地址符吧,getmemory(&str)
6
送花
回复
分享
发布于 2021-04-10 20:16
3.20里在主函数写数组char a[]="A man, a plan, a canal: Panama"  调用函数传递数组指针可以得到正确结果,但是如果用char *p="A man, a plan, a canal: Panama"的话 传递字符串常量指针,编译不会出错但是运行会报段错误,看了下函数本身也没有对字符串常量的内容进行修改,想了很久也没想到解决方法和错误成因,不知道你有没有考虑过这个问题。
2
送花
回复
分享
发布于 2021-04-11 17:31
滴滴
校招火热招聘中
官网直投
3.2(3)很不明白,union的成员变量不是只能有一人带值么?为什么前脚给a赋值了,后脚就去调用b了啊?
1
送花
回复
分享
发布于 2021-09-15 21:25
3.19大小端转换好像有问题
1
送花
回复
分享
发布于 2022-07-29 13:50
受益匪浅
点赞
送花
回复
分享
发布于 2021-06-13 11:16
3.1程序代码区中只有二进制代码,并无字符串常量,字符串常量时存放在全局区的。
点赞
送花
回复
分享
发布于 2021-07-12 11:50
学到了
点赞
送花
回复
分享
发布于 2021-09-05 15:44
3.20在进入第一个while时判断是字母或者数字就进行了++和--操作,这样是不是把字符串第一个和最后一个跳过了呀?
点赞
送花
回复
分享
发布于 2023-04-19 14:27 江苏
3.17只返回了一个位置参数啊
点赞
送花
回复
分享
发布于 2023-07-24 15:23 北京
3.15,bit应该是7吧
点赞
送花
回复
分享
发布于 2023-09-07 15:57 陕西
关于3.1 const常量的存放位置,应该存在在.rodata只读数据段,见如下实验: ubuntu 20.04 gcc9.4.0 下面程序: #include <stdio.h> ​ #define HELLO "world\n" const int i = 10; int const j = 20; char *p = "hello ubuntu\n"; int main(void){        char c_val = 'a';        printf("hello%s",HELLO);        printf("%s",p);        printf("c_val = %c\n",c_val);        return 0; }使用gcc编译之后,使用objdump -j .rodata -s a.out之后得到 a.out:     file format elf64-x86-64 ​ Contents of section .rodata: 2000 01000200 0a000000 14000000 68656c6c ............hell 2010 6f207562 756e7475 0a00776f 726c640a o ubuntu..world. 2020 0068656c 6c6f2573 00257300 635f7661 .hello%s.%s.c_va 2030 6c203d20 25630a00                   l = %c..然后通过readelf -s a.out和readelf -S a.out可以看到变量i和j通过const修饰后也放在了.rodata段。 ​ [18] .rodata           PROGBITS         0000000000002000  00002000       0000000000000038  0000000000000000   A       0     0     4 [25] .data             PROGBITS         0000000000004000  00003000       0000000000000018  0000000000000000 WA       0     0     8 ​    54: 0000000000002008     4 OBJECT GLOBAL DEFAULT   18 j    67: 0000000000002004     4 OBJECT GLOBAL DEFAULT   18 i    56: 0000000000004010     8 OBJECT GLOBAL DEFAULT   25 p ​可以看到字符串常量,const型常量存放在只读数据区,但是指针p是在.data段的</stdio.h>
点赞
送花
回复
分享
发布于 05-08 16:40 四川

相关推荐

3 2 评论
分享
牛客网
牛客企业服务