C语言中的sizeof和malloc


=Start=

缘由:

最近在用C语言写程序,由于C语言的基础已经忘得差不多了(也可能是之前就没学好……),在实现一个简单的功能时就碰到了好多问题,在这里就记录一下其中比较典型的和sizeof和malloc相关的问题(感觉用这种接近于实战的编程题去面试候选者,效果会很好,^_^)

正文:

参考解答:

sizeof是C语言的一种单目操作符,如C语言的其他操作符++、–等。它并不是函数。sizeof操作符以字节形式给出了其操作数的存储大小。操作数可以是一个表达式或括在括号内的类型名。操作数的存储大小由操作数的类型决定。


void *malloc(size_t size);
void free(void *ptr);

The malloc() function allocates size bytes and returns a pointer to the allocated memory. The memory is not initialized. If size is 0, then malloc() returns either NULL, or a unique pointer value that can later be successfully passed to free().

The free() function frees the memory space pointed to by ptr, which must have been returned by a previous call to malloc(), calloc() or realloc(). Otherwise, or if free(ptr) has already been called before, undefined behavior occurs. If ptr is NULL, no operation is performed.

在下面的一段代码中存在几个错误和几个不太合理的地方,请找出来并说明原因:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
char *get_current_time(const char* format) {
    time_t t;
    struct tm *tmp;
    t = time(NULL);
    tmp = localtime(&t);
    if (tmp == NULL) {
        perror("localtime");
        return NULL;
    }
    char *outstr = (char *) malloc(sizeof(char) * 128);
    if (outstr == NULL) {
        perror("malloc");
        return NULL;
    }
    if (strftime(outstr, sizeof(outstr), format, tmp) == 0) {
        fprintf(stderr, "strftime returned 0");
        return NULL;
    }
    printf("Result string is \"%s\"\n", outstr);
    return outstr;
}
int main(int argc, char const *argv[]) {
    char *time_str;
    if (argc == 2) {
        time_str = get_current_time(argv[1]);
    } else {
        time_str = get_current_time("%F %T");
    }
    printf("%s\n", time_str);
    return 0;
}
错误1、在get_current_time函数里:
char *get_current_time(const char* format) {
    time_t t;
    struct tm *tmp;
    t = time(NULL);
    tmp = localtime(&t);
    if (tmp == NULL) {
        perror("localtime");
        return NULL;
    }
    char *outstr = (char *) malloc(sizeof(char) * 128);
    if (outstr == NULL) {
        perror("malloc");
        return NULL;
    }
    // if (strftime(outstr, sizeof(outstr), format, tmp) == 0) {
    /*
        strftime returned 0
        Segmentation fault (core dumped)
        问题出在sizeof的使用上,因为outstr只是一个(char *)类型的指针,所以sizeof(outstr)==4
        而我们的需求实际上是获取outstr指向内容被分配到的长度大小,没办法通过sizeof来获取,最好是自己在malloc时记录一下
    */
    if (strftime(outstr, 128, format, tmp) == 0) {
        fprintf(stderr, "strftime returned 0");
        return NULL;
    }
    printf("Result string is \"%s\"\n", outstr);
    return outstr;
}
错误2、在main函数里:
int main(int argc, char const *argv[]) {
    char *time_str;
    if (argc == 2) {
        time_str = get_current_time(argv[1]);
    } else {
        time_str = get_current_time("%F %T");
    }
    printf("%s\n", time_str);
    /*
        因为 get_current_time 返回的是指向 malloc 分配空间的指针,
        所以在使用完了之后需要手动显示 free ,避免内存泄露的问题。
    */
    free(time_str);
    return 0;
}
参考链接:

=END=

, ,

《 “C语言中的sizeof和malloc” 》 有 6 条评论

  1. C语言写的(一次性执行)程序和(长时间执行的)后台进程在对内存/文件资源的泄露的容忍度方面是不一样的,虽然泄露这种问题不应该发生,但一旦发生了,两种不同的情况也决定了它能产生的后果也是不同(甚至有很大差别的)。

    常见的资源泄露有:内存泄露、文件描述符泄露。

    内存泄露对于后台进程来说是不可容忍的,但是对于一次性执行的小程序来说,后果则没那么严重——因为它在执行完后立即退出了,没有手动释放的内存操作系统会主动进行回收,所以不会造成什么严重的问题;除非执行时间特别长,把系统的内存耗光了。
    文件描述符泄露也存在类似情况,但暂时还不清楚操作系统会不会主动回收,且文件描述符的资源要多得多,所以一时半会不会出现明显的异常。

    一个文件描述符泄露的情况:「supervisord 的 fd 泄露问题」
    http://xiezhenye.com/2015/11/supervisord-%E7%9A%84-fd-%E6%B3%84%E9%9C%B2%E9%97%AE%E9%A2%98.html

  2. malloc分配的内存都是连续的么?
    `
    简短回答:是的(连续的虚拟内存,但无法保证是连续的物理内存)
    malloc will fail if there isn’t a large enough contiguous block available. (A failure with malloc will return a NULL pointer)

    malloc() doesn’t know or care about physical memory. No user-space program does. Only the operating system kernel knows the mapping between virtual and physical pages.

    As a user-space program you have no control over physical pages. If a user-space program requests a 1GB allocation from the operating system, the result will be 1GB of contiguous virtual memory, but the individual physical pages used to satisfy that request have no requirement whatsoever. They can be a random patchwork of pages from all over the place. User-space does not know or care. This is the case regardless of the scenario, so in your example there was no guarantee that your initial eight allocations used contiguous physical pages, which it almost certainly did not.
    `
    https://stackoverflow.com/questions/625270/does-malloc-allocate-a-contiguous-block-of-memory
    https://linux.die.net/man/3/malloc
    https://www.reddit.com/r/C_Programming/comments/4t1qvz/does_malloc_allocate_contiguous_physical_memory/

  3. C语言中sizeof对于指针类型数据不生效
    https://stackoverflow.com/questions/492384/how-to-find-the-sizeof-a-pointer-pointing-to-an-array

    C语言中对从参数中传递过来的数组无法用sizeof判断其大小
    https://stackoverflow.com/questions/5493281/c-sizeof-a-passed-array/5493327#5493327
    https://stackoverflow.com/questions/609267/passing-char-buffer-to-functions-and-getting-the-size-of-the-buffer
    `
    C语言中的函数参数根本就没有array这一类型,不论你传递的是:
    void printarray( double p[], int s )
    还是:
    void printarray( double p[100], int s )
    都会被当做是:
    void printarray( double* p, int s )

    所以,解决办法就是,在参数列表中显示添加一个表示该array大小的参数。
    `

    C语言中数组参数的长度如何判断?
    https://stackoverflow.com/questions/8269048/length-of-array-in-function-argument
    http://c-faq.com/aryptr/aryptrparam.html #参数中数组是被当做指针传递的
    `
    sizeof 只在对原生数组进行操作的时候可以获取它的大小,当它作用于一个指向数组的指针时,它给出的是该指针的大小(一般和平台架构有关),而非数组的大小。
    `

  4. C语言中如何正确的给函数传递一个二维数组?(linux c pass 2 dimensional array to function)
    https://stackoverflow.com/questions/9446707/correct-way-of-passing-2-dimensional-array-into-a-function
    http://c-faq.com/aryptr/pass2dary.html
    `
    void display(int p[][numCols])
    `
    https://stackoverflow.com/questions/21021562/passing-a-two-dimensional-array-to-a-function

    C语言二维数组字符串的赋值
    http://www.linuxidc.com/Linux/2015-01/112070.htm
    `
    在被调用函数的形参数组定义可以省略第一维的大小,第二维不可省略,如下:
    void fun(int a[5][20]); //合法
    void fun(int a[][20]); //合法
    void fun(int a[5][]); //不合法
    void fun(int a[][]); //不合法
    将二维数组作为形参时,第一维大小可以省略,第二维不可省略,这是由编译器原理限制的,在内存中按数组排列规则存放(按行存放),而并不区分行和列,如果在形参中不说明列数,则系统无法决定应为多少行多少列。
    `

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注