C语言新手入门——从下载IDE开始

c_book

最近,高三的弟弟马上要去大学了。于是乎,打算教一教他C语言。

让我们从下载IDE( Integrated Develop Environment,即集成开发环境)开始。

Step Zero:买书

首先你需要的是一本完美的书,能够随时在你身边指引你的书。

《C程序设计语言》——if not you, then who?

本书原著即为C语言的设计者之一Dennis M.Ritchie和著名计算机科学家Brian W.Kernighan合著的一本介绍C语言的权威经典著作。我想,没有人比他更了解自己的设计。

摒弃你们的“谭浩强”吧~不是说谭先生不好,只是还不够。

Step One:下载最smart的IDE——CodeBlocks

1. 下载

地址:http://www.codeblocks.org/downloads/26

win7系统,选择这个版本就好了codeblocks-12.11mingw-setup.exe,可以从两个站点下载,一般是从这里Sourceforge这里下,如图

QQ图片20130726151625

点击连接以后,会跳到Sourceforge网站,等几秒钟就开始自动下载。

2. 安装

QQ图片20130726152132

点击Next,如上图。

QQ图片20130726152239

点击I Agree, 如上图。

QQ图片20130726152405

这一步是装运行所必须的各种软件,不用修改,默认点击Next就行。如上图。

QQ图片20130726152637

这一步是选择安装路径,可以点击“Browse”然后在弹出的地方选择,或者直接在“Destination Folder”下面的输入框里填写。不建议安装在C盘,工具软件安装在其他盘比较好。比如我就是安装在E盘的Tools文件夹下。然后点击Install进行安装即可。如上图。

如果安装途中出现以下错误,请以管理员身份重新执行安装步骤。即右键“codeblocks-12.11mingw-setup.exe”安装文件,然后选择以管理员身份运行。

QQ图片20130726152954

安装完成后,会问你是否现在运行Codeblocks,如下图。

QQ图片20130726153122

可爱的Codeblocks就出来啦!!!!

3. 编写第一个程序之前的小配置

QQ图片20130726153813

初次启动CodeBlocks的时候,会让你选择是否所有的 C和C++文件都以CodeBlocks打开,如图红色方框所示。点击它,然后点击OK。

现在我们需要新建一个项目,如下图所示。点击界面中间的”Create a new project”

QQ图片20130726154318

在弹出的窗口里选择“Console application“,即控制台程序,如下图所示。

QQ图片20130726154410

点击go,跳到下一个页面。选择使用的语言,这里选C。如下图所示。

QQ图片20130726154736

点击Next以后进入下一个页面。

在”Project title“,即项目名称这里填写项目的名称,这里我们用填入”Test“举例。填好以后,下面的Project fileName以及Resulting fileName会自动帮你填好。

然后就是选择你代码所在的目录,即”Folder to create project in“,可以直接输入文件夹目录(如我的就是放在E盘的Code文件夹下的C文件夹下),也可以点击”…“选择目录。如下图所示。

QQ图片20130726155008

填好以后点击Next,进入下一页。

QQ图片20130726155443

这里是选择编译器(暂时不需要去理解这是什么)以及其他设置。保持默认就好,点击Finish完成项目的配置。

进入开发界面HOHO!!!!

QQ图片20130726155817

双击左边Workspace下的的Sources,即可看到main.c文件,这就是你的源代码文件了,双击main.c,则会在右边的窗口显示出该文件的内容。如下图所示

QQ图片20130726160243

4. 编写人生中的第一个程序

红色框框里的就是啦,假设我现在稍微改一改它,

把printf(“Hello world!\n”);里引号的内容换掉,换成 方脑壳!!!!!!!,即

printf(“方脑壳!!!!!!!\n”);

然后我们要怎么运行这个程序呢?

首先,程序需要编译。因为代码是人写的,写的是一些英文字母,计算机不认识英文字母,它只认0和1,而编译器就是用来干这活儿的,编译器会把人写的代码,”翻译“成计算机能识别的0和1的组合。

那么,CodeBlocks如何编译代码?如下图,点击Build按钮即可。

QQ图片20130726160829

然后会在下方显示编译的结果,可以看到这句话”0 errors, 0 warnings (0 minutes, 0 seconds)“,即表示编译过程没有警告也没有错误。也就是编译器告诉你”我已经成功把你写的代码翻译成0和1的组合了,你可以运行它了“。

然后就是如何运行?

QQ图片20130726161407

如上图所示,点击build旁边的单个绿色箭头,你就会发现系统弹出了一个窗口。而这个窗口的内容就是,看图吧

QQ图片20130726161457

至此,如何新建、编写、编译、运行一个项目以及查看结果讲述完毕,不懂留言。

Step Final:一些建议

学习程序就得发扬”东搞搞,西搞搞“的精神。一般情况下,你是整不坏你的电脑的。所以说,大家多改改这里,改改那里,然后编译运行一下看有什么有趣儿的事情发生。

比如printf里为啥要有双引号啊,我不要行不行?行啊,你去掉以后,再编译,看看结果是什么。

为什么printf以后要有一个分号,我用句号结束行不行?行啊,你改以后,再编译,看看结果是什么。

为什么。。。。

为什么。。。。。

搞就是啊!大不了再改回来。大大不了重新新建一个项目。大大大不了重新安装ColdeBlocks。大大大大大不了重装系统(要是能到这份儿上,,,你已经不得了了)

运行完你的第一个程序以后,试一试把下面这段代码考过去,编译运行一下。

[cpp]
#include <stdio.h>
#include <stdlib.h>

int main()
{
printf("方脑壳!!!!!!!\n");

int a;
int b;
int sum;

printf("请输入两个不大不小的数,正负皆可,用空格隔开吧,回车隔开也可以\n");
scanf("%d%d", &a, &b);
sum = a + b;
printf("其实,计算器的原理就是这么简单,a + b = %d\n", sum);

return 0;
}

[/cpp]

至于有很多语句你现在看不懂,很正常,跟着那本书走吧。不久就会明白的。记住一点,try more and try more and try more……

That‘s all,

Enjoy!

POJ 2453 解题报告

题目意思:给定正整数x,求出在二进制表示中与他有相同个数的‘1’,且比他大的最小的数。

可以把此题当成求二进制中1的个数来做。而该算法在我的其他帖子(点击这里)中已经有详细说明。

代码:

[cpp]
#include <iostream>

using namespace std;

int Count (int);
int main()
{
int x, num;

while(cin >> x)
{
if(!x)
break;
num = Count(x);
while(x++)
if(Count(x) == num)
{
cout << x << endl;
break;
}
}
return 0;
}

int Count(int x)
{
x = (x & 0x55555555) + ((x >> 1) & 0x55555555);
x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
x = (x & 0x0f0f0f0f) + ((x >> 4) & 0x0f0f0f0f);
x = (x & 0x00ff00ff) + ((x >> 8) & 0x00ff00ff);
x = (x & 0x0000ffff) + ((x >> 16) & 0x0000ffff);

return x;
}
[/cpp]

原题连接http://poj.org/problem?id=2453

寻找发帖“水王”

编程之美第2.3题————寻找发帖“水王”

题目描述:

Tango是微软亚洲研究院的一个试验项目。研究院的员工和实习生们都很喜欢在Tango上面交流灌水。传说,Tango有一大“水王”,他不但喜欢发贴,还会回复其他ID发的每个帖子。坊间风闻该“水王”发帖数目超过了帖子总数的一半。如果你有一个当前论坛上所有帖子(包括回帖)的列表,其中帖子作者的ID也在表中,你能快速找出这个传说中的Tango水王吗?

首先,我们可以假设有这样一个数据结构来表示帖子

[cpp]
type struct post
{
char* title;
char* content;
int uid;
}post_t;
[/cpp]

题目告诉我们已知所有帖子的列表,也就是说我们知道了所有帖子的链表 post_t* post_list,或者是所有帖子的数组post_array[num]。然后让我们找到出现次数最多的uid是多少,并且该uid一定超过了总和的一半。为了叙述方便,下面用数组举例。最容易就想到的方法,就是遍历整个数组,记录每一个uid出现的次数,然后再比较一下,求出最大的。

其实,这个问题就可以等价于:求数组中出现次数大于一半的那个数

解法一

对数组排序,然后遍历排好序的数组,统计各ID出现的次数,找到超过一半的那个即可。代码如下:

[cpp]
int Find (int* post, int len)
{
sort(post, post + len);

int num = 1;
for(int i = 0; i < len; i++)
{
if(post[i] == post[i + 1])
num++;
else
num = 1;
if(num > len / 2)
return post[i];
}
return 0;
}
[/cpp]

这里先调用了一下sort函数排序,也可以换成自己的排序函数。

因为采用了排序,复杂度有所提升,于是书中提出了第二种解法,我觉得是比较巧妙的。

解法二

由于x出现的次数大于总数的一半,所以如果每次删除两个不同的值,不管是否有x,在剩下的数中,x的次数依然会大于总数的一半。

于是,算法可以这样设计

[cpp]
int Find2 (int* post, int len)
{
int x;
int times = 0;
for(int i = 0; i < len; i++)
{
if(times == 0)
{
x = post[i];
times = 1;
}
else
{
if(x == post[i])
times++;
else
times–;
}
}
return x;
}
[/cpp]

该算法不用排序,很巧妙的通过向前移动达到了“删除”的目的,自己真的没想到:)。

越来越觉得优美的代码就是美了,简洁明了,而又功能强大。

扩展问题:

随着Tango的发展,管理员发现,“超级水王”没有了。统计结果表明,有3个发帖很多的ID,他们的发帖数目都超过了帖子总数目N的1/4。你能从发帖ID列表中快速找出他们的ID吗?

解法x

这个思路就是一样的了,设a,b,c三人的发帖数都超过了 1/4,那么把4个不同id删除以后,并不影响剩余数组中a,b,c三人的发帖数都超过1/4这个结果。示意图如下所示。

QQ图片20130721232439

把数组分为四个四个数字一组的来看。由于a的发帖数超过了1/4,所以,平均下来,在每一个分组里都有一个a,并且至少在某一组中有大于一个的a,假如分组1中的四个数字都不相同,我们删除分组1,如果a不在分组1里面,那么在剩余的2~n组中,a出现的次数显然会继续大于1/4;如果a在分组1里面,那么在剩余2~n组中,平均下来,a还是会至少在每一个分组出现一次。所以只要分组1中4个数字不相同(主要是保证没有两个a),那么删除分组1,并不改变a在剩余数组中出现次数依然大于1/4的事实。同理b,c。

代码如下:

[cpp]
void Findx(int* post, int len)
{
int a, b, c;
int ta = 0;
int tb = 0;
int tc = 0;

for(int i = 0; i < len; i++)
{
if(ta == 0)
{
a = post[i];
ta++;
}
else if(a == post[i])
ta++;
else if(tb == 0)
{
b = post[i];
tb++;
}
else if(b == post[i])
tb++;
else if(tc == 0)
{
c = post[i];
tc++;
}
else if(c == post[i])
tc++;
else
{
ta–;
tb–;
tc–;
}
}

cout << "a: " << a << "\t";
cout << "b: " << b << "\t";
cout << "c: " << c << "\t";
}
[/cpp]

Enjoy!

求二进制数中1的个数

编程之美第2.1题————求二进制数中1的个数

题目描述:对于一个字节(8bit)的无符号整形变量,求其中的二进制表示中“1”的个数,要求算法的执行效率尽可能的高。

书中由浅入深地给出了几种解法。

解法一

也是最容易想到的,因为对于无符号整数,若采取对2取余操作,结果只有两种,奇数余1,偶数余0,而奇数的二进制最后一位为1,偶数的二进制最后一位为0。这样我们就可以通过奇偶判断来得到结果,以二进制数1011和1100举例:

1011 对2取余,余数为1,二进制末尾为1,即含有一个1

1100 对2取余,余数为0,二进制末尾为0,即含有一个0

不管这个二进制数的高位有多少个0或者多少个1,对2取余的操作只与最低位相关。所以,如果能够把所有的位都作为最低位,对2取余一次,然后将结果相加,即可得到该题答案。而二进制数中,右移操作,就相当于除以2,所以有了以下解法。

[c]
unsigned int Count (unsigned int x)
{
unsigned int num = 0; // the result
while(x) // end of loop when x is 0
{
if(x % 2 == 1) // to judge whether the last bit is ‘1’
{
num++;
}
x = x / 2; // same as x >> 1
}
return num;
}
[/c]

当然你要是觉得除法耗费时间,可以把x = x / 2改成 x >> 1,本质上是一样的。

解法二

既然提到了右移,就能想到,右移的时候会把最后一位移丢,那我们只要在每一次移动的时候,判断一下最后一位是否为1,然后重复该步骤直到将所有位都移除。用1011举例:
1011,最后一位是1,结果加1,右移一位,变成0101

0101,最后一位是1,结果加1,右移一位,变成0010,

0010,最后一位是0,结果不加,右移一位,变成0001,

0001,最后移位是1,结果加1,右移一位,变成0000,所有位都已移除,结束算法。

而判断最后一位是否为1,可以用&操作,x & 0x1 即可。

算法如下:

[cpp]
unsigned int Count2 (unsigned int x)
{
unsigned int num = 0; // the result
while(x) // end the loop when x is 0
{
// x & 0x01 will be 0 if the last bit is ‘0’, otherwise ‘1’
num = num + (x & 0x01);
x = x >> 1; // right shift 1 bit
}
return num;
}
[/cpp]

可以看到,上述两种解法对每一位都进行了比较运算,时间复杂度都为O(log2x),出于降低时间复杂度的角度,书中又给出了第三种解法,让复杂度只与二进制数中‘1’的个数相关。

解法三

该解法确实好玩儿,让我有一种“让广大码农飞,让搞算法的死”的感觉,很巧妙。还是先举一个例子来阐述原理:对于无符号一个二进制数x,与 x – 1 采取&操作会消除x最低位的1,假设x为1010,0000:

x – 1 的二进制为 1001,1111,与x进行&以后得1000,0000,最低位的1没有了,让x = 1000,0000

x – 1 的二进制位 0111,1111,与x进行&作以后得0000,0000,最低位的1没有了,x 为0,结束算法

算法一共就执行了两次,即‘1’的个数。不管‘1’之后有多少个0,x & (x – 1) 总是会消除最低位的1,这就是该算法的巧妙之处了。代码如下。

[cpp]
unsigned int Count3 (unsigned int x)
{
unsigned int num = 0;
while(x)
{
x = x & (x – 1);
num++
}
return num;
}
[/cpp]

算法把时间复杂度降到了O(n),n为二进制中‘1’的个数。应该算是很不错了,关键是很巧妙,灰常好玩儿有木有。

书中还给出了查表法,因为题目是八位的二进制,那么一共也才256个数,完全可以开一个256大小的数组,将结果存起来,如255的二进制为1111,1111,让result[255] = 8,同理把其他所有数的结果都存一遍,以后需要的时候直接返回result[x]就可以了。只是如果不是八位的,变成了32位,那查表这种空间换时间的方法就很不划算了。

后来查阅网上一些题解的时候,发现很多大牛对此题书中解法的提出了质疑。比如解法三,虽然理论上一次地址计算就可以解决,但实际上这里用到一个访存操作,且第一次访存的时候很有可能那个数组不在cache里,这样一个cache miss导致的后果可能就是耗去几十甚至上百个时钟周期(因为要访问内存)。而对于这种数据规模给定,而且很小的情况下,耗去几十甚至上百个时钟周期是很慢的。所以对于这种“小操作”,这个算法的性能其实是很差的。

然后我看到了另外一个巧妙的解法,有点像二分,个人灰常喜欢。

解法X

怎么说呢,先举个例子,比如整数212的二进制1101,0100,一共有4个‘1’对吧。而正好又是8位大小,有没有一种“对折对折再对折”的冲动,有木有?

于是算法可以这样描述:

先把相邻的‘1’的个数算出来,同样二进制的形势保存起来

QQ图片20130720202159

那么现在 10,01,01,00分别代表了1101,0100中相邻两位中‘1’的个数。而且由于是计算的相邻两位,所以‘1’的个数最多为2,所以用两位足够表示。

现在的问题变成了,如何继续处理第一步得到的结果1001,0100呢,对于1001,0100,

第8位,第7位联合起来,即10,表示了原二进制数(1101,0100)中第8位与第7位所含‘1’的个数。

第6位,第5位联合起来,即01,表示了原二进制数(1101,0100)中第6位与第5位所含‘1’的个数。

第4位,第3位联合起来,即01,表示了原二进制数(1101,0100)中第4位与第3位所含‘1’的个数。

第2位,第1位联合起来,即01,表示了原二进制数(1101,0100)中第2位与第1位所含‘1’的个数。

现在我们就需要把新得到的二进制串1001,0100的

8、7联合位与6、5联合位相加,即把10和01相加,代表原第8,7,6,5位的‘1’的个数

(因为8、7联合起来才表示原第8位和原第7位一共有多少个‘1’,这里不能再次分开计算他们了,应该把他们当成一个整体。同理6、5位,也当成一个整体来看待)

4、3联合位与2、1联合位相加,即把01和01相加,代表原第4,3,2,1位的‘1’的个数

QQ图片20130720225843

后面就类似啦,把0011与0001相加,代表原第8,7,6,5,4,3,2,1位的‘1’的个数

QQ图片20130720230443

就得到最后的结果了,结果为4。

好了,现在剩下的问题就是怎么样让相邻的位相加,怎么样让相邻两位的相加,怎么样让相邻四位的相加。

拿相邻两位的相加举例,

(x & 0x55) + (x >> 1  & 0x55)

因为0x55展开成二进制就是0101,0101,观察1的位置,奇数位为1,偶数位为0,这样和x进行与操作的结果就是保留了x的奇数位。然后x右移一位以后,再与0x55进行与操作,对x>>1来说是保留了奇数位,但相对于原来的x则是保留了偶数位。最后将两式相加,就得到了x的二进制中,相邻两位的‘1’的个数。

同理因为0x33展开成二进制是0011,0011,所以相邻二位的相加则为

(x & 0x33) + (x >> 2 &  0x33)

因为0x0f展开成二进制是0000,1111,所以相邻四位的相加则为

(x & 0x0f) + (x >> 4 &  0x0f)

所以算法如下:

[cpp]
unsigned int CountX (unsigned int x)
{
x = (x & 0x55) + (x >> 1 & 0x55);
x = (x & 0x33) + (x >> 2 & 0x33);
x = (x & 0x0f) + (x >> 4 & 0x0f);
return x;
}
[/cpp]

优美的代码总是很简洁,这个算法估计只要十几行指令吧,速度应该是很不错了,至于网上其他更好的算法,写得让我半天看不懂哈哈~~感觉太复杂了也就失去了美。

最后,POJ的相关题目有Problem2453

原题链接:http://poj.org/problem?id=2453

解题报告:http://keping.me/poj2453/

That’s all,

Enjoy!

读《怀才纳贿的“二皇帝”和珅》

001

                                               和珅(1750~1799,清朝人,钮祜禄氏),权臣,豪商,巨贪。

读这本书并不是因为什么“读史可以明智”,还达不到那个境界。“以史为鉴”倒还是不错的。

读这本书是因为最近开始对历史有些感兴趣,恰巧在书店转的时候,又被它诙谐的语言,文学的形式所吸引,离开的时候也就带上了。从来没见过一本史书可以这样写,让人读起来毫不费力,有一些像小说。

本书从和珅出生,到求学,到考公务员(是的,原书写的就是考公务员),再到后来的官场沉浮,一直写到乾隆驾崩以后势力的迅速没落,最后上吊自杀。全方位、多角度地展示了一个真实的和珅,一个有血有肉的和珅。

此前,接触和珅最多的则是从电视上——《铁齿铜牙纪晓岚》,一部饱受争议的电视剧,最大的争议点当然还是不符合历史。其实大可不必纠结,电视剧本来就是用来消遣的,君不见广大抗日神剧中,八路军小战士手撕日寇么,难道这也要去算一算作用力与反作用力?

再说和珅,王刚先生之所以把和珅演的惟妙惟肖、脍炙人口,不是因为多像历史的和珅,而是因为多像剧本。试问,你见过哪个电视剧的反面能演到如此让人又爱又恨看不到又想的程度么?王刚的和珅,走的是“戏饰和珅”的感觉。结合剧本的语境词境意境,将和珅一角色表达的生动活泼惟妙惟肖。

只是,和珅真不是一个大胖子啊,王刚先生。

据广大史书记载,和珅是一个美男子,而在不是以肥为美的大清,我觉得,和珅应该不会是一个胖子。

回到书中,给我印象最深的,除了和珅的干女婿刘国泰借银填仓那一段(因为最近发生的黑龙江火龙烧仓事件,实在是很难让人不起联想),就是和珅对夫妻、兄弟、朋友之前感情的珍视了,他也有他善良的一面。不过好像上天并不眷顾他们家,在各自前途最光明的时候,都相继死去,以致最后的描写的和中堂都有点让人惋惜的感觉。

如果你还没读过这本书,也碰巧想了解一下中国史上第一贪的话,可以试着读一读。走近和坤跌宕起伏、颇具戏剧色彩的人生,无疑也能让你对人性、对历史有更多的体会。

2013-7-17 于 北京

MySQL 插入错误

MySQL insert error。。。。。。。。

 

一条简单的SQL语句,竟然插入错误:

INSERT INTO USER (id, describe) VALUES (‘David’, ‘diudiu’);

咋一看确实没错啊,瞅半天才发现,是由于table中有字段名是MySQL的保留字。这里就是‘describe’,报错也就理所当然了。

解决办法:
在关键字的地方加上反引号”`”,就是键盘1旁边的那个字符。

MySQL就是通过添加反引号来区别保留字。

上述修改为

INSERT INTO USER (id, `describe`) VALUES (‘David’, ‘diudiu’);

即可。

 

 

Linux 下 C++ 连接 Mysql

——国外有一篇博客写得挺好,有一些小错误,改正以后翻译过来,发这里了。

C++是一个效率非常高的编程语言

Linux 是一个类Unix操作系统,它促进了开源软件社区的发展,并且用这些开源软件就几乎足以构建一个复杂精妙的企业系统。

MySQL是一个流行的多线程、多用户的数据库管理系统,全球有超过1000万的安装量。

 

这篇文章将演示如何在Linux下用C++连接MySQL。

首先我们需要将所需要的头文件和库全部包含进来

#include <stdio.h>

#include <mysql.h>

然后我们来定义一下main函数

int main()

{

// code 

return 0;

};

然后我们定义一下需要用到的变量,你应该将这些写到main函数里去。

MYSQL_RES *result;

MYSQL_ROW row;

MYSQL *connection, mysql;

int state;

既然要使用MySQL数据库,我们首先就得连上它,使用下面的代码即可:

mysql_init(&mysql);

connection = mysql_real_connect(&mysql,host,usr,pswd,database,0,0,0);

其中

host – 数据库所在主机的名字,比如 “localhost” 或者 “192.168.250.100”

usr – 连接数据库时使用的用户名

pswd – 连接数据库时使用的密码

database – 数据库的名称

如果有错误发生,如用户名/密码不匹配,我们可以通过以下代码获取到错误信息:

if (connection == NULL)

{

printf(“%s\n”, mysql_error(&mysql));

return 1;

}

如果没有错误,那么证明我们连接好了,现在我们来执行一个简单的查询语句,其中mytable指的是你指定的数据库中的一张表名:

state = mysql_query(connection, “SELECT * FROM mytable”);

if (state !=0)

{

printf(“%s\n”, mysql_error(connection));

return 1;

}

如果成功执行,我们可以使用一个变量来存储查询结果:

result = mysql_store_result(connection);

使用mysql_num_rows函数,我们可以得到查询结果的条数:

printf(“Rows:%lld\n”,mysql_num_rows(result));

使用mysql_fetch_rowwhile循环中处理每一条查询结果:

while ( ( row=mysql_fetch_row(result)) != NULL )

{

printf(” %s, %s\n”, (row[0] ? row[0] : “NULL”), (row[1] ? row[1] : “NULL” ));

}

最后,别忘了释放内存:

mysql_free_result(result);

mysql_close(connection);

IMPORTANT!!! How to make this code under Linux?

重要!!!!在Linux中怎样编译上面的代码呢?

g++ test.cpp -I/usr/local/mysql/include /usr/local/mysql/lib/libmysqlclient.so

路径有可能不同,修改为自己的即可。

/usr/local/mysql/include 中含有mysql.h等头文件

/usr/local/mysql/lib/libmysqlclient.so 这个动态链接库也是需要的

Enjoy!