C001-C语言-语法与用法摘录-(ques=2)

本文摘录一些平时遇到的、需要留意的、C语言方面的语法和用法。

但不收录那些很少用到的不重要的地方,除非我觉得它有意思。


-------------------------------------------------------------------------------------------------------------------------------------

参考资料:

1、《C语言深度解剖》  2016.06

2、《让你不再害怕指针》  

3、


-------------------------------------------------------------------------------------------------------------------------------------

-4、


-------------------------------------------------------------------------------------------------------------------------------------

-3、全局变量限制只出现在一个文件中,提供专门的读写函数给外部文件操作这些变量。


-------------------------------------------------------------------------------------------------------------------------------------

-2、.注释要写"为什么这样写"、说明思路和作用

        .不用写"如何实现"、除非真的不易读,如公式推导而来的代码。


-------------------------------------------------------------------------------------------------------------------------------------

-1、./**/ 会被编译器用空格 " " 替换,所以 in/**/t i = 0; 会被编译器报错。

      ."\" 换行符/继续符后面不能有空格 " ",否则 "\" 会被编译器视为是转义符+空格 " " 而被编译器报错。

       同时要注意换行后、第二行可能会习惯性的先打空格,这些空格会被连接到第一行后面。

       如:#def\
                   ine
       会被连接成:#def    ine


-------------------------------------------------------------------------------------------------------------------------------------

0、stu.name 中的 "." 可以认为是从 stu 中取出指定的偏移 name


-------------------------------------------------------------------------------------------------------------------------------------

1、bool 变量与 0 的比较

定义:      BOOL  b_flag = FALSE;
最好的写法:if(b_flag);                if(!b_flag);
可接受写法:if(b_flag == 1);           if(b_flag = 0);
有风险写法:if(b_flag == TRUE);        if(b_flag == FALSE);

解释:FASE的定义一般都是0,TRUE的定义一般是1或-1。

            也就是说FALSETRUE都是人为定义的,可能不都是0和1,尤其是TRUE


            另外、有的时候我们需要使用 不为0即是真 来作为判定条件。


-------------------------------------------------------------------------------------------------------------------------------------

2、浮点数之间的比较

浮点数之间的比较不能直接比较,要比较一个范围,因为浮点数存储时都是有误差的,不是原值。

DELTA用来指定比较的精度。


另外、差异很大的两个浮点数之间的运算,结果很可能是错的。


-------------------------------------------------------------------------------------------------------------------------------------

3、函数的参数不要多于4个

      否则CPU的处理效率会降低,参数多了可以用struct打包。

-------------------------------------------------------------------------------------------------------------------------------------

4、用typedef给类型重命名

(1).
    const int temp; 和 int const temp; 等价
(2).
    typedef  struct student 
    {
        int data;
        ...
    } stu_data, *stu_ptr;
    const  stu_data  temp; 和 stu_data  const  temp;  等价
    const  stu_ptr p_temp; 和 stu_ptr  const  p_temp; 等价

原因在于 typedef 定义的 stu_data stu_ptr 都是一个类型,对编译器来说、他们和 (1) 例中的 int 这个类型在地位上平等

因此编译器处理 stu_data  const  temp; stu_ptr  const  p_temp; 时,与处理 int const temp; 时的过程一致。

虽然在 stu_ptr 的定义中有指针符号 * ,但它不是独立的,而是和 struct student 绑定在一起的。

stu_ptr  const  p_temp;  和 struct  student const * temp;是不一样的:前者是 stu_ptr  const  类型的指针,后者是 struct  student 类型的const指针。


另外,虽然 stu_ptr 是一个类型,但 unsigned stu_ptr 却不能被编译器识别,因为编译器... ? 

|<----待解释-question-001


-------------------------------------------------------------------------------------------------------------------------------------
5、#define 常量表达式时、留意常量的范围
     
      #define 常量表达式时、留意常量的范围, 数值较大时要用UL标记该宏为长整型。 ---- but why, if not .......
     GCC下要写为(356*24*60*60UL)UL放在括号外会被编译器报错。
      

-------------------------------------------------------------------------------------------------------------------------------------
6、#error之error-information 不用加双引号
      
      这里的 error-information 不用加双引号,加了就是字符串而已,报 error 时连双引号一起显示输出

     
      
      message来让编译器条件输出信息、辅助调试

     
      使用 #x x 和字符串区分开来,可以被 #define 的宏展开


-------------------------------------------------------------------------------------------------------------------------------------
7、数组a 的名字代表数组首元素a[0]的首地址,而不是整个数组的首地址
如: int a[5];
数组名a 作为一个地址使用时,代表的是 a[0] 的首地址,因而 *(a + 1) == a[1],因为 (a + 1)是a 的地址加了一个元素大小的单位,是 sizeof(a[0]) = 4 字节。
如果数组名a 是整个数组的首地址,而 *(a + 1) 中的 (a + 1) 是a 的地址加了整个数组大小的单位,是 sizeof(a) = 20 字节,因此 *(a + 1)  = a[5],而不是 a[1]
但实际上, *(a + 1) == a[1] 才是对的,所以数组名a 是 a[0] 的首地址。

另外,a是变量名, 在这里是这个数组的名字,和 int data; int *p = &data; 中的 data一样、是整个变量的名字,所以 &a 是取出整个数组的首地址。
&a 是整个数组a 的首地址,是相对于整个数组而言的,也就是说 &a 这个地址值的类型是 int[5],它移动一个单位的大小是 sizeof(a),因而 (&a + 1)是加了 sizeof(a) = 20 字节。
所以 *(&a + 1) = a[5]

所以、 int (*ptr)[5] = &a 是对的,而 int (*ptr)[5] = a 是不对的,因为 &a 的类型才是 int [5] ,而a 的类型是 int

另外, fun(a) fun(a[5]) 传递到函数的实参 是 a 的首元素 a[0] 的首地址 的备份。

-------------------------------------------------------------------------------------------------------------------------------------
8、函数返回的局部指针是野指针
函数内的局部变量是系统在栈空间分配的。
函数返回后,这个分配的区域被释放,这些地址就是用户程序不能访问的区域。

|<----待验证=question-002

-------------------------------------------------------------------------------------------------------------------------------------
9、malloc(0) 返回不可用的地址
      malloc(0) 在堆上申请空间A时,申请成功后会返回一个地址而不是 NULL,但这个地址是无法使用的。
      结果是: if(NULL != pf) 这样的检查不起作用了。

     free(pf) 只是取消 pf 对空间A 的 使用权,而 pf的值和空间A 中的内容都没变。

-------------------------------------------------------------------------------------------------------------------------------------
10、定义一个返回数组指针的函数
        由 typedef 定义函数指针就可以知道怎么定义对应的函数了
        定义一个返回数组指针的函数,给出两种定义:
定义一:
typedef int(*pf_array_2(void))[2];  // 对应指针
int(*fun_return_pArray2(void))[2]
{
    int  temp[2] = {1, 2};
    int (*p)[2] = &temp;
    return p;
}
定义二:
typedef int(*p_array_2)[2];  // 定义返回值
p_array_2 fun_return_pArray2(void)
{
    int  temp[2] = {1, 2};
    int (*p)[2] = &temp;
    return p;
}
对应的外部申明:
typedef int(*p_array_2)[2];
extern p_array_2 fun_return_pArray2(void);

-------------------------------------------------------------------------------------------------------------------------------------
11、结构体变量初始化的理想通用方法
以前看过Python可以定义函数的默认参数,觉得挺方便。
今天想找类似方式给结构体变量初始化,找到了这篇文章,解决了我的需求:
现摘抄如下:
#include 

struct student_st
{
	char c;
	int score;
	const char *name;
};

static void show_student(struct student_st *stu)
{
	printf("c = %c, score = %d, name = %s\n", stu->c, stu->score, stu->name);
}

int main(void)
{
	// method 1: 按照成员声明的顺序初始化
	struct student_st s1 = {'A', 91, "Alan"};
	show_student(&s1);

	// method 2: 指定初始化,成员顺序可以不定,Linux 内核多采用此方式
	struct student_st s2 = 
	{
		.name = "YunYun",
		.c = 'B',
		.score = 92,
	};
	show_student(&s2);

	// method 3: 指定初始化,成员顺序可以不定
	struct student_st s3 = 
	{
		c: 'C',
		score: 93,
		name: "Wood",
	};
	show_student(&s3);
	
	return 0;
}
如果想初始化结构体数组,可采用 {{ }, { }, { }} 方式,如
struct student_st stus[2] = 
{
	{
		.c = 'D',
		.score = 94,
		/*也可以只初始化部分成员*/
	},
	{
		.c = 'D',
		.score = 94,
		.name = "Xxx"
	},
};

-------------------------------------------------------------------------------------------------------------------------------------

12、位域的类型

      位域的大小不仅和对齐方式有关,还和位域变量的类型有关
    struct student {
        unsigned char a1:4;
        unsigned int  b2:4;
        unsigned long c3:4,
                      c4:4;
        unsigned long long d4:4,
                           d5:4;
    }stu_charlotte;
    unsigned long      size_long;
    unsigned long long size_long_long;
    
    cout << "sizeof(size_long) = "      << sizeof(size_long)        << endl;
    cout << "sizeof(size_long_long) = " << sizeof(size_long_long)   << endl;
    cout << "sizeof(stu_charlotte) = "  << sizeof(stu_charlotte)    << endl;
      结果为:
                    sizeof(size_long) = 4
                    sizeof(size_long_long) = 8
                    sizeof(stu_charlotte) = 16
      也就是说:
                        1、d4、d5相当于是在一个8byte空间里面分配出的两个4bit区。
                        2、c3、c4 相当于是在一个4byte空间里面分配出的两个4bit区。

      而增加一个unsigned int变量后、由于对齐原因、结果将增大到24byte:
    struct student {
        unsigned char a1:4;
        unsigned int  b2:4;
        unsigned long c3:4,
                      c4:4;
        unsigned long long d4:4,
                           d5:4;
        unsigned int  number;
    }stu_charlotte;
    unsigned long      size_long;
    unsigned long long size_long_long;
    
    cout << "sizeof(size_long) = "      << sizeof(size_long)        << endl;
    cout << "sizeof(size_long_long) = " << sizeof(size_long_long)   << endl;
    cout << "sizeof(stu_charlotte) = "  << sizeof(stu_charlotte)    << endl;
      结果为:
                    sizeof(size_long) = 4
                    sizeof(size_long_long) = 8
                    sizeof(stu_charlotte) = 24
      原有的16byte、加上4byte的number,结果为20byte。
      而对齐模数为8,所以空间会变成24byte,以整除对齐模数8。

位域对齐参看: 《A004-数据对齐的原因》
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值