每周小结
一、Linux系统下的多文件编译
为什么要多文件编译?
在编译的时候,特别是比较大的程序,若不拆分成多个文件,检查代码错误的过程将会是一个痛苦的过程;因此我们可以把一个完整的程序中的每一个函数分离开来,写成.c文件,最后一起编译和链接。
.h文件中一般是声明,包括:变量声明、宏定义、枚举声明、结构体声明、函数声明等。
.h头文件是对该模块(.c文件)接口的声明,接口包括该模块提供给其他模块调用的外部函数以及外部全局变量。其他模块访问这些外部定义的变量和函数都需要在.h文件中冠以extern关键字声明;模块(.c文件)内的函数和全局变量一般需要在.c文件开头冠以static关键字声明。
所以说永远不要在.h文件中定义变量,但可以声明变量
整体用法:
#ifndef AB_H //如果没有ab.h文件,#define AB_H。如果有,结束定义
#define AB_H //定义ab.h文件
//定义ab.h需要的头文件
#endif
例如:
#ifndef AB_H
#define AB_H
定义程序所需的一些头文件
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
......
#endif
.c文件的书写
在写每个函数之前要加载头文件,如果是.h文件,就需要写#include”ab.h”。
例如:引用之前的ab.h文件书写aa.c命名的如下文件
#include”ab.h”
int numadd(int a,int b)
{
return a+b;
}
主函数(add.c):
#include”ab.h”
int main(int argc, char const* argv[])
{
int sum;
sum = numadd();
printf("%d",sum);
}
保存退出。
程序编辑完成之后,需要编译链接。
我们可以用gcc编译每个.c文件。如果有三个.c文件add.c、ab.c,编译方法如下:
gcc -c add.c -o add.o //首先分别编译成.o文件
gcc -c ab.c -o ab.o
gcc add.o ab.o -o all //将.o文件编译成一个可执行文件
./all //执行程序
二、static、extern关键字
1.static
(1)针对于声明周期,使其声明的变量存放到静态区,也就是给变量续命;
无static时代码如下(示例):
#include <stdio.h>
void print_out()
{
int a = 10;
a++;
printf("a=%d,", a);
}
int main(int argc, char* argv[])
{
for (int i = 0; i < 10; i++)
{
print_out();
}
printf("\n");
return 0;
}
其运行结果为:
zpr@ubuntu:~/bace$ ./a.out
a=11,a=11,a=11,a=11,a=11,a=11,a=11,a=11,a=11,a=11,
有static时代码如下(示例):
#include <stdio.h>
void print_out()
{
static int a = 10;
a++;
printf("a=%d,", a);
}
int main(int argc, char* argv[])
{
for (int i = 0; i < 10; i++)
{
print_out();
}
printf("\n");
return 0;
}
其运行结果为:
zpr@ubuntu:~/bace$ ./a.out
a=11,a=12,a=13,a=14,a=15,a=16,a=17,a=18,a=19,a=20,
(2)static针对于作用域,其表示限定作用域,在某.c文件中声明的函数,添加后只能适用于当前的.c文件。
无static时代码如下(示例):
1.c文件:
#include <stdio.h>
char a = 'B';
void print()
{
printf("hello\n");
}
2.c文件:
#include <stdio.h>
void print();
int main(int argc, char const* argv[])
{
extern char a;
printf("%c ", a);
print();
return 0;
}
其运行结果为:
zpr@ubuntu:~/bace$ gcc -c 1.c -o 1.o
zpr@ubuntu:~/bace$ gcc -c 2.c -o 2.o
zpr@ubuntu:~/bace$ gcc 1.o 2.o -o all
zpr@ubuntu:~/bace$ ./all
B hello
有static时代码如下(示例):
1.c文件:
#include <stdio.h>
char a = 'B';
static void print()
{
printf("hello\n");
}
2.c文件:
#include <stdio.h>
void print();
int main(int argc, char const* argv[])
{
extern char a;
printf("%c ", a);
print();
return 0;
}
其运行结果为:
zpr@ubuntu:~/bace$ gcc -c 1.c -o 1.o
zpr@ubuntu:~/bace$ gcc -c 2.c -o 2.o
zpr@ubuntu:~/bace$ gcc 1.o 2.o -o all
/usr/bin/ld: 2.o: in function `main':
2.c:(.text+0x36): undefined reference to `print'
collect2: error: ld returned 1 exit status
2.extern
1、函数的声明extern关键词是可有可无的,因为函数本身不加修饰的话就是extern。但是引用的时候一样需要声明的。
2、全局变量在外部使用声明时,extern关键字是必须的,如果变量没有extern修饰且没有显式的初始化,同样成为变量的定义,因此此时必须加extern,而编译器在此标记存储空间在执行时加载内并初始化为0。而局部变量的声明不能有extern的修饰,且局部变量在运行时才在堆栈部分分配内存。
3、全局变量或函数本质上讲没有区别,函数名是指向函数二进制块开头处的指针。而全局变量是在函数外部声明的变量。函数名也在函数外,因此函数也是全局的。
4、谨记:声明可以多次,定义只能一次。
5、extern int i; //声明,不是定义
int i; //声明,也是定义
三、strlen与sizeof的区别
strlen 是一个函数,它用来计算指定字符串 str 的长度,但不包括结束字符(即 null 字符)。
原型:
size_t strlen(char const* str);
函数用法:计算给定字符串的(unsigned int型)长度,不包括’\0’在内。
因为函数 strlen 的返回结果是 size_t 类型(即无符号整型),而 size_t 类型绝不可能是负的。
关键字 sizeof 是一个单目运算符,而不是一个函数。与函数 strlen 不同,它的参数可以是数组、指针、类型、对象、函数等。
strlen的结果要在运行的时候才能计算出来,是用来计算字符串的长度,不是类型占内存的大小
四、printf("%d,%d,%d,%d",a++ ,++a )的编译问题
1、 printf函数对于括号内的表达式是从右向左执行,如 printf("%d,%d",b++,++b);先执行 ++b,再执行b++;
对于++b的输出,不管它放在printf的哪个位置,调用时使用的都是b的最终值,即4轮自增操作以后的值
而b++则在每一步计算时先把b保存到新寄存器,再把b+1后的结果赋给b
2、对于++a和a++:
++a是先自加,再调用
a++是先调用,再自加
例子:
#include <stdio.h>
int main()
{
int a=8;
printf("%d %d %d %d\n",a++,++a,a--,--a);
printf("%d %d %d %d\n",++a,a++,a--,--a);
printf("%d %d %d %d\n",++a,a--,a++,--a);
printf("%d %d %d %d\n",++a,a--,--a,a++);
return 0;
}
zpr@ubuntu:~/bace$ ./a.out
7 8 7 8
8 6 7 8
8 8 7 8
8 8 8 8
总结
重在坚持!