C 细节知识

extern关键字详解

链接地址
用于让其他文件访问某个变量
只有全局变量并且没有被static声明的变量才能声明为extern

在test.c文件中定义变量int global=0;
在头文件test.h中声明这个变量为extern int global;
要使用这个变量的其他文件,只要包含test.h就可以了。

extern和const

想在2.c 中引用1.c的定义
非const变量

 	//1.c
    int  bufsize;
    //2.c
    extern int bufsize;

const变量

	//1.c
    extern const int  bufsize=10;
    //2.c
    extern const int bufsize;

const变量如果希望其他文件能用,必须主动加上extern,因为
没有被static 和const 修饰变量自动会加上extern

extern其他细节

源文件定义了 char a[10];
另一个文件用 extern char *a;
这样使用会报错

extern 和 static

(1) extern 表明该变量在别的地方已经定义过了,在这里要使用那个变量.
(2) static 表示静态的变量,分配内存的时候, 存储在静态区,不存储在栈上面.

头文件能放的东西

1 #define
2typedef
3extern 变量
4函数声明
三个例外:1)值在编译时就已知的const变量的定义可放在头文件中,如:const int num=10;
2)类的定义可放在头文件中。
3)inline函数。

#ifndef <标识>
#define <标识>
、、、、
、、、、
#endif
可以保证在一个文件里只是定义一次。

<标识> 应该为 文件名大写并且前后加  '_' ,  句号也变成 '_'
例如 stdio.h     对应的标识 应该为 _STDIO_H_

声明和定义详细区分

定义和声明是不同的。定义只能出现一次,而声明可以出现多次。
extern int x;变量是声明,并未实际分配地址。
void print(); //函数声明,并未产生实际目标代码
extern int ival=10; //虽然ival声明为extern,但是它初始化了,代表这是个定义。
int x; int x=3; void print() { }; //均为定义。
只在头文件中做声明,真理总是这么简单

Linux内核编码规范

缩进: tab =8个空格
switch 中 case default相对与switch不用缩进

大括号:
  非函数的大括号 if switch while for do在一行的末尾写上{
  函数的大括号在一行的开始
  只有一个语句的if 不加大括号,else必须加
空格:
  这些关键字后面加一个空格if, switch, case, for, do, while
  二元和三元操作符两侧使用一个空格
变量名:
  全局变量的变量要尽可能有描述性(单词全用_分隔)
  局部变量的名字要尽可能简单
  最好基本别用typedef
函数:
  尽可能只做一件事,尽可能短(不超过两个屏幕),
  函数的本地变量应该少于(5-10)个
宏:
  能写成内联就不写成宏
  不写影响控制流的宏
  宏函数 避免依赖于一个固定名字的本地变量的宏#define FOO(val) bar(index, val)比如这里的index
  避免 宏函数作为左值
  为宏函数加括号
内联
  超过3行的函数不要内联  
返回值
  返回值类型应该统一  0成功  非0失败
   或正成功 负失败 ,不要混合使用

易混淆点

int *a[10];//a是数组,成员是指针
int (*a)[10];a是指针,指向数组,成员int

*a->b++ 等于 *(a-》b)++;

可变参数个数函数

#include <stdarg.h>  //包含头文件
int func(int a,int b, ...) //...表示可变参数 部分
{
	va_list tmp;  //由于可变参数部分没有变量名,这一步可以当成是声明一个 变量 名。
	va_start(tmp , b); // 第一个参数是上面的变量名,第二个参数 为可变参数的前一个参数名,相当于
						//为tmp 赋初值
	接收变量=va_arg(tmp,type) // type 是数据类型, char* 、int 、double  
			//va_arg()返回一个可变参数的值,然后指向下一参数
			//获取可变参数,就是不停调用va_arg();
			// 注意, va_arg()不会对越界进行提醒,所以,最好传参时就指定 变参个数
	va_end(tmp);// 最后表明使用结束		
	
	// 一共就4句
}

越界演示
demo:
在这里插入图片描述
结果:
在这里插入图片描述

字符串拼接

sprintf(buff ,“xxxx %s1 %s2”,s1,s2);

宏定义细节

消除预定义依赖

#undef 取消原有的预定义
然后再使用 #define

扩展:消除依赖
使用ide,高亮所有找到的非基础变量,每找到一个,解决依赖后再高亮,
这样方便检查

#define 中的#和##

一般用于宏函数中。

  • “#”
    将宏函数传递的变量原封不动的转换成字符串

例如:

#define FUNC(a)  #a

printf("%s",FUNC(“sos”))

执行的结果:
“sos”     //注意输出的结果中包含了" "(双引号)
#define FUNA(a) #a
#define FUNB(b,c) b ## c 

printf("%s",FUNA(FUNB(b,c)))

执行的结果:
FUNB(b,c)    

将宏的两个参数连接成一个字符串 (写这条主要是因为和下面的##区别)

#define FUNC(a,b)  #a#b

printf("%s",FUNC(mgf,fgm));

执行的结果:
mgffgm     
  • “##”
    将两个宏的参数连接在一起当成一个新的宏
#define FUNC(a,b) printf("%s",a ## b)

char *mgffgm="sos";
FUNC(mgf,fgm);

执行的结果:
sos     
#define FUNC(a,b) printf("%d",a ## b)

FUNC(12,13);

执行的结果:
1213     

对于没有调用的函数

可以对其只声明,不定义

结构体初始化, 及设置默认值

4种初始化结构体方法

struct init{
	int a;
	char b;
	double c;
	char *d; 
}

一、定义时初始化

struct init member={1, 'a', 3.14, "abc"};

二、逐个成员赋值

struct init member;
member.a = 1;
member.b = 'a';
member.c = 3.14;
member.d = "abc";

三、指定成员赋值(C风格)

struct init member ={
	.a = 1,
	.b = 'a',
	.c = 3.14,
	.d ="abc"
}

四、指定成员赋值(C++风格)

struct init member = {
	a : 1,
	b : 'a',
	c : 3.14,
	d : "abc"
}

指定默认值方法

struct init{
	int a;
	char b;
	double c;
	char *d; 
	init ()={
		.a = 1,
		.b = 'a',
		.c = 3.14,
		.d = "abc"
	}
}

注意,结构体定义时,成员间声明用 ‘;’ 隔开,而在赋值时用 ‘,’ 隔开。

结构体可以直接进行赋值操作


对于取成员操作符(->) 的理解

c库中有一个宏函数 offsetof(type, member-designator)
作用是找到结构体成员距离结构首地址的距离
实现为:

offsetof(s, m) (size_t)(&((s*)0)->m)

s 为结构体,m为成员,
含义为:将0,解释为地址为0的 结构体s,接着找到其成员m,然后取m的地址,然后转换成size_t类型
执行的结果就是m相对于结构s的偏移地址。
如果把上述的0改成1,得到的结果就为 m相对与s的偏移地址+1

顺带的,关于linux内核函数container_of(ptr,type,member)

#define container_of(ptr, type, member) ({              \         
const typeof( ((type *)0)->member ) *__mptr = (ptr);    \         
(type *)( (char *)__mptr - offsetof(type,member) );})

memset 只适合初始化成 0 或 -1

switch case 语句,在C中 case 中不能定义变量,C++可以

函数细节

strncmp(str1,str2,n); 如果n>str1的长度,会出现段错误
//避免方法:将常量放在str1的位置,n=strlen(str1)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值