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";
}
反斜杠之后不能有空格,反斜杠的下一行之前也不能有空格
单引号,双引号
Item | Comment |
---|---|
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);
}
}