总结下c语言中值得注意的地方:
1、逗号表达式。
因为实际开发中应用的不多,所以很多人对这个都容易忽视。
逗号表达式,比赋值运算符优先级还低,结合性为左结合。
例子
#include <stdio.h>
void main()
{
int a=2,b=3,c=4,x,resulta,resultb;
resulta=(x=a+b),(b+c);//resulta=5,x=5
//resulta后面的赋值运算符优先级比,高,所以resulta=5。
resultb=(resulta=(x=a+b),(b+c));//resultb=7,x=5,resulta=5。
//resultb 保存的是整个逗号表示式的值。
printf("resulta = %d,resultb = %d, x=%d/n",resulta,resultb,x);
}
2、关系运算符中的一例
int a=1,b=2,c=2,t;
while(a<b<c){t=a;a=b;b=t;c--}//关系运算符左结合 而不是右结合,可以循环两次
printf("/n%d,%d,%d",a,b,c);//输出1,2,0
3、函数(指针参数)
void settozero(int *pvar)
{
*pvar = 0;
}
//参数传递为指针就可以把原参数的值修改为0了。因为传递的指针是一个变量的地址。
看下面两个例子:
void interchange(int *u ,int *v)
{
int temp;
temp = *u;
*u = *v;
*v = temp;
}//可以实现交换*u与*v的值
void interchange(int *u ,int *v)
{
int *temp;
temp = u;
u = v;
v = temp;
}//不能实现值的交换。
注意函数参数实际传递的为实参的副本。如果参数为指针形式,则也不例外,只是传递的指针副本与实际的参数指向的地址空间一样,所以修改指针副本所指向的内存的内容也就算是修改了实参(指针)所指向的内存的内容。效果是一样的。
引申:其实scanf("%d",&num);就用到了这层含义。读取一个数值,然后将其存储到通过参数获得的地址中。
4、*p++ 等价于 *(p++) 而不是(*p)++;由于*与++的优先级别相同,但是结合性为从右至左。
5、函数中绝对不能返回局部变量的地址。
例子 1
long * IncomePlus(long *pPay)
{
long pay = 0;
pay = *pPay + 10000;
return &pay;
}//这样做就不对,因为return的时候pay这个局部变量已经不在IncomePlus函数的作用域内。
例子 2
char * str_in(void)
{
char buffer[BUFFER_LEN];
char *pString = NULL;
if(gets(buffer) == NULL)
{
return FALSE;
}
pString = (char *) malloc( strlen(buffer) +1 );
if (pString == NULL)
{
abort();
}
return strcpy(pString , buffer);
}//这样做就可以,但是需要注意内存释放,因为函数内部只是malloc了一块内存,这就需要调用函数来释放内存了。搞不好会内存泄露的。
6、注意union类型;其所占内存大小为sizeof(其成员)最大的一个。因为所有成员都共享一块内存。
7、函数指针的用法很灵活。
int (*pfun)(int ,int);
typedef int (*function_pointer) (int ,int); //声明一个函数指针类型
这样就可以function_pointer pfun1;
function_pointer pfun2; 假设有函数sum,difference,则可以
function_pointer pfun3 = sum;
function_pointer pfun3 = difference;
更有意思的是函数指针做参数:
int sum(int ,int );
int difference(int ,int );
int any_function(int (*pfun)(int ,int ),int x,int y);
void main()
{
int a = 10;
int b = 5;
int result = 0;
int (*pf)(int ,int ) = sum;
result = any_function(pf,a,b);
printf("reslut = %d/n",result);
}
int any_function(int (*pfun)(int, int),int x,int y)
{
return pfun(x,y);
}
int sum(int x,int y)
{
return x + y;
}
//仔细理解下 就会发现函数指针太灵活太好用太重要了。
还有函数指针数组 例子
int (*pfun[2]) (int ,int ) = { sum,difference };
8、注意long double的sizeof大小;很多书上介绍的都为10,但是我计算机上运行结果为8。可能这个与cpu有关,不同的机器运行结果会不同的。
9、结构中的位域
虽然用的比较少,但是还是值得注意
struct
{
unsigned int flag1:1;
unsigned int flag2:2;//占2bits,所以范围为0~3
unsigned int flag3:1;
}indic;
sizeof(indic) = ? //4
10、指向二维数组的指针常出错的地方
int a[4][3] ;
int *pa = a;//非法,虽然这样写大多时,运行结果也对。但是这样写是不合法的
int *pa = *a;//合法,这样两边就位于同一个层次了
int *pa = a[0];//合法
int *pa = &a[0][0];//合法
11、注意数组指针与指针数组不要搞混淆了
指针数组:就是指针的数组,数组的元素是指针,如 int *p[2];首先声明了一个数组,数组里的元素是int型的指针。
数组指针:即指向数组的指针,如 int (*p) [2];声明了一个指针,该指针指向一个有2个int型元素的数组。
其实这两种写法主要是因为运算符的优先级, 因为[ ]的优先级比*高。所以第一种写法,p先和[ ]结合,所以是一个数组,后与*结合,是指针。后一种写法同理。
指针数组如下处理就会很清楚:
typedef int* intPtr;
intPtr p[2];
一目了然,所以为了避免迷惑,做适当的typedef也是很有必要的。
同理,数组指针也可以作类似处理:
typedef int intArray2[2];
intArray2 * p;
和原来的声明都是等价的。