再读C和指针

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 
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 
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和指针)