一:数据类型
●枚举类型 (整形数据的集合)
声明举例: enum DAY{ mon=1 , tue , wen , thu , tue , fri , sat ,
sun };
enum DAY today , tomorrow ;
或者 enum DAY{ mon =1, tue , wen , thu , tue , fri , sat ,
sun }today , tomorrow ;
或者 enum { mon =1, tue , wen , thu , tue , fri , sat ,
sun }today , tomorrow ;
或者 typedef enum DAY{ mon =1, tue , wen , thu , tue , fri ,
sat , sun } Day;
Day today,tomorrow;
或者 typedef enum { mon =1, tue , wen , thu , tue , fri ,
sat , sun } Day;
Day today,tomorrow;
·枚举类型的默认初始化规则 ,第一个变量为 0 ;后一个比前一个大 1.
·枚举值是常量,不是变量。不能在程序中用赋值语句再对它赋值。
·枚举的使用 比如: today = mon; tomorrow = tue;
·疑问:1 若只对枚举进行部分初始化,其余值的默认初始化是怎么样的?
2 如果是自动型的枚举,其初始化是怎么样的?
3 对于上述的today 或者 tomorrow 没有初始化赋值,其默认值是
什么样的呢,如果是自动变量,结果又是如何?
·解惑:1 枚举的初始化可以看做两部分,一是枚举内部的符号常量的初始
化。始终是符合如下规律的“若第一个符号常量没有被赋值初始
化,那么它被默认初始化为0; 若非第一个符号常量未被赋值初
始化,那么它的默认初始化值始终比它左边的一个符号常量大
1”. 而对于 像 today 或者 tomorrow 这样的枚举变量,如果没
有赋值初始化。若是全局变量,那么它被初始化为0;局部变量,
被初始化为随机。
2:对于局部变量,那么枚举内部的初始化规则不会变,但是枚举变
量本身,就会被赋予垃圾(随机数)
3:见1和2的回答
●字符串常量。 记住无论什么时候看到字符串常量,那么它的直接值就是一个
地址。不过有一种情况,表面上看上去是一个字符串常量,但是它的直接值不是一个地址,那就是在字符数组的初始化中。比如 char a[10]={"hello!"}; 不过要记住,这只是表面看上去像,实际上它是 char a[10]={'h','e','l','l','o','!'}的另外一种简化表述而已,不应该被看做是字符串常量。
·疑问:当对一个字符数组进行如下初始化之后 char a[10]={'h','e','l','l','o','!'}
或者 char a[10]={"hello!"}; 其'!'字符之后有没有'\0'结束符呢?
·解惑:
对于数组,首先要明白一点,如果部分初始化了,那么其余部分会被编译器初始化为0,也就是全部都是'\0'符。而对于没有一点初始化过的数组,如果是全局变量,那么它所有的项都会被初始化为0 。 但如果是局部的变量,那么它是不会被初始化的!!另外对于全部被程序员初始化满的数组,那么在数组后面得内存是不会被自动赋0的。但是如果是全局变量,一般来说它的后面很可能会是没有动过的内存,也就是0.但对于局部变量,如果它全部被初始化满了,那么它后面的内存很可能是非0的。另外,如果虽然是局部变量,但被声明为静态的,那么它的性质就和全局变量是一样的!!
●变量的作用域。
四种作用域:文件作用域,函数作用域,代码块作用域和函数原型作用域。
任何在所有代码块外面声明的变量具有文件作用域。主要掌握文件作用域和代码块作用域 即可。
●链接属性
一共有三种链接属性:external internal none
None :总是被当做单独的个体,也就是说该标识符的多个声明被当做独立不同的实体。Internal :在同一个源文件内部指的是同一个实体,在不同的源文件之中指的是不同的实体。external :在所有的不同源文件中均指同一个实体。
具有文件作用域的标识符默认为external链接属性,可以在这些标识符前面加上static关键字,使其链接属性变更为internal;另外,对于一个函数,一般来说它也具有文件作用域。
其它作用域的标识符默认具有none标识符,在标识符加上extern关键字,使其链接属性变更为external。
从技术上说,static 和 external 只有在声明中才是必须的,然而,如果你在一个地方定义变量,并在使用这个变量的其他源文件的声明中添加external 关键字,可以使读者更容易理解你的意图。
二: C语言 语句
●switch 语句经常搞错,其标准格式为:
cwitch (x){
case x: xxx ; break;
.
.
.
case x: xxx ; break;
default : xxx ;
}
其中大括号千万不要忘记。对于c语言中switch的工作机制,要有一定的了解。当程序执行到switch体内部时,程序会试图寻找符合期望的 x 值,当找到之后,就会执行其后面的语句,并且对于该 case 之后的语句,会不加判断一并执行!所以对于一般的switch语句,case 后面的相关执行命令之后要加break,跳出。
另外,附加对break的说明,一般break适用于跳出循环,比如while 循环和 for 循环,另外对于switch虽称不上循环,但也可以跳出。所以一般情况下使用break也就只有这三个地方。
● printf格式大全
printf的格式控制的完整格式:
% - 0 m.n l或h 格式字符
下面对组成格式说明的各项加以说明:
①%:表示格式说明的起始符号,不可缺少。
②-:有-表示左对齐输出,如省略表示右对齐输出。
③0:有0表示指定空位填0,如省略表示指定空位不填。
④m.n:m指域宽,即对应的输出项在输出设备上所占的字符数。N指精度。用于说明输出的实型数的小数位数。为指定n时,隐含的精度为n=6位。
⑤l或h:l对整型指long型,对实型指double型。h用于将整型的格式字符修正为short型。
---------------------------------------
格式字符
格式字符用以指定输出项的数据类型和输出格式。
①d格式:用来输出十进制整数。有以下几种用法:
%d:按整型数据的实际长度输出。
%md:m为指定的输出字段的宽度。如果数据的位数小于m,则左端补以空格,若大于m,则按实际位数输出。
%ld:输出长整型数据。
②o格式:以无符号八进制形式输出整数。对长整型可以用"%lo"格式输出。同样也可以指定字段宽度用“%mo”格式输出。
例:
main()
{ int a = -1;
printf("%d, %o", a, a);
}
运行结果:-1,177777
程序解析:-1在内存单元中(以补码形式存放)为(1111111111111111)2,转换为八进制数为(177777)8。
③x格式:以无符号十六进制形式输出整数。对长整型可以用"%lx"格式输出。同样也可以指定字段宽度用"%mx"格式输出。
④u格式:以无符号十进制形式输出整数。对长整型可以用"%lu"格式输出。同样也可以指定字段宽度用“%mu”格式输出。
⑤c格式:输出一个字符。
⑥s格式:用来输出一个串。有几中用法
%s:例如:printf("%s", "CHINA")输出"CHINA"字符串(不包括双引号)。
%ms:输出的字符串占m列,如字符串本身长度大于m,则突破获m的限制,将字符串全部输出。若串长小于m,则左补空格。
%-ms:如果串长小于m,则在m列范围内,字符串向左靠,右补空格。
%m.ns:输出占m列,但只取字符串中左端n个字符。这n个字符输出在m列的右侧,左补空格。
%-m.ns:其中m、n含义同上,n个字符输出在m列范围的左侧,右补空格。如果n>m,则自动取n值,即保证n个字符正常输出。
⑦f格式:用来输出实数(包括单、双精度),以小数形式输出。有以下几种用法:
%f:不指定宽度,整数部分全部输出并输出6位小数。
%m.nf:输出共占m列,其中有n位小数,如数值宽度小于m左端补空格。
%-m.nf:输出共占n列,其中有n位小数,如数值宽度小于m右端补空格。
⑧e格式:以指数形式输出实数。可用以下形式:
%e:数字部分(又称尾数)输出6位小数,指数部分占5位或4位。
%m.ne和%-m.ne:m、n和”-”字符含义与前相同。此处n指数据的数字部分的小数位数,m表示整个输出数据所占的宽度。
⑨g格式:自动选f格式或e格式中较短的一种输出,且不输出无意义的零。
三、C语言 操作符
● C语言操作符优先级表:
优先级 |
运算符 |
名称或含义 |
使用形式 |
结合方向 |
说明 |
1 |
[] |
数组下标 |
数组名[常量表达式] |
左到右 |
|
() |
圆括号 |
(表达式)/函数名(形参表) |
|||
. |
成员选择(对象) |
对象.成员名 |
|||
-> |
成员选择(指针) |
对象指针->成员名 |
|||
2 |
- |
负号运算符 |
-表达式 |
右到左 |
单目运算符 |
(类型) |
强制类型转换 |
(数据类型)表达式 |
|||
++ |
自增运算符 |
++变量名/变量名++ |
单目运算符 |
||
-- |
自减运算符 |
--变量名/变量名-- |
单目运算符 |
||
* |
取值运算符 |
*指针变量 |
单目运算符 |
||
& |
取地址运算符 |
&变量名 |
单目运算符 |
||
! |
逻辑非运算符 |
!表达式 |
单目运算符 |
||
~ |
按位取反运算符 |
~表达式 |
单目运算符 |
||
sizeof |
长度运算符 |
sizeof(表达式) |
|||
3 |
/ |
除 |
表达式/表达式 |
左到右 |
双目运算符 |
* |
乘 |
表达式*表达式 |
双目运算符 |
||
% |
余数(取模) |
整型表达式/整型表达式 |
双目运算符 |
||
4 |
+ |
加 |
表达式+表达式 |
左到右 |
双目运算符 |
- |
减 |
表达式-表达式 |
双目运算符 |
||
5 |
<< |
左移 |
变量<<表达式 |
左到右 |
双目运算符 |
>> |
右移 |
变量>>表达式 |
双目运算符 |
||
6 |
> |
大于 |
表达式>表达式 |
左到右 |
双目运算符 |
>= |
大于等于 |
表达式>=表达式 |
双目运算符 |
||
< |
小于 |
表达式<表达式 |
双目运算符 |
||
<= |
小于等于 |
表达式<=表达式 |
双目运算符 |
||
7 |
== |
等于 |
表达式==表达式 |
左到右 |
双目运算符 |
!= |
不等于 |
表达式!= 表达式 |
双目运算符 |
||
8 |
& |
按位与 |
表达式&表达式 |
左到右 |
双目运算符 |
9 |
^ |
按位异或 |
表达式^表达式 |
左到右 |
双目运算符 |
10 |
| |
按位或 |
表达式|表达式 |
左到右 |
双目运算符 |
11 |
&& |
逻辑与 |
表达式&&表达式 |
左到右 |
双目运算符 |
12 |
|| |
逻辑或 |
表达式||表达式 |
左到右 |
双目运算符 |
13 |
?: |
条件运算符 |
表达式1? 表达式2: 表达式3 |
右到左 |
三目运算符 |
14 |
= |
赋值运算符 |
变量=表达式 |
右到左 |
|
/= |
除后赋值 |
变量/=表达式 |
|||
*= |
乘后赋值 |
变量*=表达式 |
|||
%= |
取模后赋值 |
变量%=表达式 |
|||
+= |
加后赋值 |
变量+=表达式 |
|||
-= |
减后赋值 |
变量-=表达式 |
|||
<<= |
左移后赋值 |
变量<<=表达式 |
|||
>>= |
右移后赋值 |
变量>>=表达式 |
|||
&= |
按位与后赋值 |
变量&=表达式 |
|||
^= |
按位异或后赋值 |
变量^=表达式 |
|||
|= |
按位或后赋值 |
变量|=表达式 |
|||
15 |
, |
逗号运算符 |
表达式,表达式,… |
左到右 |
从左向右顺序运算 |
●复杂表达式的求值顺序有三个因素决定,首先是操作符的优先级,操作符的结合性以及操作符是否控制执行的顺序。相邻操作符的执行顺序由它们的优先级决定,如果它们的优先级相同,那么由它们的操作符结合性决定。除此之外,编译器可以自由决定使用任何顺序进行求值,只要不违背 逗号 , && , || ,?: 所施加的顺序即可。
●表达式的存在不一定会被求值。例如在sizeof(a=b+c)里面,表达式a=b+c是不会被求值的。另外由于 && , || ,?:的短路效应,也有可能造成表达式不被求值。
●对于逗号表达式,其求值顺序是从左到右,并且以最右边那个表达式的值作为整个逗号表达式的值。而对于等号表达式,其除了能产生赋值这一行为外,其整个表达式也是有值的,它的值就是左值被赋予的值。
●对于移位操作符:存在两种移位,逻辑移位和算术移位。逻辑左移和算术左移是一样的。唯有有符号数的逻辑右移和算术右移才会不一样。对于有符号数的算术右移,若最高位(符号位)为0,那么与逻辑右移是一样的,若最高位是1,那么算术右移时,高位补1.编译器对有符号数的右移是逻辑右移还是算术右移是取决于编译器的。
●sizeof
假设对于一个结构 sruct abc,那么sizeof(abc)是没有意义的,正确的应该是
sizeof(struct abc)
2.1.4 指针
●对所有的指针进行显示的初始化是见好事情。
●对于 ++*P 的计算顺序要烂熟于心,由于++ 和 * 的优先级相同,所以根据它们的结合性来进行判断。因为结合性自右向左,所以先进行 * 操作 ,再进行 ++ 操作。
●指针在和一个整数进行加或减操作时时钟会根据合适的大小进行调整。
●指针可以进行的三种运算 :指针 + 整数 , 指针 - 指针 和 指针间的比较。对于后两种,两个指针必须指向同一个数组。两个指针相减的结果是返回两个指针间的单位长度,而两个之争间的比较也很容易理解。
●对于数组名基本上的时候都等同于指针,它和指针之间可以相互转换,对于数组a[x] 和*(a+x)是等价的。对于指针 * (p+x) 也可以用p[x]来表示。
五:C语言 函数
●函数定义
●函数声明
函数声明的作用:当编译器遇到一个函数调用时,它产生代码传递参数并调用这个函数,而且接收这个函数的返回值(如果有的话)。但编译器是怎么知道该函数期望接收的是什类型的参数和多少数量的参数呢?如何知道该函数的返回值(如果有的话)类型呢?这样就是函数声明的作用了。
如果没有函数声明,编译器会在调用这个函数时,假定所接受的参数是正确的,并同时假定一个整形的返回值。对于那些返回值并非整形的函数而言,这种隐式的认定常常导致错误。不过在VC++6.0上面貌似没有声明是不行的。
函数声明的两种方法,一是在使用前就有了该函数的定义,通常是在main函数之前。另一种是用函数原型来进行声明。
●可变参数函数
包含的头文件是<stdarg.h>,需要用到三个宏:va_list , va_start , va_arg ,va_end ;其中va_list 是一种类型。
一个求平均值的例子
#include <stdarg.h>
float average(num,...)
{
int count=0;
float sum=0;
va_list var_arg;
va_start(var_arg , num);
for(count=1;count<=num;count++)
sum += va_arg(var_arg , int);
va_end(var_arg)
return (sum = sum / count);
}
六:C语言 数组
●对于数组 a[i] 那么a[i] 和 *(a+i)完全等价。
●对于数组 a[i] . *a++ 绝对是非法的。 因为a虽然是一个指针,但是这是一个常量指针,其值是不可能改变的 。 *a++这一表达式做了两件事,一是把拷贝出一份a值,用于*操作,这一步没有错,然而,另一步,这是a的自加1,这一步是不允许的。不过,当一个数组名作为一个函数的形参时,是可以对这个数组名实行 *a++操作的,原因在于作为形参,实际上市拷贝了一个数组名来使用而已,该数组名便具有了变量的性质。所以是可以这样做的。这一结论可以从srcpy的实现中得到验证。
●c语言所有函数的参数都是一份拷贝而已,包括数组名和指针。那为什么传数组或指针可以改变原来的值呢。这是因为得到了一份指针,那么根据指针的内容访问内存,并修改相应的内存是我完全可以的。但要是想改变指针本身的内容,即指针所指向的位置是不可能的。
● int const *p :指向只读整形变量的指针
Int * const p:指向整形的常量指针
● int *p[10] ; 根据优先级,先是数组,后是指针。所以它是指针数组
Int (*p)[10] ; 先是指针,后是数组,所以是指向数组的的指针。和一个二维数组等价。
●对于二维或者多为数组,把*转化为 [ ] 是一个明智之举。
七:字符串及字符串操作函数
●字符串长度测量函数:size_t strlen(char const *string);注意到size_t 是无符号整形的,而一个无符号整形永远是大于等于0的,所以如下判断是错误的:
if(strlen(x)-strlen(y)>=0)
……
正确的应该是:if((int)strlen(x)-(int)strlen(y)>=0)
……
●字符串拷贝函数:char *strcpy(char *dest , char const* src); 注意dest 和 src 不能重叠。
●字符串拼接函数 :char *strcat(char *dest , char const* src); 注意dest 和 src 不能重叠。
●词典比较: int strcmp(char const *s1 , char const *s2);
返回 0 :s1 = s2;
返回 正数 : s1 > s2;
返回 负数 : s1 < s2;
带有字符个数限制的字符串操作函数:
●字符串拷贝函数 :char *strncpy(char *dest , char const* src,size_t lenth); 注意该函数不会保证NUL字符的拷贝。如下例子是比较稳妥的strncpy操作函数:
char buffer[SIZE];
……
strncpy(buffer , name , SIZE);
buffer[SIZE-1] = '\n';
●字符串拼接函数 :char *strcat(char *dest , char const* src, size_t lenth);
●词典比较: int strcmp(char const *s1 , char const *s2 , size_t lenth);
●查找一个字符函数: char *strchr(char const *str , int ch);返回第一个该字符的指针;另外一个与之相对应得函数时 char *strrchr(char const *str , int ch);返回最后一个匹配字符的指针。
●查找任何几个字符。char *strpbrk(char const *str , char const *group);
●查找一个子字符串:char *strstr(char const *s1 , char const *s2);
●查找第一个“是”/ “不是”位置 size_t strspn(cahr const *str , cahr const *group); size_t strcspn(cahr const *str , cahr const *group); 注意返回值,第一个位置是0.
●分解字符串char *(char *s , char *delim);使用方法如下:
char *p = strtok(s1 , “;”);
P = strtok(NULL , “;”);