Archive for the ‘C语言的那些事儿’ category

名次确定问题(Update:8/10)

29 7 月, 2010

五位运动员参加比赛,进行结果预测:

A说:B第一,我第三
B说:我第二,E第四
C说:我第一,D第三
D说:C最后,我第三
E说:我第四,A第一

比赛结束,每位选手只说对了一半,编程确定比赛名次。

拿到这道题,我首先想到的是,ABCDE五人有5!个排列次序,对于每个次序都验证题目要求,满足即可break各个循环。于是代码如下:

#include 

int main()
{
	int i,sum=0;
	int a,b,c,d,e;
	int flag=0;

	for(a=1;a<\6;a++)
	{
		for(b=1;b<\6;b++)
		{
			for(c=1;c<\6;c++)
			{
				for(d=1;d<\6;d++)
				{
					for(e=1;e<\6;e++)
					{

						sum=0;
						sum=(b==1||a==3)+(b==2||e==4)+(c==1||d==2)+(c==5||d==3)+(e==4||a==1);
						if(sum==5)
						{
							flag=1;
							printf("e=%d ",e);
							break;
						}
					}

					if(flag)
					{
						printf("d=%d ",d);
						break;
					}
				}
				if(flag)
				{
					printf("c=%d ",c);
					break;
				}
			}
			if(flag)
			{
				printf("b=%d ",b);
				break;
			}
		}
		if(flag)
		{
			printf("a=%d ",a);
			break;
		}
	}
	printf("\n");
	return 0;
}

编译运行。结果错误:11134。原因是这样的:满足上述条件的序列很多,但是遇到第一个满足条件的序列(此时a,b,c都是1,可见没跑几下就break了)时,因为多个break的原因就跳出了所有循环。看来还得加一个筛选条件。既然名字是1~5的任意排序,那么五位选手名次之和肯定是固定的,因此加入此条件即可筛选处最终名次。如下:

						sum=0;
						sum=(b==1||a==3)+(b==2||e==4)+(c==1||d==2)+(c==5||d==3)+(e==4||a==1);
						if(sum==5)
						{
							if((a+b+c+d+e)==15)
							{
							flag=1;
							printf("e=%d ",e);
							break;
							}
						}

这样就OK了。看来除了得到题目表面的信息后,还得学会抓住隐含条件。其实第一个条件也可以有下面的几种表达方式:

                                                //方法1
						sum=0;
						sum+=(b==1&&a!=3)||(b!=1&&a==3);
						sum+=(b==2&&e!=4)||(b!=2&&e==4);
						sum+=(c==1&&d!=2)||(c!=1&&d==2);
						sum+=(c==5&&d!=3)||(c!=5&&d==3);
						sum+=(e==4&&a!=1)||(e!=4&&a==1);
						//方法2
						sum=0;
						sum=(a==3||a==1)+(b==1||b==2)+(c==1||c==5)+(d==2||d==3)+(e==4);

这里还有一个更简单的方法。在我作完后,我觉得这个题目用5重循环是否太多余,就想看看牛人是如何完成的,所以就咨询牛涛,它发过来一个链接,果然很简便。虽然意思是一样的但是时间复杂度只有N平方而已。我这个N的5次方 e42

Update:8/10

今天一位同学留言说,判断条件应该再加一条判断a,b,c,d,e互不相等。这个条件在我做这个题的时候就想过,可当时总觉的两个判断条件即可。但是,为什么上面的代码也可以显示处正确的答案?那是因为我加入了break。恰好第一组名词就互不相等。如果把每个break都删除了,就会发现有两组结果,其中一个结果就有重复的名次。

其实这个题目属于一个穷举类的题目,应该在程序中让它列出所有的可能(尽管本题中的答案只有一个),而不是找到一个就立刻停止。

字节对齐

23 7 月, 2010

今天在看结构体和共用体部分的时候,遇到了一个新名词“内存对齐”。先引入问题吧。如下:

struct student
{
	char name[20];
	int age;
	char sex;
	char phone[15];
};
struct student p1;

sizeof(p1)=?
这个很简单得出答案,即20+4+1+15=40Byte。如果将phone[15]改为phone[16],结果是44。难道不是41吗?

这里便要引入内存对齐的概念。内存为了提高访问效率,便规定以结构体中最大的基本单位长度为对齐标准。即实际分配的内存大小是对齐标准的整数倍(必要条件)。在上面的结构体中,最大的基本类型是int。因此以4Byte为对其标准。所以实际内存大小应该为4的整数倍,即为44Byte.

也许你有疑惑:name[20]不是要20Byte吗,为什么以4Byte为对齐标准?请注意这里的基本类型。name[20]是一个字符串数组,数组属于复合的数据类型。复合的数据类型还有结构体,共用体。基本的数据类型是整型,字符型,浮点型。如果你还有不解,那么看下题:

struct score
{
	float english;
	float math;
	float computer;
};
struct student
{
	char name[10];
	int age;
	char sex;
	struct score st_score;
};

在student结构体中含有数据类型为struct score这样的变量。struct score的大小为12Byte,也是struct student结构体中最大的数据类型,但是我们的对齐标准是4Byte,就如我们刚说的那样,内存的对齐标准是取结构体中最大的基本数据类型,这里我们取sizeof(int)。

再看下面的问题:

union data1
{
	int i;
	char c;
	char str[9];
};
struct data2
{
	int i;
	char c;
	char str[9];
};

sizeof(struct data1)=? sizeof(struct data2)=?
对于前者,由于共用体的存储大小由最大的成员来决定,因此上题中共用体的存储大小为9Byte,考虑到内存对齐,它以sizeof(int)=4Byte来对齐,因此实际内存分配大小是12Byte。对于后者,存储大小是4+1+9=14Byte,这个结构体以4对齐,因此实际分配大小为16Byte。

现在你应该对于共用体和结构体的内存对齐有所了解了。那么再接着往下看:

struct data
{
	char c;
	double d;
	char ch;
}

sizeof(struct data)=?
有了上面的理解,10肯定不会是答案,那么会是16吧。也错。正确答案是24。你可能会感到莫名其妙:按照上面所说的方法,以8为对齐标准,那么10不是8的倍数,那就是16呀。为什么呢?请你耐心看下面。

首先我们知道结构体变量是分配的连续内存空间。d毫无疑问是分配8Byte,对于d“前面”的c,按照对齐标准我们应该分配8Byte,而d后面的ch,按照对齐标准也应该分配8Byte。因此结果是24。

如果这样:

struct data
{
	double d;
	char c;
	char ch;
}

sizeof(struct data)=16。

因为内存没必要分别为c和ch分配8Byte来进行对齐,将它们放在一个8byte里就可以实现内存对齐。上面在引入内存对齐概念的时候,我用括号特别注释必要条件便是说明此处内容。

线程ID的输出格式

20 7 月, 2010

Last Update:8/15

在获取线程ID的时候,常用到pthread_self()函数获取当前线程的ID,它返回的ID是pthread_t类型的。如果要打印出线程ID,用什么输出格式呢?

此书中例题8_1中使用的是%u,即unsigned int,但是编译时候有一个warning:

format ‘%u’ expects type ‘unsigned int’, but argument 2 has type ‘pthread_t’。

其实线程ID的格式应该是unsigned long,所以输出格式为:%lu。再次编译就对了。linux中还有一些ID类的数据类型,比如常见的pid,它的输出格式为%d;

再比如文件状态结构体struct stat *buf中的st_ino字段,它的类型为ino_t,而输出格式为%lu。在内核的inode结构体(include/linux/fs.h)中可以找到这个字段的最终面目:unsigned long i_ino;它便是索引结点号。

try it,please

28 6 月, 2010
#include "stdio.h"

int main()
{
	while(1)
	{
		printf("my tooth is fucking aching again!!!\n");
	}
	return 0;

}

C语言笔记本

16 6 月, 2010

在此记录一些常见的C语言错误,可以当作学习C语言的笔记,需要的时候可以回过头看看。

1.关于“++”

#include
int main()
{
	int a,b,cd;
	a=10;
	b=a++;
	c=++a;
	d=10*a++;
        printf("a=%d b=%d c=%d d=%d\n",a,b,c,d);
	return 0;
}

关于自加自减运算符用于句话就可以总结:a++先使用后自加,++a先自加再引用。这样就可以轻松得出结果:

edsionte@edsionte-laptop:~/code$ ./test
a=13 b=10 c=12 d=120

2.交换变量
第一种方法即中间变量法,下面说明第二种,不添加中间变量:

int a=1,b=2;
a=a+b;
b=a-b;
a=a-b;

3.char* str=”hello world”; sizeof(str)=12,strlen(str)=11.因为strlen函数求字符串长度时不包含字符串末尾的’\0’。

4.char a[10];int len=strlen(a); len其实不等于10,应该注意strlen求的是从字符串第一个元素到‘\0’之间字符总数(不包含‘\0’),而此数组未初始化。尽管语法上是正确的,但是用法错误。如果换作sizeof(a),结果便是10。

5.int(*s[10])(int) 表示什么?

这个语句看起来很复杂,不过我们一步步来分析。首先s[10]是一个数组,*s[10]说明他的元素是指针。那种类型的指针呢?整体来看这个语句:int(*)(int),不用多说是函数指针。那么进一步说就是,s[10]是一个有10个元素的指针数组,每个元素都指向一个函数,并且这个函数有一个int参数,并且返回int型的值。这样看好像很麻烦,不久是一个指针数组吗,何必这么麻烦。为了好理解,我们可以这么看:int(*)(int)  s[10]。

类似的对于数组指针,我们也可以这么理解,以便加深记忆。比如int (*s)[10];s是一个指向数组的指针,这样定义一个指针说实话很难让人理解。那么我们不要被他这个样子而迷惑,那就这么理解吧:int (*)[10] s;这样很符合一般指针定义的规则。

6.如下语句:

char* dest=”hello,world”;

char* src=”hello”;

puts(strcpy(dest,src));

这样做是错误的,原始是因为dest指向字符串常量,不可修改其某个字符值。其实正确的写法应该是const char* dest=”hello,world”;但是一般情况下不加const编译的时候也不会出错。但是如果dest[2]=’a’;那么就会出错,即便这条语句合乎语法。如果 char a[100]; char* dest=a;因为数组中的元素是变量,可以修改,也就可成功打印。如果有下面语句:char str[100]=”hello,world”,这样写完全正确,因为只是用hello world初始化这个数组而已。

7.想要输入下面一段字符串:edsionte is a goog boy。用scanf函数是不行的。因为scanf函数以回车键和空格键作为字符串输入结束的标志。而gets函数则仅以回车键作为字符串输入的结束符。因此可以使用gets函数来接收含有空格键的字符串。

8.宏定义和预编译不属于c语言语句,因此不已;结尾。而结构体定义属于c语言语句,应该以分号结尾。

9.共用体内可以定义多个不同类型的成员变量 ,但是每次只能使用一个变量。即这些变量共同使用一段内存空间,这段内存空间的大小由这些变量中长度最长的变量决定。

10.对于int a[5]这个数组来说,a,&a,&a[0],a+1,&a+1,&a[0]+1各代表什么?

a:a是这个数组的名称,它代表数组首元素的首地址,也就是a[0]的首地址。

&a:是数组的首地址。注意如果打印a和&a的值,两者是一样的,但是意义不同。

&a[0]:a[0]的首地址,等价于a。

a+1:数组中下一个元素的地址,即a[1]的地址。

&a+1:这是一个指向下一个数组的指针,这个数组是一个长度为5的整型数组。

&a[0]+1:等价于a+1。

11.在一般的32位机当中,指针类型均为4个字节,即便这个指针指向一个字符,因为指针说到底还是地址。对于一个数组int a[10],那么sizeof(a)=10*4;但是如果p是一个指向此数组的指针,那么sizeof(p)就是4。

12.给一个指针加上一个整数与给该指针的二进制表示加上同样的整数含义是不同的。比如int a[10];int *p=a;那么p+1指向的是这个数组的第二个元素,并不是在a[0]元素的首地址上加1。

13.定义一个枚举类型的变量:

enum return_value
{
	RET_TURE;
	RET_FALSE;
	RET_RANDOM;
}

枚举类型中每一个枚举常量都是用标示符表示的整形常量。上述枚举类型中的各个枚举常量并未赋值,但是默认值依次为0,1,2。

windows 7 ultimate product key

windows 7 ultimate product key

winrar download free

winrar download free

winzip registration code

winzip registration code

winzip free download

winzip free download

winzip activation code

winzip activation code

windows 7 key generator

windows 7 key generator

winzip freeware

winzip freeware

winzip free download full version

winzip free download full version

free winrar download

free winrar download

free winrar

free winrar

windows 7 crack

windows 7 crack

windows xp product key

windows xp product key

windows 7 activation crack

windows7 activation crack

free winzip

free winzip

winrar free download

winrar free download

winrar free

winrar free

download winrar free

download winrar free

windows 7 product key

windows 7 product key