个人原创整理的C语言各方面的琐碎知识点。如果这些基础你都不知道,你还好意思说自己精通C吗?
错误!printf是标准输入输出库的部分
%3.0f表示至少3个字符的宽度,不显示小数点和小数部分
%.0f表示无小数和小数点部分
‘A’就是符号常量
1.函数要使用外部变量可以用extern
显示声明,如:
int max;
int main()
{
extern int max;
return 0;
}
2.如果外部变量在函数之前,函数使用该外部变量可以不使用extern
3.外部变量在file1定义,在file2,file3使用需要extern
显示声明。用于建立之间的联系。
short
实际上是short int
,一般简写为short
long(int)
signed
和unsigned
可以用于char和任何整型
long double
是高精度浮点数
1234
是int常量(数值太大int无法表示的则为long常量)
123456789L
,包含了L
orl
的是 long常量
u or U
的后缀是 无符号常量
小数形式如3.12
或者1e-2
形式的,是浮点常量:后缀f or F
为float常量;什么都没有则为double常量;有l or L
则是long double
常量
\013
是八进制,代表制表符
\xb
十六进制,也表示制表符
又称为字符字面值
“hello”或者“”(空字符串)都是字符串常量
字符串常量中可以使用转义字符
“hello” “world”可以用于链接字符串
\a 响铃
\n 换行
\f 换页(例如回车25行)
\b 回退
\r 回车
\t 横向制表符:跳到下一个制表符的位置(一个tab8位)
\v 纵向制表符:跳转到下一行,\v前一个字符的下一列
\030 八进制
\xb 十六进制
enum color{ green = 0, red, black};
注意点:
1. 名字必须不同
2. 不同名可以同值
优点:
1. 值可以自动生成
2. 编译器会检查enum变量的值是否是其有效值
3. 可以以符号形式打印enum变量的值
const
能整除400 或者 能整除4却不能整除100 的年份是闰年
((n % 400) == 0) || ( (n % 4) == 0 && (n % 100) == 0)
double
(定义在math.h中) signed
还是unsigned
char
、float
,也把函数的声明写成int
或者double
void squeeze(char *s, char c)
{
int i, j;
for(i = j = 0; s[i] != '\0'; i++)
{
if(s[i] != c)
{
s[j++] = s[i];
}
}
s[j] = '\0';
}
void strcat(char *s, char *t)
{
int i, j;
i = j = 0;
while(s[i] != '\0')
i++;
while((s[i++] = t[j++]) != '\0')
;
}
算术移位右移补符号位
算术移位左移,逻辑移位左右移均补0
x *= y + 1
等效于x = x * (y + 1)
x *= y + 1
的类型是什么,值是什么类型是左侧操作数的类型
值是赋值完成后的数值
紧凑
所有的对函数参数的副作用都必须在调用函数之前完成
if(n > 0)
{
int i;//这里的i与if外面的i没有任何关系
}
break, return
逗号运算符是从左到右的顺序计算的。
但是如:for(int i = 0, j = 10; i < j; i++)
中的逗号, 不是逗号运算符,是平等关系
函数定义和变量定义
参数、返回值、外部变量
不允许在函数内部定义函数,因为函数本身是外部的
对同一名字的外部变量的多次引用实际上是引用同一个变量
作用域更广,生命周期更长
外部变量只能在定义时初始化
extern int array[];
static用于:
外部变量: 其他文件不可见,不会与其他文件同名变量冲突
函数:仅在所在文件可见
内部变量:仅能在所在函数中使用,但一直占据空间
实际机器上
1. 只有较少变量可以放入寄存器
2. 只有部分特定类型的变量才能存入
在没有显式的情况下,初始化为0
初始化表达式必须是常量表达式,且只初始化一次
在没有显式的情况下,初始化为垃圾值
初始化表达式可以不是常量表达式:表达式中可以包含任意之前定义的值,包括函数调用。
*数组初始化可以跟初始化表达式列表:int a[10] = {1, 3, 7, 2, 9};
* 如果初始化列表的值比需要的少,对于自动变量,静态变量,外部变量没有初始化元素值为0
有两种方法:
char string[40] = "hello feather"
char string[40] = {'h','e','l','l','o',' ','f','e','a','t','h','e','r','\0'};//这里需要加上结束标志\0
根据相应规则在库目录里查找
"my.h"
先在源文件所在位置查找该文件,如果没找到,则根据相应规则在库目录里查找
例如:
#define YES hello
printf("YES");//不会用hello 替换YES
比如max(a, b)是函数,对于a,b不同的数据类型会有不同的max版本
而使用宏
#define max(A, B) ((A)>(B)?(A):(B))
//记得宏调用中每个部分都要用上(),防止产生副作用
如:
#define dprintf(expr) printf(#expr "= %g\n", expr)`
/*调用*/
dprintf(x/y)
/*等效于*/
printf("x/y" " = %g\n", x/y);
/*因为“”有链接字符串功能,所以等效为*/
printf("x/y = %g\n", x/y);
如果替换文本中的参数与##相邻,则该参数将被实际参数替换,且##前后空白删除
#define paste(front, back) front##back
paste(name,1);
//结果将建立记号name1
常量整型表达式不能包含:
1. sizeof
2. 类型转换运算符
3. enum常量
是能够存在一个地址的一组存储单元(通常是2个或者4个字节)
不能用于表达式,常量和register变量
会先将ip的值加一,然后再取出其中的值。*和++这类一元运算符符合从右到左的结合顺序。
这和(*ip)++结果不一样。
指针是变量:可以pa++或者pa=a
但是对于数组a++和a=pa都是非法的
以栈的形式进行管理,此外afree的调用顺序必须和alloc相反!
malloc和free
没有这样的限制
0一般为NULL
void strcpy(char *s, char *t)
{
while( *s++ = *t++ )
;
}
void strcmp(char *s, char *t)
{
int i;
for(i = 0; s[i] == t[i]; i++)
{
if(s[i] == '\0')
return 0;
}
return s[i] - t[i];
}
请再次用指针形式来实现。
func(int a[3][10]);
func(int a[][10]);
func(int *a[10]);
char *name[]={"wang", "zhang"};
int a[10][20]; //a分配了200个int类型数据的大小
int *b[10]; //b仅仅分配了10个int指针的大小
int main(int argc, char * argv[]);
argv[argc]
必须为NULL
int (*comp)(void *, void *); //函数指针
int * comp(void *, void *); //函数
struct point{
int x;
int y;
};
声明变量:struct {...} x, y, z;
初始化: struct point maxpt = {320, 220};
//必须是常量
*p->str
*p->str++
*p++->str
(*p->str)++
sizeof a
sizeof(int)
#define NKEYS (sizeof keytab/ sizeof(struct key))
#define NKEYS (sizeof keytab/ sizeof keytab[0])
第二种方法最好,能够无视数组元素的类型
因为#define并不需要求值
struct node{
char name[MAXSIZE];
struct node * left; //仅仅使用了自身的指针,是可以的
};
但是如果使用了struct node tnode;
这是非法的,不能使用自身的实例
并没有创建新的类型,但是创建了新的类型名
Tree
比复杂的结构体指针要更容易理解 union type{
int a;
float b;
char *c;
}u;
作用:一个变量可以合法保存多种数据类型中的某一类型的对象
要求:需要能装下最大的成员变量,具体长度与实现有关。
提供了和宏一样操作位的方法
struct{
unsigned int is_keyword :1;
unsigned int is_extern :1;
unsigned int is_static :1;
}flags;
冒号后为字段的宽度(二进制)
特殊宽度0可以用来强制在下一个字边界上对齐
字段仅仅能申明为int
字段不是数组,并且没有地址。
执行s
中的命令,然后继续执行当前程序
sin(x),cos(x),exp(x),log(x),log10(x),pow(x,y),sqrt(x),fabs(x)
使用rand()
生成随机数,但是这是伪随机数
使用srand(unsigned int)
用于装载随机数的种子。可以使用time(0)
获取当前时间来装载种子
下面是例子:
srand(time(0));
while(1)
{
printf("%d\n", rand());
}
getchar
的实现一次read
读入一组数据,然后每次调用getchar返回一个字符
int getchar(void)
{
static char buf[BUFSIZE];
static char *bufp = buf;
static int n = 0;
if( n == 0) //缓冲区为空
{
n = read(0, buf, sizeof buf);
bufp = buf;
}
return (--n >= 0) ? (unsigned char)*bufp++ : EOF;
}
_Bool 布尔,只有真假
_Complex 复数
_Imaginary 虚数
头文件 stdbool.h
使用bool
代替_Bool
输入12,13,14 将14给n,前面2个舍去
循环:更快且节省内存
递归:慢且消耗内存
代码如下:
void f(int n)
{
int r = n % 2;
if(n > 1)
f(n / 2);
printf("%d", r);
}
int arr[6] = {[5] = 223};
其余自动为0
int **pt;
int a[3][2];
int (*pa)[3];
如下赋值:
pt = a; //错误!一个是指针的指针,一个是数组的指针
*pt = a[0]; //可以,两个都是是int的指针
pa = a; //错误!,pa是3个int的数组指针,a是两个int的数组指针
int a = 4;
int b = 5;
int array[a][b]; //是可以的
int sum(int r, int c, int a[r][c])
/*普通数组声明*/
int a[2] = {10, 20};
/*复合文字*/
(int [2]){10, 20}; //or
(int []){30, 40};
通常使用指针来保存
int *p = (int [2]){10, 20};
h
atoi(s) ,以字符串为参数,返回相应整数
atol(s), 字符串转换为long
atof(s), 字符串转换为double
static
如int w(static int a);
错误!!!
应用于裸机开发,使变量尽可能保存在寄存器中
适合:CPU支持的数据类型,自动变量,函数形参,
不适合:全局变量,函数
声明为静态变量,在程序结束后才释放(保存在全局数据区,默认初始化为0)
能修饰变量和函数
1. 全局静态变量 :文件作用域(在当前文件可访问,其他文件不可见)具有静态存储时期
2. 局部静态变量: 代码块作用域, 具有静态存储时期
3. 静态函数:文件作用域,函数名对其他文件不可见
错误!!!!
声明为只读变量,而非常量,可以间接修改其值。
const int a = 10;
int * p = &a;
printf("%d\n", a);
*p = 11;
printf("%d\n", a);
是符号常量
优点: 提高程序的自注释性
缺点:不安全,不语法检查
适合简短、频繁的函数
优点:高效
不适合复杂的函数
不同名可以相同值
优点:
1. 自动生成常量值
2. 提高自注释性
3. 编译时进行语法检查
数组 | 指针 |
---|---|
数组名为指针常量,不可以自加 | 指针可以自加 |
数组给所有元素分配空间 | 指针仅仅分配指针的空间 |
直接访问 | 间接访问 |
要注意防止越界 | 要注意防止内存泄露 |
缺点:
1. 维护性差
2. 复用性差
3. 扩展性差
优点:
1. 适合底层开发
2. 能做界面,绘图等工具
3. 适合做服务器
4. 驱动开发
volatile是表示该变量容易改变。使用语法和const
一致
volatile int local;//local是易变的值
volatile int *p; //p指向易变的值
一般的程序会暂时使用寄存器中的值,有时候就会出错。volatile
修饰的值直接从变量地址获取值,保证数据的一致性和正确。
只能用于指针,表示指针是访问一个数据对象唯一且初始的方法
将一个位置的字节复制到另一个位置
void *memcpy(void *restrict des, void *restrict src, size_t n);
void *memmove(void *restrict des, void *restrict src, size_t n);
memcpy要求两个位置间不能重叠
memmove允许重叠(但是更危险)
* 确保没有重叠时,使用memmove
fpos_t 类型能处理较大数的定位函数(20亿字节以上)
int fsetpos(FILE * stream, const fpos_t * pos);
//*pos 使得文件指向该数值所示的位置
int fgetpos(FILE * stream, fpos_t * pos);
//Return:0 if OK, 值放于pos
struct book library = {.number = 2, .title = “The”, 10};
struct book read;
read = (struct book){22, “hello”, 6.9};
struct flex{
int count;
double scores[];
};
//如果要使得`scores`表示含5个double
struct flex * p = (struc flex *)malloc(sizeof(struct flex) + 5 * sizeof(double));
p->scores[2] = 10; //访问数组成员
函数说明符 inline
#include
inline void func(void)
{
;
}
int main()
{
func();
return 0;
}
void qsort(void *base, //要排序的数组的首地址(数组名)
size_t nmemb, //排序的数量
size_t size, //数组每个元素的大小
int(* compare)(const void *, const void * ) //自定义比较数组的元素
);
使用方法:
int double_compare(const void * p1, const void * p2)
{
double ac = 0.001;
const double * a = (const double * )p1;
const double * b = (const double * )p2;
if(fabs(*a - *b) < ac) return 0;
else if(*a - *b > ac) return 1;
else
return -1;
}
qsort(array, 10, sizeof(double), double_compare);
//能比较结构体
qsort(array, 10, sizeof(struct node), mycompare);