宏
1、可变参数宏...和__VA_ARGS_
__VA_ARGS_是一个可变参数的宏,是新的c99规范中新增的,目前似乎只有gcc支持(VC6.0不支持)
宏定义中参数列表的最后一个参数为省略号(也就是3个点),这样预定义宏__VA_ARGS_就可以用在替换部分中,替换省略号所代表的字符串。
比如define PR(...) printf(__VA_ARGS_)
int main()
{
int wt=1,sp=2;
printf("hello\n");
printf("weight=%d, shipping=%d\n", wt, sp);
return 0;
}
输出结果:
hello
weight=1, shipping=2
注意省略号只能代替最后面的宏参数,#define W(x, ..., y)错误!
#define debug(…) printf(__VA_ARGS__)
缺省号代表一个可以变化的参数表。使用保留名 __VA_ARGS__ 把参数传递给宏。当宏的调用展开时,实际的参数就传递给 printf()了。
2、可变参数va_start, va_arg, va_end
3、预处理运算符"#"
在类函数宏的替换部分,#用作一个预处理运算符,将语言符号转化成字符串,该过程为字符串化。
#include <stdio.h>
#define PSQR(x) printf("the square of" #x "is %d.\n", (x)*(x))
int main()
{
int y=4;
PSQR(y);
PSQR(2+4);
return 0;
}
输出结果为:
the square of y is 16.
the square of 2+4 is 36.
4、预处理运算符"##"
##可用作于类函数宏的替换部分,也可用作类对象宏的替换部分。这个运算符把两个语言符号组合成一个语言符号。
#include <stdio.h>
#define XNAME(n) x##n
#define PXN(n) printf("x"#n" =%d.\n", x##n)
int main()
{
int XNAME(1) = 12; //int x1 = 12;
PXN(1); //printf("x1 =%d.\n", x1);
return 0;
}
输出结果为:
x1 =12.
5、FD_ZERO, FD_SET, FD_CLR, FD_ISSET
FD_ZERO(fd_set *fdset);将指定的文件描述符集清空,在对文件描述符集合进行设置前,必须对其进行初始化,如果不清空,由于在系统分配内存空间后,通常并不作清空处理,所以结果是不可知的。
FD_SET(fd_set *fdset);用于在文件描述符集合中增加一个新的文件描述符。
FD_CLR(fd_set *fdset);用于在文件描述符集合中删除一个文件描述符。
FD_ISSET(int fd,fd_set *fdset);用于测试指定的文件描述符是否在该集合中。
6、container_of
offsetof用来判断结构体中成员的偏移位置,需要头文件stddef.h
#define offsetof(type, member) (size_t)&(((type *)0)->member)
巧妙之处在于将地址0强制转换为type类型的指针,从而定位到member在结构体中的偏移位置,编译器认为0是一个有效的地址,从而认为0是type指针的起始地址。
typeof是GNU对C新增的一个扩展关键字,用于获取一个对象的类型,很多时候我们处理的对象是一个指针类型,如果想知道指针所指向的对象的类型,就派上用场了。
container用来根据成员的地址来获得结构体的地址,需要头文件linux/kernel.h
#define container_of(ptr, type, member)
({ const typeof( ((type *)0)->member ) *__mptr = (ptr);
(type *)( (char *)__mptr - offsetof(type, member) );
})
container_of宏分两部分,
第一部分:const typeof( ((type *)0)->member) *__mptr = (ptr);
通过定义一个member指针类型的指针变量__mptr,并将ptr赋值给__mptr
第二部分:(type *)( (char *)__mptr - offsetof(type, member) )
通过offset宏计算出member在type中的偏移,然后用member的实际地址__mptr减去偏移,得到type的起始地址,即指向type类型
7、#if 0
8、宏定义
C++和C中命名变量时不能以数字开头,定义宏时也不能,否则编译结果显示预处理时出错,显示信息为error: macro names must be identifiers。
像10_MODE这样的定义是错误的,应该改成MODE_10
9、#ifdef与#if defined()区别
区别在于后者可以组成复杂的预编译条件,比如:
#if defined(AAA) & defined(BBB)
xxxxx
#elif defined(CCC)
xxxxx
#endif
而#ifdef就不能用上面的用法,只能判断单个宏是否定义。
10、likely与unlikely
在if else语句中根据预判使用likely与unlikely宏
#define likely __builtin_expect((x), 1)
#define unlikely __builtin_expect((x), 0)
11、offsetof
#include <stddef.h>
offsetof(structName, memberName);
举例:
struct af_alg_iv {
__u32 ivlen;
__u8 iv[0];
};
offsetof(struct af_alg_iv, iv); 计算得到iv的偏移量为4
12、BUILD_BUG_ON_ZERO和BUILD_BUG_ON_NULL
在内核文件 include/linux/bug.h中
/* Force a compilation error if condition is true, but also produce a
result (of value 0 and type size_t), so the expression can be used
e.g. in a structure initializer (or where-ever else comma expressions
aren't permitted). */
#define BUILD_BUG_ON_ZERO(e) (sizeof(struct { int:-!!(e); }))
#define BUILD_BUG_ON_NULL(e) ((void *)sizeof(struct { int:-!!(e); }))
BUILD_BUG_ON_ZERO(e) 表示的就是若表达式e结果为0,则编译通过,该宏的值也为0;若表达式e的结果不为0,则编译不通过。
BUILD_BUG_ON_NULL(e)用来在编译时断言e是否为NULL,若是,这个宏返回(void *)0 (即NULL,与第一个宏的区别);不为NULL时编译出错。
定义
1、unsigned int和int
2、typedef
typedef自定义函数指针类型
#include <stdio.h>
typedef int (*fp_t)(char c);
int f0(char c) { printf("f0, c = %c\n", c); return 0;}
int f1(char c) { printf("f1, c = %c\n", c); return 1;}
int main()
{
int ret;
fp_t fp;
fp = f0;
ret = fp('a');
fp = f1;
ret = fp('x');
return 0;
}
运行结果:
f0, c=a
f1, c=x
typedef自定义函数类型
#include <stdio.h>
typedef int fp_t(char c);
int f0(char c) { printf("f0, c = %c\n", c); return 0;}
int f1(char c) { printf("f1, c = %c\n", c); return 1;}
int main()
{
int ret;
fp_t* fp;
fp = f0;
ret = fp('a');
fp = f1;
ret = fp('x');
return 0;
}
运行结果:
f0, c=a
f1, c=x
另外说明一下:函数名是一个指针,fun *t = funA
定义一个函数指针类型 函数名
有typedef和没有typedef的区别
void (*Fun)(void);
定义了一个返回类型为void,参数为void的函数指针变量Fun,这个Fun变量是一个指针,指向函数在内存中的首地址
而typedef void (*Fun)(void);
typedef的功能是定义一个新类型,所以这里Fun就不是变量了,而是一个新的类型。可以用Fun来定义一个变量,Fun p;
3、volatile
需要申明为volatile的变量
硬件地址
多线程中共享的全局变量
中断服务程序和进程(线程)共享的全局变量
数据类型
在一个32位系统上
type | bits | values |
| 8 | -127 to 127 |
unsigned char | 8 | 0 to 255 |
| | |
short | 16 | -32767 to 32767 |
unsigned short | 16 | 0 to 65535 |
| | |
int | 32 | -2,147,483,647 to 2,147,483,647 |
unsigned int | 32 | 0 to 4,294,967,295 |
| | |
long | 32 | -2,147,483,647 to 2,147,483,647 |
unsigned long | 32 | 0 to 4,294,967,295 |
| | |
long long | 64 | -9,223,372,036,854,775,807 to 9,223,372,036,854,775,807 |
unsigned long long | 64 | 0 to 18,446,744,073,709,551,615 |
__int128为128 bit长度的类型
gcc中long int一般是32 bit,long long int一般是64 bit,但是随着PC硬件的不同和gcc的不同实现可能会有不同
函数
1、access
int access(const char *filename, int amode);
amode参数为0时表示检查文件的存在性,如果文件存在,返回0,不存在,返回-1。
该函数还可以检查文件的其他属性。
06检查读写权限
04检查读权限
02检查写权限
01检查执行权限
00检查文件存在性
2、atexit
int atexit(void (*func)(void));
#include<stdlib.h>
注册终止函数,即main函数结束后调用的函数。
一个进程可登记多达32个函数,这些函数将由exit自动调用。
atexit注册的函数类型应为不带任何参数的void函数,exit调用这些注册函数的顺序与注册他们的顺序相反。同一个函数如若登记多次,也会被调用多次。
实例:
#include <stdio.h>
#include <stdlib.h>
void exit_fn1(void)
{
printf("Exit function #1 called\n");
}
void exit_fn2(void)
{
printf("Exit function #2 called")
}
int main()
{
atexit(exit_fn1);
atexit(exit_fn2);
return 0;
}
输出结果:
Exit function #2 called
Exit function #1 called
3、strdup
char *strdup(char *str)
strdup拷贝一个字符串的副本并返回,这个副本有自己的内存空间,和str不相干。
使用完一定要释放这个函数中动态申请的内存。
#include <stdio.h>
#include <string.h>
#include <alloc.h>
int main()
{
char *dup_str, *string = "abcde";
dup_str = strdup(string);
printf("%s\n", dup_str);
free(dup_str);
return 0;
}
4、strchr
char *strchr(char * __str, int __ch)
头文件:#include<string.h>
功能:查找字符串str中首次出现ch的位置,并返回首次出现ch的位置的指针。
5、fcntl
fcntl(fd, F_SETFD, FD_CLOEXEC);
fcntl设置FD_CLOEXEC标志,意味着close on exec, not fork
如果对描述符设置了FD_CLOEXEC,使用execl执行的程序里,此描述符被关闭,不能在使用它,但是在使用fork调用的子进程中,此描述符并不关闭,仍可以使用。
6、assert
7、回调函数
8、函数指针与指针函数
9、system
system只是把命令执行完了,返回值是执行完就是ok,被中断就是fail,而不是命令执行的结果
其他
1、错误处理方法
2、segment fault
3、\t 制表符
分为水平制表符\t和垂直制表符\v(不常用)
水平制表符占8列,如果字符个数不足8个,则制表符用空格补充为8个输出;如果字符个数正好8个,则制表符另外补充8个空格输出。
4、malloc与内存池
malloc是分配虚拟地址空间,如果不memset或者bzero,那么就不会分配实际的物理页面。
内存池的作用看名字也能猜到,"池"意味着资源是统一管理和创建释放的,就像数据库的连接池、系统的线程池。主要就是为了避免创建、销毁资源的代价。c标准的malloc/free会造成大量的内存碎片以至于影响效率,所以“内存池”的技术某种程度上避免了这种消耗和影响。
5、NULL,'\0',0在数值上都是0
6. !!
两个!是为了把非0值转换成1,而0值还是0。
在C语言中,所以非0值都表示真。所以!非0值 = 0,而!0 = 1。
所以!!非0值 = 1,而!!0 = 0。
7. 结构体向前声明
如果结构体定义如下:
typedef struct tag_test
{
ULONGLONG utime;
ULONGLONG umac;
}test;
你在申明的时候就必须申明为:
typedef struct tag_test test;
如果结构体定义为:
struct test
{
ULONGLONG utime;
ULONGLONG umac;
};
就需要这样声明:
struct test;
标签:总结,知识点,int,void,C语言,fp,__,printf,type From: https://blog.51cto.com/u_15854579/5897269