C语言基础 - HeavyYuan/A-CD-Record-Management-System GitHub Wiki

typedef定义新类型

  1. 数据类型定义
typedef int myint;  //定义新类型myint

int main()
{
  myint a = 1;
  printf("%d\n",myint);
}
  1. 函数类型定义
typedef int (*MyFun)(void) //定义了返回值为char类型的函数指针MyFun

int func()
{
   printf("This is func\n");
   return 0;
}

int main()
{
   MyFun myfun = func;
   myfun();

}

setjmp & longjmp

int setjmp(amp_buf env); void longjmp(jmp_buf env, int val);

man page:

longjmp()  and  setjmp(3) are useful for dealing with errors and interrupts encountered in a low-level subroutine of a program.

setjmp() saves the stack context/environment in env for later use by longjmp(3). 
The stack context will be invalidated if the function which called setjmp() returns.

longjmp() restores the environment saved by the last call of setjmp(3) with the corresponding env argument. After longjmp() is completed, 
program execution continues as if the corresponding call of setjmp(3) had just returned the value val.  longjmp() cannot cause 0 to be
returned.  If longjmp() is invoked with a second argument of 0, 1 will be returned instead.

longjmpsetjmp在程序底层子路径的错误处和中断处理上很有用处。

setjmp当前堆栈上下文/环境存储到jmp_buf env变量中,longjmp加载前述env,实现跳转。(跳转到setjmp的调用处)

setjmp的第一次调用的返回值为0,通过longjmp回跳后的返回值是val

这里的env必须是全局变量。

实例:

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

jmp_buf env;

void jmp_point()
{
    fprintf(stderr, "Jumped, and unnormal exit\n");
    exit(1);
}

int main()
{
    int ret;

    ret = setjmp(env);
    if(!ret)
    {
        printf("Start\n");
    }
    else if(ret == 2) //验证跳转的返回值是否等于longjmp的参数2
    {
        jmp_point();
    }else{
        printf("nothing\n");
    }

    longjmp(env, 2);

    printf("Normal exit\n");
    return 0;
}

程序输出Start
Jumped, and unnormal exit

volatile关键字

volatile int count;

count在当前进程会被用到,在其他进程会被用到和修改,此种情况下,count应该做上述声明,保证count的修改可以反应到各个进程。

在没有volatile时,当前进程的count会被优化,导致当前进程在用count时,直接取用的寄存器里的count值。

而其他进程在修改count值时,修改的是内存中的值,致使运行结果和预期不符。

volatile告诉编译器不要将count优化到寄存器(或每次用时需要从内存存取),避免了count所在的当前进程,用寄存器的count值,而不是从内存读取。

常量指针 VS 指针常量

int const *p;  --   常量指针,指向的整形是常量
int * const p; ---  指针常量,指针是常量

const 代表常量,*代表指针。

名称记忆:

根据const*的位置顺序来记忆,const *是常量指针,* const是指针常量。

含义记忆:

const*的位置顺序不同,那么const修饰的东西就不同。

const *p, const修饰的是*p*p就是指针所指向的值,所以指针所指的值是常量;

* const pconst修饰的是p, p是指针变量,所以指针是常量;

指针函数 VS 函数指针

int *fun(int x, int y)       指针函数,返回值是指针
int (* fun)(int x, int y)    函数指针,函数是指针

void指针类型赋值和被赋值

......留白

变参应用

可变参数的应用,普遍的例子的是printf(const char *fmt, ...)

其内部实现大概是(from linux man page)

#include <stdio.h>
#include <stdarg.h>

void
foo(char *fmt, ...)
{
    va_list ap;
    int d;
    char c, *s;

    va_start(ap, fmt);
    while (*fmt)
        switch (*fmt++) {
        case 's':              /* string */
            s = va_arg(ap, char *);
            printf("string %s\n", s);
            break;
        case 'd':              /* int */
            d = va_arg(ap, int);
            printf("int %d\n", d);
            break;
        case 'c':              /* char */
            /* need a cast here since va_arg only
               takes fully promoted types */
            c = (char) va_arg(ap, int);
            printf("char %c\n", c);
            break;
        }
    va_end(ap);
}

其中的核心是现实va_star, va_arg, va_end, 三个宏,三个宏都要用到va_list ap变量。

va_start(ap, fmt)-- 初始化ap变量

va_arg(ap,tpye) -- 从ap中获取下一个参数

va_end(ap) -- 每一次va_start的调用,都必须对应调用一次va_end,来释放ap变量。

stdarg.h还提供了va_copy(va_list dst, va_list src),用于va_list之间的拷贝。

可变参数的另外的应用在与面向对象编程的的new()函数

new(Set);
new(String, "abcd");
⚠️ **GitHub.com Fallback** ⚠️