C语言深度解剖 (三)


原文链接: C语言深度解剖 (三)

前言

C语言的水深不见底,好在一些前辈们已经将很多雷区探了一遍

这里分享一下我在学习 《C语言深度解剖》 过程中的一些笔记和心得


概要

  • TOC
    {:toc}


typedef

typedef 从字面上理解,是类型定义的意思,但其实应该是 typerename 的意思,给一个已经存在的数据类型取一个别名,而非定义一个新的数据类型

void main()
{
	typedef struct student
	{
		int age;
		char sex;
	} Stu_st,*Stu_pst; 
	
	struct student stu1={23,'f'};
	Stu_st stu2={24,'m'}; //与 stu1 的定义形式等价
	struct student *stu3=&stu1;
	Stu_pst stu4=&stu2; //与 stu3 的定义形式等价
}

虽然看起来很奇怪,但是只要将 struct student { ... }struct student { ... } * 看作成一个整体,对整体进行命名就容易理解了

typedef 与 const

void main()
{
	typedef struct student
	{
		int age;
		char sex;
	} *Stu_pst; 
	struct student stutmp1={23,'f'},stutmp2={24,'m'};
	const Stu_pst stu1=&stutmp1; //const 在前的定义 , 参考之前的const使用方法,如果将 Stu_pst 与 typedef struct student { ... } * 进行简单替换,理论上const 修饰的是 *stu1 从而限定的是 stu1 指的对象,即stu1指的对象不能改变,stu1本身应该可以改变值  
	Stu_pst const stu2=&stutmp2; //const 在后的定义 , 参考之前的const使用方法,如果将 Stu_pst 与 typedef struct student { ... } * 进行简单替换,理论上const 修饰的是 stu2 从而限定的是 stu2 本身,即stu2本身不能改变,stu2指的对象应该可以改变值
	stu1->age=12; 
	stu1->sex='m'; //结果stu1所指的对象可以接受修改
	stu2->age=13;
	stu2->sex='f'; 
	//stu1=&stutmp2; //error C2166: l-value specifies const object //stu1 本身不能修改,stu1所指的对象反而可以接受修改,说明了const修饰的其实是指针变量本身,与stu2的一样
	//stu2=&stutmp1; //error C2166: l-value specifies const object 
}

这个实验结果与我们之前的理解有些出入,原因是 typedef struct student { ... } * 被编译器当作了一个整体,解释的过程中,Stu_pst 是一个类型名,被忽略掉,从而直接修饰了指针本身

typedef 与 #define

#define INT32 int
unsigned INT32 i=10;
typedef int int32;
//unsigned int32 k=10; //error C2061: syntax error : identifier 'k'
				//error C2059: syntax error : ';'
				//error C2513: '/*global*/ ' : no variable declared before '='
typedef static int intx; //error C2159: more than one storage class specified
int32 j=10;
intx l=11;

void main()
{

}

出错的信息代表,typedef 取的别名不支持类型扩展

#define PCHAR char*
PCHAR p1,p2;  // p1为指针,但是p2为字符类型

typedef char* pchar;
pchar p3,p4;  //p3,p4都是指针

void main()
{
	int i=sizeof(p1); //4
	int j=sizeof(p2); //1
	int k=sizeof(p3); //4
	int l=sizeof(p4); //4
}

由于预处理仅仅是简单的替换,所以p2为字符的情况很容易被忽略

加上数组与指针的情况

有以下几种声明

#define a int[10]
typedef int a[10];
#define a int*[10]
typedef int * a[10];
#define *a int[10]
typedef int (*a)[10];
#define *a * int[10]
typedef int* (*a)[10];

和以下几种定义,它们之间相互结合,哪些可以正常使用

a[10] a[10];
a[10] a;
int a[10];
int a;
a b[10];
a b;
a* b[10];
a* b;

以上的实例,实话说我目前还没完全理清,先记录下来,慢慢研究,准备专门开一篇来详细探究


注释

C语言里有两中注释方式:/**///

/*这是*/#/*一条*/define/*合法的*/ID/*预处理*/replacement/*指*/list/*令*/

// 从这里开始 /*这是/*非法*/的*/   因为/*总是与离它最近的*/匹配

void main()
{
	int/*there is a comment here*/i=10;
	char* s="abcdefgh //hijklmn";
	//Is it a \
	valid comment?  //warning C4010: single-line comment contains line-continuation character
	in/*this is a comment*/t j; //error C2065: 'in' : undeclared identifier
								//error C2146: syntax error : missing ';' before identifier 't'
								//error C2065: 't' : undeclared identifier
								//error C2146: syntax error : missing ';' before identifier 'j'
								//error C2065: 'j' : undeclared identifier
}

注释里可以包含换行符,但是编译过程中会产生警告

编译器会将注释剔除,但不是简单的剔除,而是用空格代替原来的注释

/*总是与离它最近的 */ 匹配

void main()
{
	int x=5,y=0,a=4,*p;
	p=&a;
	//y=x/*p; //fatal error C1071: unexpected end of file found in comment 
	y=x/ *p;
	y=x/(*p);
}

只要 /* 之间没有空格,都会被当作注释的开始,要进行合理规避

注释代码段时,应强调 为何做why 而不是 怎么做how


接续符

\ 表示断行,编译器会将反斜杠剔除掉,跟在反斜杠后面的字符自动接续到前一行

#def\
ine MAC\
RO xyz

void main()cl
{
//这是一条合法的\
单行注释  //warning C4010: single-line comment contains line-continuation character

cha\
r* s="comment te\
st";
}

反斜杠之后不能有空格,反斜杠的下一行之前也不能有空格


单引号,双引号

ItemComment
1整型常数,32位系统下占4个byte
'1'字符常量,占1个byte
"1"字符串常量,占2个byte

逻辑运算

#include <stdio.h>

void main()
{
	int i=0;
	int j=0;
	if((++i>0)||(++j>0))
	{
			printf("%d  %d\n",i,j); // 1 0
	}
}

逻辑或,在遇到第一个表达式为真的情况下,会短路掉第二个表达式的计算,同样逻辑与遇到第一个表达式为假的时候也一样


位运算

a^=b; b^=a; a^=b; 可以实现不用第三个临时变量来交换值

void main()
{
	int i = 0x01 << 2 + 3; // 32 , 算术运算优先级高
	int j = 0x01 << 2 + 32; // 0 , 溢出
	int k = 0x01 << 2 - 3; // 0 
	int l = 0x01 >> 2 - 3; // 0 不能为负数
	int m = 0x01 << 2; // 4
}

++,--

#include <stdio.h>

void main()
{
	int i = 3; 
	int j = (++i) +(++i) +(++i); // i:6 j:16 非常奇怪的结果
	int k = 0;
	int l = (k++,k++,k++); // k:3  l:2 取第三个表达式的值
	int x =0;
	int y =(++x,x++,x+10); //x:2 y:12
	for(i=0,printf("First=%d",i);i<10,printf("Second=%d",i);i++,printf("Third=%d",i)) //会死循环下去,因为第二个表达式取值为printf的返回结果,永远为真
	{
		printf("Fourth=%d",i);
	}
}
`