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
#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; }
int setjmp(jmp_buf env):函数会保存当前执行环境,初次调用时会返回0值;在调用链中如果遇到void long_jmp(jmp_buf env, int value)时会返回该处,并取得value值。
与goto相比,longjmp可以实现函数间的跳转,goto只能在函数内部跳转。
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中函数的嵌套定义可以实现类似闭包的效果,只是如果函数是定义在一个循环中时,如:
for (; i>arg-2; i--) { int squ() { return i*i; } arr[arg-i]=squ; }
在最后使用arr函数数组时始终是指向最后定义的一个squ函数。查看2次循环时,发现squ始终指向同一个地址,所以此处认为C在处理时会将前面定义的squ函数覆盖。
这个现象和Python中的返回函数中,引用了可变化变量的现象不同,Python中在循环中定义的函数是指向不同的地址的。
C 实现可变参数
C中可以借助va_list实现可变参数:
va_start:使用传入的可变参数的第一个变量初始化va_list
va_arg:获取当前可变参数,每次调用时会将指针向后移
va_end:结束
利用这个机制实现可变参数时,需要保证传入的参数可判断结束位置,即第一个参数用于控制结束,同时传入的参数必须得有这个参数作为结束。
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; }
注意: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、指针常量和常量指针
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;
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);
位域中宽度可自定,但要其变量类型范围内