C 开发学习记录

时间:2021-09-05 03:28:40

C基本类型

C基本类型有:

  char:8位,可添加修改符signed或是unsigned

  short:16位,同有singed和unsigned

  int:32位,同有singed和unsigned

       long:在32位系统为32位,64位系统为64位,分为signed和unsigned

  long long:64位,分为signed和unsigned

  float:32位,精确度为6

  double:64位,精确度为15

  long double:80位,精确度为19

C中没有bool类型,可以引入stdbool.h文件使用定义的bool,true, false宏

 常用字面值使用:

  int in = 1234;

  long ln = 1234L;

  unsigned int uin = 1234U;

  unsigned long uln = 1234UL;

  float  f = -12.34e-1f;

  double df = 123.45;

  long double ldf = 123.45L;

 

C GOTO使用示例

GOTO虽然会破坏程序的结构,使用代码可读性变差,但是GOTO依然还是有可用的地方

#include <stdio.h>
#include <stdbool.h>

int main()
{
  bool a = false;

step1:
  if (a)
    goto step2;
  while (true)
  {
    printf("while out\n");
    while (true)
    {
      printf("while inner\n");
      a = true;
      goto step1;
    }
  }

step2:
  return 0;
}

 

C setjmp和longjmp

C 开发学习记录
#include <stdio.h>
#include <setjmp.h>

void test(jmp_buf *env)
{
    printf("setjmp test\n");
    longjmp(env, 1);
}

int main()
{
    jmp_buf env;

    int ret = setjmp(env);

    if (!ret)
        test(&env);
    else
        printf("longjmp... %d\n", ret);

    return 0;
}
C 开发学习记录

int setjmp(jmp_buf env):函数会保存当前执行环境,初次调用时会返回0值;在调用链中如果遇到void long_jmp(jmp_buf env, int value)时会返回该处,并取得value值。

与goto相比,longjmp可以实现函数间的跳转,goto只能在函数内部跳转。

 

C 返回函数与闭包的考虑

C 开发学习记录
#include <stdio.h>

typedef int (*fun)();

fun closure(int i)
{
    int squ()
    {
        return i*i;
    }
    return squ;
}

int main()
{

    fun f = closure(2);

    printf("closure %d\n", f());

    return 0;
}
C 开发学习记录

C中函数的嵌套定义可以实现类似闭包的效果,只是如果函数是定义在一个循环中时,如:

C 开发学习记录
    for (; i>arg-2; i--)
    {
        int squ()
        {
            return i*i;
        }
        arr[arg-i]=squ;
    }
C 开发学习记录

在最后使用arr函数数组时始终是指向最后定义的一个squ函数。查看2次循环时,发现squ始终指向同一个地址,所以此处认为C在处理时会将前面定义的squ函数覆盖。

这个现象和Python中的返回函数中,引用了可变化变量的现象不同,Python中在循环中定义的函数是指向不同的地址的。

 

C 实现可变参数

C中可以借助va_list实现可变参数:

va_start:使用传入的可变参数的第一个变量初始化va_list

va_arg:获取当前可变参数,每次调用时会将指针向后移

va_end:结束

利用这个机制实现可变参数时,需要保证传入的参数可判断结束位置,即第一个参数用于控制结束,同时传入的参数必须得有这个参数作为结束。

C可变参数的实现原理基于函数传参数的的栈,参数从右往左入????,通过第一个参数可以确定参数栈的位置,然后利用数据类型,配合结束符,可以将所有参数取出来。

C 开发学习记录
#include <stdio.h>
#include <stdarg.h>
#include <string.h>

void testchar(const char *var, ...)
{
    va_list args;
    va_start(args, var);
    char *value;
    while(strcmp((value=va_arg(args, char*)), var))
    {
        printf("%s\t", value);
    }
    va_end(args);
    printf("\n");
}

void testint(int count,...)
{
    va_list args;
    va_start(args, count);
    for (int i=0; i<count; i++)
    {
        printf("%d\t", va_arg(args, int));
    }
    printf("\n");
    va_end(args);
}

int main(void)
{
    testchar("eof", "abc", "123", "eof");
    testint(3, 1, 2, 3);
    return 0;
}
C 开发学习记录

注意:va_start和va_end必须同时存在。

 

C 数组使用时注意点

数组注意点:

  数组为静态时,初始化长度必为常量表达式

  数组长度由最后一个元素决定其长度,可以指定特定元素,如:

    int arr[] = {0, 1, [5]=5, 6};

  没有提供初始值的元素赋值为0或NULL

  数组作为参数传递时,被转化为指向第一个元素的指针,而不再是数组,无法使用sizeof取得长度,但是如果传入的是二维数组时,形参必需指定二维数组长度,如对函数

    void test(int arr[][5])

  来说,直接sizeof(arr)时是指针的长度,但是sizeof(arr[0])时是数组的元素,内层数组的长

 

C 指针的几个注意点

1、静态指针在初始化时必须使用编译时可以确定地址表达式完成赋值,如

    static int a;
    static int* pa = &a;//初始化时必须使用可以确定地址的表达式
    int b;
    static int *pb;
    pb = &b;//此处属于运行时,使用没有问题

2、指针常用的一些运算:

  a 相等或不等判断是否指向同一个变量

  b 指针加上整数i指向第i个元素

  c 指针间相减判断相隔几个元素

3、指针常量和常量指针

C 开发学习记录
    int a=1;
    int b=2;
    int * const p2= &a;
    //p2 = &b; //p2是一个指针常量,无法改变他的值
    int const *p1 = &a;
    p1 = &b;
    //*p1=2; //p1是常量指针,可以改变指针的值,但是无法改变指向的元素的值,指向的元素可以不为常量

    const int* p3; //p3同p1,个人认为这种写法更有利理解,推荐这种
    p3 = &a;
    p3 = &b;
    //*p3 = 3;
C 开发学习记录

 4、int (*p) []:数组指针,指向数组的一个指针

       int * p[]:指针数组,成员是指针的一个数组

理解这个要记住[]的优先级比*高,在不加括号的前提下,(p[])优先结合,表示是一个数组

 

C 结构体零散知识点

1、结构体无法把自己作为成员,但可以包含指向自己的指针成员

2、定义不完整结构类型(类型,结构,占用空间无法确定,如void指针,声明没有指明数组长度数组)只能使用小标签,即struct struct_name定义其成员

3、匿名结构体时赋值时可使用 . 代表当前变量,指定特定成员值

    struct{int a; int b;} stu = {.a=1, .b=2};
    printf("%d\n", stu.a);//输出1
    stu.a = 123;
    printf("%d\n", stu.a);//输出123

如果结构体里再包含结构体,如下所示:

    struct{int a; int b; struct{int c; int d}in;} stu = {.a=1, .b=2, .in.d=5};
    printf("%d\n", stu.in.d);

此外结构体定义变量间分隔使用的是 ;

4、弹性结构成员,在结构体尾部声明一个未指定长度的数组成员,这种结构体一般以指针形式使用,在申请空间后,将指针指向其,并指定数组长度,为数组赋值,在对该结构体sizeof取值时,数组不计入,该结构体不可初始化

 

C 其他一些

1、联合,语法

  union{成员1;成员2;}

联合间的成员共享同一个空间,也就是说,成员1赋值如果没有被清除,会体现在成员2上

2、位字段,结合结构体瓜分完整的类型存储空间,如

    struct{
        int y:22;
        int mon:4;
        int day:5;
        char m:5;
        char s:3;
    } f = {1,2,3,4,3};

    printf("%d, %d, %d, %d, %d", f.y, f.mon, f.day, f.m, f.s);

位域中宽度可自定,但要其变量类型范围内