问题
今天看到《自制编程语言》里面的提到这个不完全类型,我还真不知道
细节
大概是这样的,不让别人知道我们代码内部实现细节 
比如我们实现一个栈,我们把数据结构的定义放在头文件里面,然后别人include我们头文件的时候(并添加.o),就能看到具体细节,比如可能就会主动操作里面的num或者array,破坏封装性  
#ifndef STACK_H
#define STACK_H
typedef struct Stack{  
    int num;
    int array[MAX_N];
}Stack;
#endif
实现
按照如下实现就不会有这样的问题
//stack.h
#include <stdio.h>
#include<stdlib.h>
#include<string.h>
#ifndef STACK_H
#define STACK_H
typedef struct MyStack Stack;  
Stack *createStack();  
void destroyStack(Stack **s);  
int push(Stack **s, int w);  
int isEmpty(Stack **s);  
int pop(Stack **s);  
#endif
//stack.c
#include "stack.h"
#define MAX_N  100
typedef struct MyStack_tag{  
    int num;
    int array[MAX_N];
    struct MyStack *node; 
}MyStack;
Stack *createStack() {  
    MyStack *s = (MyStack *)malloc(sizeof(MyStack));
    return (Stack *)s;
}
void destroyStack(Stack **s) {  
    MyStack *stack = (MyStack *)*s;
    free(stack);
    *s = NULL;
    stack = NULL;
}
int push(Stack **s, int w) {  
    MyStack *stack = (MyStack *)*s;
    if(stack->num + 1 <= MAX_N) {
        stack->array[stack->num++] = w;
        return 1;
    }
    return 0;
}
int isEmpty(Stack **s) {  
    MyStack *stack = (MyStack *)*s;
    if(0 == stack->num) {
        return 1;
    }
    return 0;
}
int pop(Stack **s) {  
    MyStack *stack = (MyStack *)*s;
    if(0 == stack->num) {
        return 0;
    }
    return stack->array[--stack->num];
}
int getLen(Stack **s) {  
    MyStack *stack = (MyStack *)*s;
    return stack->num;
}
//main.c
#include "stack.h"
#include <stdio.h>
#include <stdlib.h>
int main() {  
    Stack *s = createStack();
    push(&s, 2);
    printf("%d\n", pop(&s));
    destroyStack(&s);
}
大概逻辑是这样的,我们自己的库: 
头文件:typedef struct MyStack Stack;还有各种对外的接口函数 
cpp文件:有详细对自己内部MyStack的定义,可见Mystack,也可见Stack,内部实现的用Stack结构体 
外部调用:#include头文件,调用跟Stack相关接口,这里注意,因为main.c里面,Stack是延迟定义的,所以不能使用sizeof等等,也就是说不能直接使用malloc Stack;而且main.c对MyStack不可见,不能使用MyStack
其他语言
面向对象语言中不叫“延迟定义”。而是叫封装、访问权限、接口和多态。类的封装和访问权限可以阻止客户访问类的实现细节,而接口和多态可以隐藏实现细节。
其他说明
Linux C编程一站式学习上面的 
C语言的类型分为函数类型、对象类型和不完全类型三大类。对象类型又分为标量和非标量类型。不完全类型是暂时没有完全定义好的类型,编译器不知道这种类型该占几个字节的存储空间。具有不完全类型的变量可以通过多次声明组合成一个完全类型。不完全类型的结构体有重要作用:  
struct  s{
        char  data[6];
       struct  s  *next;
};
当编译器处理到第一行struct  s{ 时,认为struct  s{是一个不完全类型,当处理到第三行struct  s *next;时,认为next是一个指向不完全类型的指针,当处理到第四行};时struct  s成了一个完全类型,next也成了一个指向完全类型的指针。但不可再结构体内定义struct  s next,因为它不知道next将占用几个字节,所以结构体可以递归的定义指针成员但不可以递归的定义变量成员。