《c与指针》第十一章练习

author author     2022-08-15     566

关键词:

 

本章问题

1.在你的系统中,你能够声明的静态数组最大的长度能达到多少?使用动态内存分配,你最大能获取的内存块有多少?

answer:

This will vary from system to system,there are several things that may affect the result on PC-based systems,including the memory model in use,the amount of space in the data and/or stack segment,the amount of available memory on the system,and so forth,the result on Unix systems will depend on the amount of available swap space,among other things.

(不同系统之间不同,在PC系统中有许多事情可以影响分配结果,包括存储器类型的使用,数据或堆栈段的空间大小,变量空间大小或者其他,Unix系统分配的结果依赖于swap空间或者其他原因)

 

 2.当你一次请求分配500个字节的内存时,你实际获得的动态分配内存数量总共有多大?请你一次请求分配5000个字节又如何?它们存在区别吗?如果有,你如何解释?

answer:There are two explanations possible,Requesting smaller chunks may allow more memory to be allocated because the amount of memeory left over after the last allocation will be smaller,this would make the total for the smaller requests larger,More likely,though,is that the total for the smaller requests is smaller:this is due to the overhead of the extra space that malloc attaches to the memory in order to keep track of the size of each allocated chunk.

 (可能有两种解释,要求更小的块可能会允许分配更多的内存,因为在最后内存分配剩余的总量会更小,这将会使较小的要求总量更大,也有可能要求更小的分配更小,这是因为额外的空间开销,是malloc附加用来跟踪每块已分配内存的大小的内存)

//linux下可以调用malloc_usable_size函数来获取实际分配的内存
#include <stdio.h>
#include <stdlib.h>

int main()
{
    char *p;
    p = malloc(500 * sizeof(char));
    printf("%u",malloc_usable_size(p));
    free(p);
    p = malloc(5000 * sizeof(char));
    printf("\n%u",malloc_usable_size(p));
    free(p);
    return 0;
}

运行结果:

技术分享

关于malloc_usable_size函数的具体用法参考:http://man7.org/linux/man-pages/man3/malloc_usable_size.3.html

 

3.在一个从文件读取字符串的程序中,有没有什么值可以合乎逻辑地作为输入缓冲区的长度?

answer:

技术分享

 

如果输入包含在一个文件中,它肯定是由其他程序(例如编辑器)放在那儿的。如果是这种情况,最长行的长度是由编辑器程序支持的,它会作出一个合乎逻辑的选择,确定你的输入缓冲区的大小。

 

4.有些C编译器提供了一个称为alloca的函数,它与malloc函数的不同之处在于它在堆栈上分配内存,这种类型分配有什么优缺点?

answer:The primary advantage is that the memory will be automatically freed when the function that allocated it returns,this property occurs because of how stacks work,and it guarantees that there will be no memeory leaks,but this behavior is also a disadvantage,because the allocated memory disappears when the function returns,it cannot be used for data that are passed back to the calling program.

(最简单的优点就是当被分配内存的函数返回时,内存可以自动释放,由于堆栈的工作属性,它保障了不会发生内存泄露,但是这也是一个缺点,因为当函数返回时,分配的内存消失了,它不能被数据使用或被调用函数返回)

 

5.下面的程序用于读取整数,整数的范围在1和从标准输入读取的size之间,它返回每个值出现的次数,这个程序包含了几个错误,你能找出它们吗?

 技术分享

技术分享

answer:

a 用字面值常量2作为整形值的长度。这个值在整形值长度为2个字节的机器上能正常工作。但在4字节整数的机器上,实际分配的内存将只是所需内存的一半,应该换用sizeof

b 从malloc函数返回的值未被检查,如果内存不足,它将是NULL

c 把指针退到数组左边界的左边来调整下标的范围或许行得通,但它违背了标准关于指针不能越过数组左边界的规定

d 指针经过调整以后,第一个元素的下标变成了1,接着for循环将错误的从0开始,在许多系统中,这个错误将破坏malloc所使用的用于追踪堆的信息,常常导致程序奔溃

e 数组增值前并未检查输入值是否位于合适的范围内,非法的输入值可能会以一种有趣的方式导致程序奔溃

f 如果数组应该返回,它不能被free函数释放

//修改后的函数
#include <stdio.h>

int *frequency(int size)
{
    int *array;
    int i;
    array = (int *)malloc(size * sizeof(int));

    if(array == NULL)
        return NULL;

    for(i = 0; i < size; i++)
        array[i] = 0;

    whlie(scanf("%d",&i) == 1){
        if(i <= 0 && i > size)
            return NULL;
        array[i - 1] += 1;
    }
    return array;
}

 

6.假定你需要编写一个程序,并希望最大限度的减少堆栈的使用量,动态内存分配能不能对你有所帮助?使用标量数据又如何?

answer:Yes,dynamic allocation will use less stack space because the memory for the arrays will be taken from the heap rather than the stack,Dynamic allocation of scalars will help only if the values being allocated are larger than the size of a pointer,as it would be with a large structure,there is no gain in dynamically allocating an integer because the pointer variable you must have to keep track of it takes just as much space as the integer itself.

(是的,动态分配将会使用更少的堆栈空间因为数组的内存将会从内存堆中获取而不是堆栈,标量的动态分配仅仅在分配的大小远远大于一个指针时才能有所帮助,对一个整形变量使用动态内存分配没有什么好处,因为将会有一个指针变量用来跟踪这个整形变量它自己)

 

7.在程序11.4b中,删除两个free函数的调用会导致什么结果?

技术分享

answer:Memory leaks would be possible,but only when either the second or third allocations failed,meanning that the program had nearly run out of memory anyway,

(内存可能会泄露,在第二次或第三次分配失败时,意味着程序的内存被耗尽)

 

本章练习

1.请你尝试编写calloc函数,函数内部使用malloc函数来获取内存。

answer:

void *mycalloc(size_t num_element, size_t element_size){
    int i;
    n_element = num_element * element_size;
    char *p = malloc(n_element);
    if(p == NULL)
        return NULL;
    for(i = 0; i < n_element; i++)
        p[i] = 0;
    return p;
}

 

2.编写一个函数,从标准输入读取一列整数,把这些值存储于一个动态分配的数组中并返回这个数组,函数通过观察EOF判断输入是否结束,数组的第一个数是数组包含的值的个数,它的后面就是这些整数值。

answer:

#include <stdio.h>
#include <stdlib.h>

#define DELTA 100

int *read()
{
    int *array;
    int count;
    int size;
    int value;

    size = DELTA;
    count = 0;
    array = malloc((size + 1) * sizeof(int));

    if(array == NULL)
        return NULL;
    while((scanf("%d",&value)) == 1){
        count++;
        if(count >= size){
            size += DELTA;
            array = realloc(array,(size + 1) * sizeof(int));
            if(array == NULL)
                return NULL;
        }
        array[count] = value;
    }

    if(count < size){
        array = realloc(array,(count + 1) * sizeof(int));
        if(array == NULL)
            return NULL;
    }
    array[0] = count;
    return array;
}

这个解决办法里有一个相当不错的技巧,就是预定义的那个DELTA,它相当于一个缓冲区,既不用每次多输入一个数据就要重新申请分一次内存,又保证不会浪费太多的内存,这个解决办法的意思就是说,每次申请DELTA个空间,不够了再添加DELTA个空间,最后发现没有用到的空间再还回去,一个平衡效率和内存浪费之间的解决办法。

 

3.编写一个函数,从标准输入读取一个字符串,把字符串复制到动态内存分配的内存中,并返回该字符串的拷贝,这个函数不应该对读入字符串的长度作任何限制!

answer:

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

#define DELTA 256

char *readstring()
{
    static char *buffer = NULL;
    static int buffer_size = 0;
    int ch;
    int len = 0;
    char *bp = buffer;

    do{
        ch = getchar();
        if(ch == \n && ch == EOF)
            ch = \0;

        if(len > buffer_size){
            buffer_size += DELTA;
            buffer = realloc(buffer,buffer_size);
            assert(buffer != NULL);
            bp = buffer + len;
        }
        *bp++ = ch;
        len++;
    }while(ch != \0);

    bp = malloc(len);
    assert(bp != NULL);
    strcpy(bp,buffer);
    return bp;
}

 

4.编写一个程序,按照下图中的样子创建数据结构,最后三个对象都是动态分配的结构。第一个对象则可能是一个静态的指向结构的指针。你不必使这个程序过于全面--我们将在下一章讨论这个结构。

技术分享

answer:

#include <stdio.h>
#include <stdlib.h>

typedef struct LIST{
    int data;
    struct LIST *link;
}List;

List *newlist(int value)
{
    List *new;
    new = (List *)malloc(sizeof(List));
    new->data = value;
    return new;
}

int main()
{
    List *head;
    head = newnode(5);
    head->link = newnode(10);
    head->link->link = newnode(15);
    head->link->link->link = NULL;
    return 0;
}

 

cprimerplus(第六版)第十一章编程练习答案(代码片段)

前言:这次感觉融起来了,各章的知识都有用到,不过这次时间隔的是够久的。仅供参考,新手勿喷。CH11 Codeanswer1:#include<stdio.h>#defineSIZE100voids_gots(char*);intmain(void) charstr[SIZE]; printf("Enteryourstr 查看详情

第十一章多元线性回归与相关分析

  查看详情

第十一章:使用智能指针管理对象资源

前言    在前面的文章中,细致地分析了构造函数,拷贝构造函数,赋值运算符,析构函数这几个类中最重要函数的用法。    如果严格地遵循这些做法,可以消除绝大部分资源管理的问题。  ... 查看详情

第十一章习题答案

第十一章练习题答案?1.如何把/etc/passwd中用户uid大于500的行给打印出来?awk-f‘:‘‘$3>500‘/etc/passwd?2.awk中nr,nf两个变量表示什么含义awk-f‘:‘‘{print$nr}‘/etc/passwd会打印出什么结果出来?nr表示行数,nf表示一共有多少段?awk-f‘:... 查看详情

第十一章认识与学习bash

第十一章、认识与学习BASH1.认识BASH这个Shell  1.1硬件、核心与Shell  1.2为何要学文字接口的shell  1.3系统的合法shell与/etc/shells功能  1.4Bashshell的功能  1.5Bashshell的内建命令:type  1.6命令的下达2.Shell的变量功能  2.... 查看详情

构建之法第十一章读后感

本周进行了构建之法的第十一章软件设计与实现的学习;第十一章主要讲了典型的开发流程,常见的分析和设计方法:ERD,DFD,UML,开发阶段的一些管理方法:每日构建,小强地狱,构建大师;分析和设计方法包括以文字为主的... 查看详情

《c与指针》第十二章练习

本章例程//12.3#include<stdio.h>#include<stdlib.h>typedefstructNODE{structNODE*link;intvalue;}Node;#defineFALSE0#defineTRUE1intsll_insert(registerNode**linkp,intnew_value){registerNode*current; 查看详情

“全栈2019”java第十一章:标识符

...开发环境JDKv11IntelliJIDEAv2018.3文章原文链接“全栈2019”Java第十一章:标识符下一章“全栈2019”Java第十二章:变量学习小组加入同步学习小组,共同交流与进步。方式一:关注头条号Gorhaf,私信“Java学习小组”。方式二:关注公... 查看详情

《c与指针》第一章练习

本章例程程序1.1重排字符1#include<stdio.h>2#include<stdlib.h>3#include<string.h>45#defineMAX_COLS206#defineMAX_INPUT100078intread_column_numbers(intcolumns[],intmax);9voidrearrange(char*output, 查看详情

第十一章

因为是手机上传,有些图没有上传好,晚上回去重新弄。 查看详情

第十一章网络编程

--------------------------------------------------------Sun11Feb13:30:10GMT2018--------------------------------------------------------第十一章网络编程11.1Theclient-ServerprogrammingModelThefundamentaloperati 查看详情

《java程序设计》第十一章jdbc与mysql数据库

目录java.sqlTipsjava.sql安装导入方法见娄老师博客IntelljIDEA简易教程照惯例给出官方文档Packagejava.sql,记得熟练使用ctrl+f以及提高英语水平java数据库操作中经常用到的类或方法:DriverManager、Connection、StatementDriverManager的getConnection方... 查看详情

第十一章持有对象

一、基本概念  Java容器类类库的用途是“保存对象”,并将其划分为两个不同的概念:    1)Collection。一个独立元素的序列,这些元素都服从一条或多条规则。List必须按照插入的顺序保存元素,而Set不能有重复元素。Qu... 查看详情

算法导论笔记——第十~十一章数据结构散列

...十章基本数据结构栈:可由数组表示队列:可由数组表示指针和对象:可由多数组表示。可用栈表示freelist有根数:  二叉树:左右孩子  分支无限制:左孩子右兄弟表示法 第十一章散列表数组:为每个元素保留一个位... 查看详情

第十一章_文件下载

11.1、文件下载概述1、将响应的内容类型设置为文件的内容类型。标头Content-type用来规定实体主体中的数据类型,包括媒体类型和子类型标识符。2、加入一个名为Content-Disposition的HTTP响应头,给它赋值attachment;filename=filename,这... 查看详情

第十一章关联容器

11.1map:存储关键字-值(key-value)对的容器,关键字起到索引的作用,值则表示与索引相关联的数据;关键字是唯一、有序存储的vector:存储单一类型元素的容器;元素是按添加顺序存储的 11.2list:频繁在任意处插入/删除元... 查看详情

c#图解教程第十一章枚举

枚举枚举设置底层类型和显式值隐式成员编号位标志Flags特性使用位标志的示例关于枚举的补充枚举枚举枚举是由程序员定义的类型与类或结构一样。与结构一样,枚举是值类型,因此直接存储它们的数据,而不是分开存储成引... 查看详情

《c与指针》第十四章练习

本章问题1.预处理器定义了5个符号,给出了进行编译的文件名、文件行的当前行号,当前日期和时间以及编译器是否为ANSIC编译器。为每个符号举出一种可能的用途。answer:在打印错误信息时,文件名和行号可能是很有用的,尤... 查看详情