Something About Expert C Programming

原创 2016年05月30日 13:35:04
  1. 字符串的自动合并

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

输出结果:abcdef

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


2.sizeof 的操作数

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指向的数据的数据类型的长度。

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

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


3.结合性

    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.三维或更多维数组 —— 无法使用。必须将之分解成几个维度更小的数组。


本文出自 “Zimomo” 博客,请务必保留此出处http://zimomo.blog.51cto.com/10799874/1737231

版权声明:本文为博主原创文章,未经博主允许不得转载。 举报

相关文章推荐

Something about C

What the hell? How long since I touch with C? What a pity, I have to work with it now.Pointer To ...

Something about C pointers and arrays

#include #include void display(int*, int size); void display2DArray(int (*)[5], int nrow); ...

我是如何成为一名python大咖的?

人生苦短,都说必须python,那么我分享下我是如何从小白成为Python资深开发者的吧。2014年我大学刚毕业..

Expert C Programming 总结 (六)

这是第九章的内容了。哈哈。看得比较慢拉。但是还是得看下去哈。这本书实在是写得太好了。       1. 声明本身还可以进一步分成三种情况:     (1)外部数组的声明。     (2)数组的...

Expert C Programming总结(二)

1. “声明的形式和使用的形式相似”这种用法可能是C的独创。至今为止一个比较好的声明指针的方法是: int &p;  它至少提示p 是一个整形数的地址。这种语法现已被C++采纳,用于表示参数的传址调用...

Expert C Programming 总结(四)

1. 80386在80286的基础上增加了两种新的地址模式:32位的保护模式和虚拟的8086模式。    2. 今天,计算机系统结构的真正挑战不在于内存的容量,而是内存的速度。 ...

Expert C Programming 总结(五)

1. 标准C语言具有八进制,十进制和十六进制常量,但是没有二进制常量。  2. 警惕! 真正值得注意之处———参数也会被提升!另一个会发生隐式类型转换的地方就是参数传递。这就是为什么单个...

《Expert C Programming 》学习笔记

一、P19      将 char ** 类型 传给 const char **        1 foo(const char **p){ }  2  3 void main(int argc, c...

From《Expert C Programming》01

1、Macrouse is best confined to naming literal constants, and providing shorthand for afew well-chose...

《Expert C Programming》总结(一)

1.  关键字const并不能把变量变成常量! 在一个符号前加上const限定符只是表示这个符号不能被赋值。也就是它的值对于这个符号来说是只读的,但它并不能防止通过程序的内部(甚至是外部)的方法来修改...
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)