C tips

1. 在写程序之前,首先要对输入数据的合法性做判断,考虑可能出现的各种情况,分类处理,如果没有输入数据,要综合考虑处理过程中的意外情况。


2.区分'\0',0和空格,需要注意的是在C语言字符数组中,字符数组以'\0'为结束标志。这里的'\0'是ASCII码表中的第一个字符,名字是‘NUL’,ASCII编码为0x0,阿拉伯数字0的ASCII编码为0x30,空格的ASCII编码为0x20。在遍历字符串数组时,可以通过判断‘\0’字符,检测是否到达字符串末尾。

在写有关字符串的程序时,尽量用string.h中提供的标准字符串处理函数,常用的有strlen,strcpy,strcmp等。


3.可以直接让字符参与运算,比如a+13,a+13的结果类型取决与左值的类型,如果char b=a+13,那么b就是字符型变量n,如果int b=a+13,那么b就是整形变量n。

在输入的时候,如果变量类型为int型,那么如果输入的是字符型数据的话,变量的实际得到的值是该输入字符的ascii码!!!

判断一个字符是不是大小写字母时,可以用isalpha,也可一用‘a’到‘z’或者‘A’到‘Z’来判断,根据具体情况,选择合适的判断方法。

在字符参与表达式运算时,要特别小心,对字符做强制类型转换为int型时,得到的是字符对应的ascii码,例如int(0)等于0x30,在与数值混合运算时都是用的字符的ascii码!!!


4.memset函数一般用来给数组赋值,而不能用来给int型数组赋值,因为int类型变量占四个四节,如果采用memset(array,1,sizeof(array)对int型数组初始化的话,这个数组每个字节都会被初始化为1,那么数组中的每个元素就都被初始化为0x01010101,即4个值为1的字节,0x01010101写成十进制就是16843009,还有一个问题就是上式只能初始化sizeof(array)个字节,int型数组后边没有被初始化,因此数组后边都是随机值。memset执行后,就会出现前边一串16843009,后边都是混乱数字的情况。


5.stdarg库提供了可变参数支持,如果函数参数定义如下

int max_list(int a,int b,int c,int first_args,...)

那么我们在调用这个函数的时候,必须指定前四个参数的值,比较关键的是参数first_args,因为它备用来确定可变参数的起点。

可变参数函数使用步骤一般如下

va_list ap;              //可以当作定义参数列表数组
va_start(ap,first_args); //根据最后一个固定参数,确定可变参数表的开端
type a=va_arg(ap,type)   //得到参数,此处可以用while或者for循环得到所有参数,type是参数的类型
va_end(ap);              //收尾工作,关闭参数表

示例如下,该例子求得参数表中最大的参数,其中a,b,c和first_arg是必须有的参数,它们是固定参数,那么显然,可变参数的位置通过first_arg确定,va_start做的就是这个工作

int max_list(int a,int b,int c,int first_arg, ...)
{
	va_list var_arg;
	int max=0;
	if(first_arg>=0){
		int this_arg;
		max=first_arg;
		va_start(var_arg,first_arg);
		while(( this_arg=va_arg(var_arg,int)) >=0)   
			if(this_arg>max)
				max=this_arg;
		va_end(var_arg);
	}
	return max;
}

在上例中,while循环通过检测参数的正负,确定是否到达参数表尾端,因此传递给函数的参数最后一个必须是负数。也可以通过first_arg传递要处理参数的个数,然后通过for循环处理这first_arg个参数。

可变参数表中传递的参数个数是任意的,最终都是用va_arg从前往后顺序得到参数!!!

6.C语言中,int数组初始化没有给出具体值的全部自动初始化为0,char数组没有给出值的全部自动初始化为‘\0’,我们需要对多维数组的某个元素初始化时,就要在初始化列表中给出必要的能够确定该元素位置的括号,可能还要给出一部分元素的值,才能确定这个元素的位置,并初始化。

从前往后,用一维数组的思想去理解多维数组,比如数组a[3][4][5],可以理解为:有三个元素的一维数组,这个一维数组的每个元素又是一个有四个元素的数组,而这四个元素同时还是有五个元素的数组,用括号表示法如下,{{{1,2,3,4,5}, {},{},{}},  {{},{},{},{}},  {{},{},{},{}}},后边的元素省略

7.C语言判断给定矩阵是否是单位矩阵,即主对角线都为1,副对角线都为0的矩阵。下面例子比较巧妙

int identity_matrix(int mat[5][5])
{
	int r=0;
	int c=0;
	for(;r<5;++r){
		for(;c<5;++c){
			if(mat[r][c] != (r==c))
				return 0;
		}
	}
	return 1;
}

if语句很巧妙的判断了对角线和非对角线上两种情况!!!

8.牢记,C语言中int型数组默认初始化为0!!!!!!!!!!!!!!

9.C语言中联合体的典型应用如下,适用于必须在多个不同的候选结构体成员中选一个的情况,可以有效的节省内存:

struct INFO2
		{
			char cust_name[21];
			char cust_addr[41];
			char model[21];
			float msrp;
			float sales_price;
			enum { PURE_CASH, CASH_LOAN, LEASE } type;
			union
			{
				struct {
					float sales_tax;
					float licensing_fee;
				} pure_cash;
				struct {
					float sales_tax;
					float licensing_fee;
					float down_payment;
					int loan_duration;
					float interest_rate;
					float monthly_payment;
					char bank[21];
				} cash_loan;
				struct {
					float down_payment;
					float security_deposit;
					float monthly_payment;
					float lease_term;
				} lease;
			} info;
		};}


在一个成员长度不同的联合里,分配给联合的内存数量取决于它的最长成员长度。这样,联合的长度总是足以容纳它最大的成员。如果这些成员的长度相差悬殊,当存储长度较短的成员时,浪费的空间是相当可观的。在这种情况下, 更好的方法是在联合中存储指向不同成员的指针,而不是直接存储成员本身。所有指针的长度都是相同的,这样就解决了内存浪费的问题。 当它决定需要使用哪个成员时,就分配正确数量的内存来存储它

联合变量可以被初始化,但这个初始值必须是联合第一个成员的类型,并且它必须位于一堆花括号里面,例如:

struct {
	int a;
	float b;
	char c[4];
}x={5};

10.c语言中,变量和函数都是可以重复声明(声明必须一样),但不可以重复定义,由此可以想到,如果头文件中全部都是声明,不包括实现的话,重复包含头文件是没问题的,但是如果头文件中有实现的话,重复包含就会出错。

11.有时候为了方便阅读或者其他原因,再给一个字符数组赋值的时候,我么可以用连着个几个字符串,如下所示

 #include<stdio.h>

int main()
{
        char str[]="he""llo"" world";
        printf("%s\n",str);
}

最后打印出来的,还是“hello world”

12.perror函数打印自定义字符,并且以readable的方式输出当前errno的意思,下面我们读取一个并不存在的文件,看一下perror是怎样使用的。

FILE *p=fopen("file_not_exist","r");
perror("This is perror")

结果perror会输出以下信息:

This is perror: No such file or directory

后面的No such……是perror解释当前errno的值得出的信息,如果我们单纯调用perror,参数字符串为空的话,此时的perror相当于strerror(errno)。

13.select函数原型如下:

int select(int numfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); 
When select() returns, readfds will be modified to reflect which of the file descriptors you selected which is ready for reading. You can test them with the macro FD_ISSET(), below.

select返回时,readfds、writefds和exceptfds被设置为已经就绪的描述符,可以用FD_ISSET测试相应位,以便查看是哪个感兴趣的描述符就绪了。

14.send和recv

需要特别注意的是send和recv函数的返回值,send返回实际发送的字节数,recv返回实际收到的字节数,为了有效准确的发送和接收大文件,需要用如下所示的while语句来处理send和recv

#include <sys/types.h>
#include <sys/socket.h>

int sendall(int s, char *buf, int *len)
{
    int total = 0;        // how many bytes we've sent
    int bytesleft = *len; // how many we have left to send
    int n;

    while(total < *len) {
        n = send(s, buf+total, bytesleft, 0);
        if (n == -1) { break; }
        total += n;
        bytesleft -= n;
    }

    *len = total; // return number actually sent here

    return n==-1?-1:0; // return -1 on failure, 0 on success
} 

发送方在完成发送后,关闭连接,recv返回值为0,可以利用这个返回值来判断文件发送是否完毕

#include<#include <sys/types.h>
#include <sys/socket.h>

while((numbytes=recv(sockfd,buf,MAXDATASIZE-1,0))!= 0){
        buf[numbytes]='\0';
        printf("%s",buf);	
}





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值