其实我也不知道是为了啥, 到了现在这种年纪还想学习Linux下的C语言编程。因为我一直就傻傻地认为机会是垂青有准备的人,也一直呆呆地认为活到老学到老。现在Android这么火,各种终端如雨后春笋,而这些终端如果不安装Windows的,势必会使用开源的Linux,而Linux上面跑的程序,C还是占据很大市场的,一旦时机成熟,就可以立马换车改门庭,不至于产生职业耽误。这就是我的这种蠢蠢的初衷。在深圳,特别是现在这个时段,周围的人想到的是如何快速的融入股市,杀进房金融市场,趁着牛市的尾巴赚个盆满钵满,买房买车,迎娶白富美,实现人生的伟大理想。我这种想法似乎十分幼稚,但此时此刻,当我发布这篇随笔时,我的的确确就是这么想的。

回到今天的主题,我今天是想先通过一个简单的Hello,World的程序,然后讲讲Module,讲讲Makefile 等一些非常初级的c编程,如果你很熟悉Linux和C的话,那么你可以选择绕过这篇,或者你可以在后面发表你的建议,欢迎批评指正,但拒绝一上来就乱喷的青子,我们都过了那个年龄了。

首先来看一下我使用的gcc版本

cc -v

或者

gcc -v

至于为什么两个命令得到的是相同的信息gcc version 4.8.3 20140911 (Red Hat 4.8.3-9) (GCC)呢? 这是因为cc是unix下的编译器,而现在大多数的linux上,都把cc连接到gcc。也就是说cc是gcc的软连接。

1,  开始我们的第一个程序代码编写吧,这里我使用的编辑器是vim

vim hello.c

然后在hello.c里输入

#include <stdio.h>

int main(int argc, char* argv[])
{
prinftf("Hello,World!");
return ;
}

:wq!出来之后,继续输入

gcc hello.c -o hello.out && ./hello.out

就这么简单,就可以得到我们伟大的Hello, World程序了。

这里我只是补充说明一下上面如果不使用-o hello.out来指定输出的文件名的话,那么gcc会默认生成一个叫a.out的文件出来。那么后面的连接的就应该相应的改成a.out了,也就是

gcc hello.c && ./a.out

当然,如果你觉得这个&& 突兀的冒出来,一时不知是干什么用的话,那么你也可以分成两批去执行,先编译,然后再执行。其实&&的作用就是将两个命令合在一起提交,如果前一个命令如果没有返回错误的话,那么继续执行下一条命令。上面的语句就是编译完hello.c没有错误的话,紧接着就执行刚编译出来的程序。对于我来说,我是喜欢用&&来联接的,我在网上下载下来的源程序也常喜欢make && make install来一步执行。因为我觉得./configure 没有错的话,后面基本上没有啥大问题了,当然这不是绝对。

2, 伟大的Hello,World就完成了。之所以伟大,就是因为其极其简单,武侠小说里面常常也说简单就是实用,比如杨过练的独孤九剑,使用的剑是要比平常人的宝剑要重得的多的,谓之曰重剑不锋。但是一直靠大砍大杀的简单招式去对敌的话,估计也成就不了神雕大侠的。所以还有独孤九式,还有黯然销魂掌等等,所以接下来,我们也来把hello.c改变一点点,注意是一点点,仍然很简单,就是我们让hello.c里的main方法去调用另一个方法。

#include <stdio.h>

int main(int argc, char* argv[])
{
int a=;
int b=;
int maxNo=max(a,b);
printf("the max number is %d",maxNo);
return ;
} int max(int a, int b)
{
if(a>b) return a;
return b;
}

嗯,上面这段代码最终输出:

the max number is .

按照我们的意愿得到了结果,这是一个很好的开端。但是我们想想,如果这个max函数是由别人来协同开发的,两个人不可能都来修改hello.c这个文件吧?如果是那样的话就乱套了,merge 的时候都会把人搞疯的。为了不疯人,为了少抓狂,我们把max这个函数另存进一个max.c文件里去,或者由另一位同事去实现。

这里我讲一下我的操作是这样子的。

vim hello.c

打开编辑,然后再把光标移到int max(int a,intb)的那一行,按5dd得到5行到剪贴板,然后:sp max.c,然后ctrol+w+上光标键,最后按p,将复制的那5行码贴入max.c里,最后:wqa!

分开了两个文件之后,我们的编译就得这样了:

gcc max.c hello.c -o hello.out

最后执行

./hello.out

也可以得到想要的正确结果。

the max number is .

3, 嗯,很好,完成了分离了,结果如预期所想。完美解决方案。可是不久之后,又有人实现了一个min.c的模块,并且通知了hello.c去调用一下并显示出最小的数据出来。于是编译相应的就改成了

gcc max.c min.c hello.c -o hello.out

没错,就是将相关的.c文件列举上去就可以,很容易很简单,可是如果项目一旦复杂,.c文件有上千个,那岂不要累死程序员,累死那个发布的同事了吗?当然不可以,我们对于每个人都是不抛弃不放弃。所以我们使用Makefile来解救他们。

#this is a make file for hello.out
hello.out:max.c min.c hello.c
gcc max.c min.c hello.c -o hello.out

那么接下在bash里面直接输入make就可以了。当然,作为第一次使用,我还是没有忘记使用make -v看一下我使用的版本号是多少:

最后执行的结果也是想要的预期结果。

4, 似乎很完美了,可是我们去看看Makefile的内容,总有一种异样的怪怪感。当然如果没有发现也很正常,毕竟我们还只有三个文件,而且现在的机器也是很快的,执行make时也不会有啥差异的。其实如果我们去看网上下载的Makefile 之后,发现很多不怎么变的文件,都会有.o的形式存在的。另外源代码里面可能还充斥着很多.h的文件。那么这些文件是干什么的。为什么会存在这类文件呢?在回答这类问题之前,首先我要申明,不是非要以.o, .h来命名,只不过约定俗成罢了,.o就是模块文件,也就是不常变动的.c 文件可以先编译成.o作为模块保存下来。 .h文件是给调用者说明的,他一般只包含.c文件的方法的签名即可。说得更接近高级语言一点,就是相当于c#(java)里的interface.

接下来,我就把max.c转成max.o, 将min.c转成min.o保存下来。并且生成两份.h文件出来,最后也提供一份更接近生产的Makefile,注意这个M是要大写的,这也是约定,而且这个约定是不能变的。

gcc -c max.c
gcc -c min.c

然后max.h和min.h文件输入的内容是:

int max(int a, int b);

int min(int a ,int b);

最后的Makefile

#this is a make file for hello.out
hello.out:max.o min.o hello.c
gcc max.o min.o hello.c -o hello.out
max.o:max.c
gcc -c max.c
min.o:min.c
gcc -c min.c

5,  最后,我把我的代码全部贴上来吧。

int max(int a, int b)
{
if(a>b) return a;
return b;
}

max.c

int max(int a, int b)
{
if(a<b) return a;
return b;
}

min.c

#include <stdio.h>
#include "max.h"
#include "min.h" int main(int argc, char* argv[])
{
int a=;
int b=;
int maxNo=max(a,b);
int minNo=min(a,b);
printf("the max number is %d"\nthe min number is %d\n",maxNo,minNo);
return ;
}

hello.c

6,  关于我的第一个linux的C程序就是这样子的,Happy ending, right?

7,  不,这样的结局并不完美。作为一个程序员,我并没有觉得很happy,因为上面并没有讲到如何调试。都是一次过的简单程序,能够一次性将所有程序逻辑(包括复杂的)写出来就真是大牛逼人物了。 静态的编译错误还好说,make阶段就能发现,可是有些逻辑却不是那么容易发现的。程序员的终极武器debug就要祭出了。在linux里面一般用的就是gdb工具。按照惯例看一下我的版本先:

为了调试我上面的那个小程序,我将所有的*.o文件和hello.out先删除掉。

rm -rf *.o
rm -rf hello.out

然后修改Makefile成如下:

#this is a make file for hello.out
hello.out:max.o min.o hello.c
gcc -g max.o min.o hello.c -o hello.out
max.o:max.c
gcc -g -c max.c
min.o:min.c
gcc -g -c min.c

执行make之后。继续输入

gdb ./hello.out

这样就加载了我们hello.out进了内存,而且可以调试了。至于什么是栈内存、堆内存、数据内存,代码内存这些,我就不说了,可以回去翻翻大学教程或者网络上自己搜索一下吧,贴出我的一些简单的调试操作吧:

8, 最后解决一个小问题,就是在上面的图中,我们可以看到其中出现了一个错误说Missing separate debuginfos, use: debuginfo-install glibc-2.17-78.el7.x86_64

其实要解决这个问题,也很简单,直接在bash下面输入

debuginfo-install glibc

前提是我已有/etc/yum.repos.d/CentOS-Debug.repo如下:

# CentOS-Debug.repo
#
# The mirror system uses the connecting IP address of the client and the
# update status of each mirror to pick mirrors that are updated to and
# geographically close to the client. You should use this for CentOS updates
# unless you are manually picking other mirrors.
# # All debug packages from all the various CentOS- releases
# are merged into a single repo, split by BaseArch
#
# Note: packages in the debuginfo repo are currently not signed
# [base-debuginfo]
name=CentOS- - Debuginfo
baseurl=http://debuginfo.centos.org/7/$basearch/
gpgcheck=
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-Debug-7
enabled=
#

CentOS-Debug.repo

如果没有就需要先到http://debuginfo.centos.org/7/x86_64/ 找到与内核完全一样的debuginfo. 例如机器的内核是CentOS Linux 7 (Core) Kenerl 3.10.0-229.el7.x86_64 on an x86_64,  就是在输入用户名的登录时可以看到这个信息。 那么就应该

wget http://debuginfo.centos.org/7/x86_64/kernel-debuginfo-3.10.0-229.el7.x86_64.rpm
wget http://debuginfo.centos.org/7/x86_64/kernel-debuginfo-common-x86_64-3.10.0-229.el7.x86_64.rpm

然后rpm安装下载到的rpm包,不过我认为可能不需要下载这两个应该也是可行的。最后仿照配置yum源并执行

debuginfo-install glibc

就可以解决这个问题。

9, 至于如何使用gdb,网上找到两篇介绍文章

http://www.cnblogs.com/hankers/archive/2012/12/07/2806836.html

http://blog.csdn.net/feixiaoxing/article/details/7199643

CentOS 7 之Helloworld with c的更多相关文章

  1. Helloworld with c

    CentOS 7 之Helloworld with c   其实我也不知道是为了啥, 到了现在这种年纪还想学习Linux下的C语言编程.因为我一直就傻傻地认为机会是垂青有准备的人,也一直呆呆地认为活到 ...

  2. netcore项目在Centos部署:nohup和supervisor方式

    Centos上部署netcore项目 1 准备工作 在Centos上部署netcore应用程序有两种常用方式:nohup和supervisord,这里简单演示一下这两种部署方式. 首先我们写一个简单的 ...

  3. Docker镜像细节

    前言 只有光头才能变强. 文本已收录至我的GitHub仓库,欢迎Star:https://github.com/ZhongFuCheng3y/3y 回顾前面: 为什么需要Docker? Docker入 ...

  4. 【实战】Docker入门实践二:Docker服务基本操作 和 测试Hello World

    操作环境 操作系统:CentOS7.2 内存:1GB CPU:2核 Docker服务常用命令 docker服务操作命令如下 service docker start #启动服务 service doc ...

  5. docker-2-安装

    安装之前确定Centos的相关问题: CentOS Docker 安装 Docker支持以下的CentOS版本: CentOS 7 (64-bit) CentOS 6.5 (64-bit) 或更高的版 ...

  6. docker进阶1-命令补充和容器卷使用

    命令补充 docker信息与帮助 docker version 和 docker info docker --help 查看所有docker命令列表 docker --help run/commit/ ...

  7. docker学习与应用

    概要 众所周知,Docker是当前非常火的虚拟化容器技术,对于它的优势以及使用场景网上的资料非常多,这里我推荐大家去这个网站去详细学习docker. 我这里就写一下作为一名后端开发程序员,自己学习do ...

  8. Docker 基础入门

    Docker是一个开放的平台,将应用和基础设施分隔开来, 方便快速的交付软件.利用docker的提供的方法可以快速的测试和部署代码,显著的减少写代码和部署直接的延迟. Docker 平台(The Do ...

  9. Docker 镜像构建之 Dockerfile

    在 Docker 中创建镜像最常用的方式,就是使用 Dockerfile.Dockerfile 是一个用来构建镜像的文本文件,文本内容包含了一条条构建镜像所需的指令和说明.官方文档:https://d ...

随机推荐

  1. COJ 2004 序列

    传送门:http://oj.cnuschool.org.cn/oj/home/addSolution.htm?problemID=978 试题描述: WZJ的数字游戏又开始了.他写了N个自然数Ai到黑 ...

  2. Linux Kernel ‘kvm_set_memory_region()’函数本地提权漏洞

    漏洞名称: Linux Kernel ‘kvm_set_memory_region()’函数本地提权漏洞 CNNVD编号: CNNVD-201306-343 发布时间: 2013-06-20 更新时间 ...

  3. 【转】android JNI

    原文网址:http://jinguo.iteye.com/blog/696185 Java Native Interface (JNI)标准是java平台的一部分,它允许Java代码和其他语言写的代码 ...

  4. Linux系统下UDP发送和接收广播消息小例子

    // 发送端 #include <iostream> #include <stdio.h> #include <sys/socket.h> #include < ...

  5. HDOJ(HDU) 2139 Calculate the formula(水题,又一个用JavaAC不了的题目)

    Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) 看到这个时间,我懵逼了... 果然,J ...

  6. entityframework分布式事务中遇到的 “与基础事务管理器的通信失败”的解决方法

    首先是ef的多数据库操作实现事务的方法 public int AddDifferenceDB(userinfo1 user1, userinfo user) { ; using (var test2D ...

  7. Swing实现文件选择(目录选择)附导出

    具体生成工具如图: (1) (2) (3) (4) 源码 : example.java package org.qiailin.jframe; import java.awt.Container; i ...

  8. Mysql学生管理系统:表的建立,外键一对多,多对多关系,中间关联表的建立

    学生管理系统 管理员注册/登录/注销 注册班级(班级详细信息) 注册学生信息 查看班级信息/查看老师资料 教师注册/注销  查看教师资料  查看学生资料  根据名称/班级/ 查看学生详细信息--支持模 ...

  9. angularJS 服务三

    路由 一 简介 1.路由机制 为了实现无刷新的视图切换,我们通常会用ajax请求从后台取数据,然后套上HTML模板渲染在页面上,然而ajax的一个致命缺点就是导致浏览器后退按钮失效,尽管我们可以在页面 ...

  10. 三星 note3销售地查询、销售地代码

    查看销售地代码 用KIES验销售地:记下设定-关于-状态里的序号. 手机不要连接电脑,注意是不要连接,打开KIES3,点工具——固件恢复和初始化,输入大写的型号SM-N900或者SM-N9005,确认 ...