Something About Expert C Programming

  1. 字符串的自动合并程序员

int main( )
{
	char *Str[] = {"abc" "def"};
	printf( "%s\n", Str[0] );
	system("pause");
	return 0;
}

输出结果:abcdef编程

ANSI C规定相邻的两个字符串合并成一个字符串,必须注意在两个字符串之间添加“ ,”!但多余的“ ,”却并无什么影响!数组


2.sizeof 的操做数ide

int main( )
{
	int b = 2;
	int *p = &b;
	int a = 3 *sizeof  *p;
	printf( "%d\n", a );
	system( "pause" );
	return 0;
}

输出结果:12函数

在上述程序中sizeof后面的操做数是(*p)因此是计算了 p指向的数据的数据类型的长度。性能

此处要注意运算符的优先级问题spa

tips: .高于* []高于* ()高于*  ==和!=高于位操做和赋值 算数高于移位 逗号的优先级最低指针


3.结合性ip

    1)全部赋值运算都具备右结合性  内存

int main( )
	{
		int a, b = 1, c = 2;
		a = b = c;
		printf( "%d\n", a );
		system( "pause" );
		return 0;
	}


    输出结果:2

    c赋值给b ,b赋值给c。


4.空格的误用

int main( )
{
	char a[] = "Hi,I am a\ 
		long string!";
	printf( "%d\n", a );
	system( "pause" );
	return 0;
}

此处在转义符的后面添加了空格,语法错误编译不能经过。这样的错误很难被发现,尽可能避免!


5.最大一口策略

int main( )
{
	int a = 0;
	int b = 0;
	b = a+++3;
	printf( "%d,%d\n", b ,a);
	system( "pause" );
	return 0;
}

输出结果:3,1

结合性是a++ + 3

int main( )
{
	int *a = NULL;
	int *b = NULL;
	int c=*b/ *a;
	printf( "%d,%d\n", b ,a);
	system( "pause" );
	return 0;
}

在“/”和“*”之间必须有空格,否则编译器将后面的内容自动解析称为注释!


6.优先级规则分析案列

char *const *(*next)();

解释为:next是一个指向函数的指针,该函数的返回值是char *const


7.内存泄漏

内存泄漏:顾名思义,内存泄漏是因为咱们使用了内存却没有收回形成的,

 这样的问题十分容易下降咱们计算机的性能,而且泄漏的内存

 每每大于你忘记释放的数据的大小,由于malloc分配的内存一般

 会圆整为下一个大于申请数量的2的整数次方。

*同时,咱们必需要避免内存损坏,即释放、改写仍在使用的内存。


8.段错误

1).解引用非法值的指针引发

	//引发一个段错误
	int main()
	{
		int *p=0;
		*p = 7; 
		system( "pause" );
		return 0;
	}

2).若是未初始化的指针刚好具备未对齐的值,会引发总线错误而不是段错误。

3).解引用一个空指针一样引发段错误(经常因为系统返回一个空指针而程序员不加判断直接使用形成)

4).用完了堆栈或者堆空间

引发段错误常见编程问题:1.坏指针值错误:在指针赋值以前就用它来引用内存或在释放以后仍然使用该指针

2.改写错误:越界写入数据

3.指针释放:屡次释放或者释放一块不属于你的内存

注意:链表中元素的释放会常常引发此问题,咱们必须用一个临时变量来保存下一次的地址才可以进行释放

9.类型转换

a.类型提高

        printf("%d\n",sizeof'A');

此程序的输出结果为4,在此char型自动被提高为int型。事实上计算机将char和short提高为整型,将float提高成double型

整型提高规则:每一个变量的值被提高为int型的长度,而后对这些值进行计算,最后再对结果进行裁剪。

若是两个char型的加法运算结果没有溢出,那么在实际执行时只须要产生char类型的运算结果,能够省略提高。

	float f1,f2;
	double d;
	....
	f1 = f2*d;

若是编译器能够肯定用float运算的结果和用double运算的结果相同,那么编译器也可使用float类型运算。


C语言中的类型提高

char-->int 位段-->int 

enum-->int unsigned 

char-->int short-->int 

unsigned short-->int

float-->int 

任何数组-->相应类型的指针

float-->int 任何数组-->相应类型的指针

***参数也会进行提高,可是若是使用了函数原型,那么缺省的提高将不会发生!


10.有限状态机

有限状态机用于有限数量子程序的发展变化每一个程序处理并选择下一个应该进入的状态

例如:注释转换

基本思路:用一张表来保存所可能的状态,并列出进入每一个状态以后须要执行的动做,通常的最后一个动做用于决定你将要进入哪一个状态。

咱们能够经过函数指针数组来调用函数

	   extern int a() ,b(),c(),d();
	   int (*State[])()={a,b,c,d};
	   //能够经过调用数组中的指针来调用函数
	   (*State[i])();

固然,switch也是一个不错的实现机制


11.数组和指针的规则

a.“表达式中的数组名”至关于指针

b.C语言把数组的下标做为指针的偏移量

c.做为函数参数的数组名至关于指针

 关于这一点咱们应该明白是出于对效率的考虑,相似的函数的返回值必定不能是一个数组,而是一个指向该数组的指针

Tips:为了保证一致性咱们的定义和声明的形式必须保持一致

请尝试一下代码:

char ga[] = "abcdefghi";
void my_array( char ca[10] )
{
	printf( "数组的地址是:%#x \n",&ca);
	printf( "数组第一个元素的地址:%#x\n",&(ca[0]) );
	printf( "数组第二个元素的地址是:%#x\n",&(ca[1]) );
	printf( "++数组名获得的地址是:%#x\n",&(ca[1]) );
}
void my_pointer( char *pa )
{
	printf( "指针的地址是:%#x\n",&pa);
	printf( "指针偏移0的地址是:%#x\n", &(pa[0]) );
	printf( "指针偏移1的地址是:%#x\n", &(pa[1]) );
	printf( "++指针的地址是:%#x\n", ++pa );
}
int main( )
{
	printf( "全局变量数组的地址为:%#x\n",&ga );
	printf( "全局变量数组ga[0]的地址为:%#x\n", &(ga[0]) );
	printf( "全局变量数组ga[1]的地址为:%#x\n", &(ga[1]) );
	my_array( ga );
	my_pointer( ga );
	system( "pause" );
	return 0;
}

在输出的结果里咱们发现:在函数里数组的地址和数组的第一个参数的地址不同!

**在C语言中,咱们没法向一个函数传递一个长度不肯定的多维数组,你必须提供给它除了最左边一维之外全部的长度

在数组做为参数时:

1.一维数组 —— 须要包含一个计数值来方便咱们检测是否越界访问

2.二维数组 —— 不能直接传递给函数,能够改为一个指向向量的指针数组来传参

        eg: char **myarray;

3.三维或更多维数组 —— 没法使用。必须将之分解成几个维度更小的数组。

相关文章
相关标签/搜索