美文网首页
再读C和指针

再读C和指针

作者: 窗前的樱花 | 来源:发表于2021-07-09 14:54 被阅读0次
    int a;int *p0=&a;
    void *p=p0;    //可以不用写void *p=(int *)p0;
    

    在输出*p和p+1的值的时候会出错

    指针是强类型的,为什么不用一种通用的类型来表示指针:我们不仅仅保存地址,我们还使用指针来对地址进行解引用和写入数据
    递归调用会每次都产生调用空间,因此可能会导致栈溢出
    函数参数中传指针比传值可能会节省内存空间

    int a[]={1,2,3,4,5};
    计算数组长度,可以使用int size=sizeof(a)/sizeof(a[0]);
    但如果在函数中,将数组作为参数传递时候,如
    int add(int a[])
    {
    int size=sizeof(a)/sizeof(a[0]); //得到的永远是1
    }
    在数组定义处,sizeof(A)是该数组的实际占用空间,传递参数之后,sizeof(A)实际是一个指针所占用的大小,因为数组是以指针形式来传递的。也就是说,在另一个栈帧里面创建一个指针,而不是把整个数组复制进去。

    如果p不是数组名,仍然可以写成p[0],p[1]的形式

    char c[4]="john"; //错误,编译器强制要求至少为5
    char c[4]={'j','o','h','n'}; //正确

    char c1[20]="hello"; //存放在数组的内存空间,栈上
    char c2="hello"; //存放在常量区,不能修改内容

    int b[2][3];
    int (* p)[3]=b; //p是指向一个长度为3的数组的指针
    多维数组是数组的数组
    int c[3][2][2];
    int (p)[2][2]=c; //p是指向一个22的数组的指针

    如果定义了int c[4][4][3];那么在函数调用的时候,传递参数只能是int fun(int c[][4][3])或者int fun(int (*c)[4][3]),写成int fun(int ***c)是错误的
    如果定义了int * a[10];可以用参数int **p来接收,即int **p=a;

    stack overflow(栈溢出)的一种情况是:写了有问题的递归函数导致的无穷递归。但内存中给栈预留的空间,在运行中并不会增长。 但是,堆的大小在应用程序的整个周期是可变的。

    assert断言被定义为宏的形式(assert(expression)),而不是函数。assert 将通过检查表达式 expression 的值来决定是否需要终止执行程序。也就是说,如果表达式 expression 的值为假(即为 0),那么它将首先向标准错误流 stderr 打印一条出错信息,然后再通过调用 abort 函数终止程序运行;否则,assert 无任何作用。默认情况下,assert 宏只有在 Debug 版本(内部调试版本)中才能够起作用,而在 Release 版本(发行版本)中将被忽略。

    int (*f[ ])(); //f是一个数组,数组元素的类型是函数指针,它指向的函数返回值是一个整型数。
    int (f[])(); // 这个声明创建了一个指针数组,指针所指向的类型是返回值为整型指针的函数。

    在头文件中为了避免重复包含,经常写成如下方式:

    ifndef _HEADNAME_H

    define _HEADNAME_H 1 //也可以写成 #define _HEADNAME_H

    ...

    endif

    函数的返回值不能是一个函数,像foo()[]是非法的;函数的返回值不能是一个函数,像foo()()是非法的。
    函数的返回值允许是一个函数指针,如 int(fun())();
    函数的返回值允许是一个指向数组的指针,如int (
    foo()) [ ]
    数组里面允许有函数指针,如int (*foo[ ]) ( )

    在ANSI C标准中,signal声明如下:
    void (signal(int sig, void (func) ( int ) ) ) (int);
    可以分析,signal是一个函数,返回一个函数指针,该函数指针指向的函数接受一个int参数并返回void。可以用typedef来代表通用部分。
    typedef void(*ptr_to_func) (int);
    signal函数可简化为以下:
    ptr_to_func signal(int, ptr_to_func);

    define peach int

    unsigned peach i; //没问题

    typedef int peach;
    unsigned peach i; //错误,非法!!!
    可以用其他类型说明符对宏类型名进行扩展,但对typedef所定义的类型名却不能这么做。

    int a[10]; // 从数值上看,a的值和&a的值是一样的

    /* 将源文件的timestamp转换为表示当地格式日期的字符串 */
    char * localized_time(char * filename)
    {
        struct tm *tm_ptr;
        struct stat stat_block;
        char buffer[120];
        
        /* 获得源文件的timestamp, 格式为time_t */
        stat(filename, &stat_block);
        /* 把UNIX的time_t转换为tm结构,里面保存当地时间 */
        tm_ptr = localtime(&stat_block.st_mttime);
        /* 把tm结构转换成以当地日期格式表示的字符串 */
        strftime(buffer, sizeof(buffer), "%a %b %e %T %Y",tm_ptr);
       
        return buffer;
    }
    

    // 这段代码的问题在于最后一行返回局部变量的指针,当函数结束时,由于该变量已经被销毁,即当控制流离开
    //声明自动变量范围时,局部变量自动失效,无法知道这个局部变量指针所指的内容是什么

    #include <stdio.h>
    int *add(int *a, int *b)
    {
        int c = (*a) + (*b);  //应该改成 int *c=(int *) malloc(sizeof(int)); *c=(*a)+(*b);
        return &c;
    }
    int main()
    {
        int a = 2, b = 3;
        int *ptr = add(&a, &b);
        printf("The value is %d\n", *ptr);
        return 0;
    }
    

    以上代码是错误的,因为调用函数里面的变量,可能在调用完该函数之后,就会被释放了。正确的做法应该是用申请内存(堆)的方式,返回申请到的指针。返回全局的指针变量也可以。

    函数指针可以作为函数的一个参数来使用。回调函数示例。

    #include <stdio.h>
    void func_a()
    {
        printf("hello\n");
    }
    void func_b(void (*ptr)())
    {
        ptr(); //回调函数
    }
    int main()
    {
        void (*p)() = func_a;
        func_b(p);         // 通过函数指针调用函数
    
        func_b(func_a); //和上面两行写法一致
        return 0;
    }
    

    相关文章

      网友评论

          本文标题:再读C和指针

          本文链接:https://www.haomeiwen.com/subject/jebxxltx.html