名称解释
- ANSI C
C语言标准,为各种操作系统上的C程序提供可移植性的保证 - POSIX标准
定义了兼容操作系统的C语言系统接口以及工具标准 - 库函数
库函数完成常见的特定功能,被应用程序调用 - 系统调用
系统调用函数与操作系统相关,不同的操作系统使用的系统调用可能不同。库函数中也可以使用系统调用
第一个程序
写一个程序first.c
#include <stdio.h>
int main(int argc,char **argv)
{
printf("this is first proram.\n");
return 0;
}
使用gcc进行编译,链接
$ gcc first.c
运行一下看结果
$ ./a.out
调用函数
写一个阶乘的函数factorial.c
#include <stdio.h>
#include <stdlib.h>
int factorial(int n)
{
if(n <= 1) return 1;
else
return factorial (n-1) * n;
}
写一个调用文件second.c
#include <stdio.h>
#include <stdlib.h>
int factorial(int n);
int main(int argc, char **argv)
{
int n;
if(argc < 2){
printf("Usage: %s \n", argv[0]);
return -1;
}else{
n = atoi(argv[1]);
printf("Factorial of %d is %d . \n",n,factorial(n));
return 0;
}
}
编译和调用方法
$ gcc -c factorial.c
$ gcc -c second.c
$ gcc -o second factorial.o second.o
$ ./second 6
Make工具的使用
刚才我们看到了,如果我们的程序有多个文件,我们需要编译,链接才能正常调用程序。这个过程是很繁琐的,我们可以使用make工具来解决这个问题。
make工具会调用makefile文件,我们先写一个makefile看看
Makefile
second : second.o factorial.o
gcc -o second second.o factorial.o
second.o : second.c
gcc -c second.c
factorial.o : factorial.c
gcc -c factorial.c
clean :
rm -f *.o
rm -f second
如果发现make有如下错误:
Makefile:2: *** 遗漏分隔符 。 停止。
可能是执行文件开头使用了空格,应该使用TAB。
Makefile中的常用变量
$@--目标文件
$^--所有依赖的文件
@<--第一个依赖文件
main: main.o mytool1.o mytool2.o
cc -o $@ $^
main.o: main.c mytool1.h mytool2.h
cc -c $<
mytool1.o: mytool1.c mytool1.h
cc -c $<
mytool2.o: mytool2.c mytool2.h
cc -c $<
库文件的使用
上面的例子中我们使用了printf函数,这个函数的实现是在库文件中。我们连接自己的应用程序的时候,编译器会查找对应函数的连接位置,运行时在当前系统内存空间中查找该库函数在对应库文件中的位置。
Linux系统下有两种库文件:
- 静态库:.a为后缀,应用程序从静态库中复制函数到二进制文件中
- 共享库:.so为后缀,应用程序运行时将函数代码从共享库文件中读出,从而间接引用。
系统库的路径:
# 系统必备共享库
/lib
# 标准共享库和静态库
/usr/lib
# 本地函数库
/usr/local/lib
库文件的搜索路径为:
- 环境变量:LD_LIBRARY_PATH 所指定的位置
- 搜索动态加载器在/etc 目录下的缓存文件 /etc/ld.so.cache
在进行cc编译的时候,会自动链接一些常用的库,这就是为什么printf不需要指定链接库的原因。如果我们需要指定库的路径可以使用如下方法:
cc -o temp temp.c -L/home/hutou/myLib
系统缺省库位置:/lib,/usr/lib,/usr/local/lib
静态库的使用
- 创建静态库:主要使用ar命令
创建hello.c,hello.h 文件
#ifndef _libhello_H_
#define _libhello_H_
void hello(void);
#endif
#include <stdio.h>
#include "hello.h"
void hello(void){
printf("this is a static lib.\n");
}
编译生成静态库hello.a
$ gcc -c hello.c
$ ar rc hello.a hello.o
- 使用静态库
使用静态库需要两个文件:头文件,静态库文件
默认库文件如果不在库搜索路径中,需要将此库文件拷贝到当前目录,或者在编译时指定库文件路径
书写一个调用静态库的文件 libuse.c
#include <stdio.h>
#include "hello.h"
int main(int argc, char **argv){
hello();
return 0;
}
编译链接
$ gcc -o useHello libuse.c hello.a
共享库的使用
共享库的创建基本上与静态库相同,主要的差别体现在编译上
- 使用gcc和参数-fPIC将源代码编译成.o的目标代码
- 使用-shared来创建共享库
下面我们使用上面的hello.c,编译成共享库
# 生成.o的目标文件
gcc -fPIC -c hello.c
# 编译共享库,指定共享库名称和版本,-lc表示引用c库
gcc -shared -o libhello.so.1.0 hello.o -lc
# 创建软连接
ln -sf libhello.so.1.0 libhello.so
下面我们看看怎么使用共享库
# 编译程序,生成目标文件
gcc -c libuse.c -o libuse.o
# 连接程序,-L 表示查找库的路径
gcc -o useDynHello libuse.o -L ./ -lhello
# 通过ldd命令查看引用的动态库
ldd useDynHello
# 调用程序,需要指定共享库的位置
LD_LIBRARY_PATH=$(pwd) ./useDynHello
说明一下,还记得我们说过的库文件的搜索路径。我们在链接程序的时候,指定了 -L 的路径,如果将我们的共享库放置在可以搜索到的路径下,则不用指定此参数。
进程
程序:包含可执行代码的文件,是一个静态文件。
进程:一个开始执行但是还没有结束的程序实例。
程序被系统调入内存,系统给程序分配一定的资源,让程序变成进程,为了区分进程,系统会给每一个进程分配一个ID号。进程有新建,运行,阻塞,就绪,完成五个状态。
- 获得进程ID号
#include <unist.h>
pid_t getpid(void); // 获得进程的ID
pid_t getppid(void); // 获得父进程的ID
演示用程序
#include <unistd.h>
#include <stdio.h>
int main(int argc,char *argv){
printf("process id = %d\n",getpid());
printf("process parent id = %d\n", getppid());
}
- 运行中出现:段错误(吐核)
使用如下的命令进行调试(pid是运行的程序)
strace ./pid
- 进程的所有者和执行者
#include <stdio.h>
#include <sys/types.h>
int main(int argc, char const *argv[])
{
printf("这是程序的运行者%d\n", getuid());
printf("这是程序的所有者%d\n", geteuid());
printf("这是组ID =%s\n", getgid());
printf("这是组EID =%s\n", getegid());
return 0;
}
- 登录用信息
#include <stdio.h>
#include <pwd.h>
int main(int argc, char const *argv[])
{
struct passwd* my_info;
my_info = getpwuid(getuid());
if(my_info)
{
printf("我的登录名:%s\n", my_info->pw_name);
printf("我的密码:%s\n", my_info->pw_passwd);
printf("我的ID= %ld\n", my_info->pw_uid);
printf("我的组ID= %ld\n", my_info->pw_gid );
printf("我的真实名称:%s\n", my_info->pw_gecos);
printf("我的Home目录:%s\n", my_info->pw_dir);
printf("我的Sheel:%s\n", my_info->pw_shell);
}
return 0;
}