C语言基础(Linux)
Linux
vim快捷键
- shift+k:进入函数内部 ;qq返回
- `shift+n:提示功能
- Xshell中Ctrl+Tab切换窗口
-
- tabo:关闭除本窗口外关闭其他
- tabc:关闭当前的
- gt:切换窗口
- wqa:退出所有窗口。
-
-
关于swp文件 使用vi,经常可以看到swp这个文件,那这个文件是怎么产生的呢,当你打开一个文件,vi就会生成这么一个.(filename)swp文件 以备不测(不测下面讨论),如果你正常退出,那么这个这个swp文件将会自动删除 。下面说不测。 不测分为: 1当你用多个程序编辑同一个文件时。 2非常规退出时。 第一种情况的话,为了避免同一个文件产生两个不同的版本(vim中的原话),还是建议选择readonly为好。 第二种情况的话,你可以用vim -r filename恢复,然后再把swp文件删除(这个时候要确保你的swp文件没有用处了,要不然你会伤心的) swp文件的来历,当你强行关闭vi时,比如电源突然断掉或者你使用了Ctrl+ZZ,vi自动生成一个.swp文件,下次你再编辑时,就会出现一些提示。 原文链接:https://blog.csdn.net/shishu8385/article/details/86629398 - 我出现上述问题是打开了多个窗口对同一个文件进行编写
-
makefile编写
-
二、工程的组成
一般而言,基本工程
复杂版工程日录
1.主函数和功能函数源文件*.c
2.功能函数的头文件头文件*.h宏定义结构体函数声明变量共用体
3.库文件日录库文件.s。.a(包装函数)
4.执行文件日录二进制文件编译生成的程序
5.makefile工程管理文件 -
简单版
1.功能函数fun.c
2.主函数文件main.c
3.功能函数头文件fun.h
4.makefile
编译:gcc fun.cmain.c-o main
gcc*.c-o main -
三、如何书写makefile规则
1. -
目标文件一定要存在,依赖文件可以不要
-
maketarget: echo "hello world!"
输出:
echo “hello world!”
hello world!
注意:删除执行make输出的makefile文件代码–进行如下改写:
target:
@echo “hello world!” - 小的项目
-
vi * -p
-
add.h #ifndef _ADD_H__ #define _ADD_H__ #include<stdio.h> int add(int a,int b); #endif -
main.c #include "add.h" int main() { int result; result = add(1,2); printf("1+2 = %d\n",result); return 0; } -
main.c #include<stdio.h> int add(int a,int b) { printf("我正在做加法__________"); return a+b; } -
makefile main:main.c add.c gcc main.c add.c -o main -
当有多个gcc编译时
-
main_add:main_add.c add.c gcc main_add.c add.c -o main_add main_max:main_max.c max.c gcc main_max.c add.c -o main_max - make只执行第一个gcc
- mkae main_max:执行第二个
-
-
删除目标
-
main_add:main_add.c add.c gcc main_add.c add.c -o main_add main_max:main_max.c max.c gcc main_max.c add.c -o main_max clean: rm main_add main_max - make clean
-
-
mytool:main.o tool1.o tool2.o
gcc main.o tool1.o tool2.o -o mytool
main.o:main.c
gcc main.c -c -Wall -g -o main.o
tooll.o:tooll.c
gcc tooll.c -c -Wall -g -o tool1.o
tool2.o:tool2.c
gcc tool2.c -c -Wall -g -o tool2.o -
make
Git(Linux)
- 首先创建一个版本库
git init
初始化空的 Git 版本库于 /root/My_Repository_Git/.git/ - 使用
- 一些修改文件的操作~~~~~~~~~~~ vim code.txt
- 使用下面两条命令可以创建一个版本
- git add code.txt
- git commit -m “版本1”
- 查看版本记录
git log
git log --pretty=oneline:简短显示。 - 版本回退(有一个指针HEAD执行目前的版本)
- 回退到版本1:git reset --hard HEAD^
- 回退到版本2:git reset --hard HEAD^^
- 回到前面的:git reset --hard 版本号(前面几位就好了)
- 查看之前的版本号:git reflog
丢弃修改:- 撤销修改(工作区的未被提交到暂存区)
git checkout – 文件名 :可以撤销对工作区1的改造(也就是为加入暂存区之前的修改) - 撤销修改到暂存区的内容
git reset HEAD 文件名:取消暂存区的修改。
git checkout – 文件名:取消工作区的修改。 - 撤销版本(已经提交版本了)
- 执行版本回退
分支:
- 查看分支
git branch - 切换分支:
git checkout dev - 创建分支但不切换
git branchdev - 创建分支,并且切换到新分支:dev
git checkout -b dev - 合并分支
git merge dev - 删除分支(合并之后就可以删除原来的分支了)
git merge -d dev
解决冲突
C语言原则
- 不能有警告,除非该警告是你知道对于程序无害的。
- 以函数为单位解决为题
- 生命部分 + 实现部分
调试规则
- printf(“[%s:%d]:”,杠杆FUNCTION杠杆,杠杆LINE杠杆);
基础
引例
- c语言不支持二进制数
- c源文件-预处理-编译-汇编-链接-可执行文件
- -E:gcc中的预处理(处理#行 ) gcc -E hello.c > hello.i产生.o文件
- -S:gcc中的编译`gcc -S hello.o 产生hello.i
- ‘-C’:gcc中的汇编;gcc -c hello.i产生hell.s
- -o:gcc中的链接。 gcc -o hello.s -o hello产生hello
- gcc hello.c -Wall:打印出程序中的所有警告
- 在C语言中如果程序中没有原型,但是在程序中使用了该函数,则默认该函数的返回值是int型。
- exit定义在<stdlib.h>中
-
#include<stdio.h> 2 #include<stdlib.h> 3 #include<errno.h> 6 int main(void) 7 { 8 9 FILE *fp; 10 11 fp = fopen("tmp","r"); 12 if(fp == NULL) 13 { 14 15 fprintf(stderr,"fopen():%s\n", strerror(errno)); 16 exit(1); 17 } 18 19 puts("OK!"); 20 exit(0); 21 } //在gcc hello.c -o 没有错误 //./a.out报错 -------- 段错误(吐核) //hello.c:15:4: 警告:格式 ‘%s’ expects argument of type ‘char *’, but argument 3 has type ‘int’ [-Wformat=]------------------这个应该其实是一个错误警告(下面分析) /* 因为 char *strerror(int errnum);包含在<string.h> 但是没有包含这个文件,那么程序默认其返回int */-
int main(){ printf("hello"); //return 0; //在linux中使用echo $? 返回5 } -
注释:
-
# if 0 #endif#ifndef PROJ_H___ #define PROJ_H___ void func(void); #endif
-
-
变量
-
c标准本身没有定义数据类型大小,只是定义了一个大小关系。
-
char ch:ch还是未定义性的(即是有符号还是无符号的c是未定义的)
-
特殊性
- bool
- float类型:不能精确匹配
- char型是否有符号
. 不同形式的0: 0 ‘0’ “0” ‘\0’ NULL - 数据类型与后续代码中所使用的输入输出要相互匹配,防止自相矛盾。
- %d表示输入或输出十进制有符号数.
-
常量与变量
- 常量
- 整型常量:1/2 = 0; 1.0/2=0.5
- 实型常量
- 字符常量:
- 由单引号引起来的单个的字符或者转义字符.如:‘a’,
- ‘\aaa’:表示8进制数(‘\018是错误的’)–三位八进制数,
- ‘\xhh’:表示十六进制数--------------------二位两进制数。
- 由单引号引起来的单个的字符或者转义字符.如:‘a’,
- 字符串常量
- 双引号引起来的常量
- 空串:“”
- 标识常量:#define,只是单纯的预处理阶段,一改全改
-
int a = 1,b = 2; #define MAX ((a),(b)) ((a)>(b)?(a):(b)) printf("MAX = "a++,b++) a = 3,b = 4;//b自增了的二次 预处理结果:gcc -E hello.c printf("mox = %d\n",( a++ > b++ ? a++ : b++ )); -
上面的修改
#define MAX(a,b) ({ int i=a,j=b; ( (i) > (j) ? (i) : (j) ); })
gcc -E hello.c
printf(“mox = %d\n”,({ int i=a++,j=b++; ( (i) > (j) ? (i) : (j) ); }));
#define MAX(a,b) ({ typeof(a) i=a,j=b; ( (i) > (j) ? (i) : (j) ); })
gcc -E hell -
不检查语法.只是单纯的宏名和宏体的替换。不能出现赋值的左侧。
-
- 变量:值随时会变化。
- 存储类型
- auto:默认,自动分配空间,自动回收空间
- static:静态型变量,自动初始化为0值或者空值.而且其值具有继承性。列如func()调用三次。另外,常用于修饰变量或函数(当定义一个全局变量时,一般定义使用static)
- 1)全局变量和全局静态变量的区别:
1)全局变量是不显式用 static 修饰的全局变量,全局变量默认是有外部链接性的,作用域是整个工程,在一个文件内定义的全局变量,在另一个文件中,通过 extern 全局变量名的声明,就可以使用全局变量。
2)全局静态变量是显式用 static 修饰的全局变量,作用域是声明此变量所在的文件,其他的文件即使用 extern 声明也不能使用。修饰函数:防止函数扩展文件,修饰变量:防止变量超过文件
- 1)全局变量和全局静态变量的区别:
- register:(建议型,inline也是建议型关键字) 寄存器类型,快~~~,
-
register int i = 1; //只是一个建议编译器。 - 只能定义局部变量,不能全局变量。
- 大小有限制,只能定义32位大小的数据类型。如double就不可以。
- 寄存器没有地址,所以一个寄存器型的变量无法打印出地址进行查看和使用
-
- extern(说明型上面三个是定义型关键字),意味着不能改变被说明的变量的值或者类型。
-
如果全局变量不在文件的开头定义,其有效的范围为其定义处到文件结束。如果想在定义点之前的函数引用该全局变量,则要在引用之前用关键字extern对该变量作“外部变量声明”,即表示该变量是一个已经定义的外部变量。这样就可以从声明处其,使用该外部变量。
#include <stdio.h>
int max(int a,int b);int main(void){ int c; extern int x_a; extern int x_b; c = max(x_a,x_b); printf ("max %d\n",c); return 0; } int x_a = 1; int x_b = 2; int max(int a,int b){ return (a>b? a:b); } -
如果一个工程由多个源文件组成,在一个源文件中想引用另一个源文件中已定义的外部变量,也需要在引用变量的源文件中用extern关键字声明变量,如下代码所示:
/*第一个源文件/
#include <stdio.h>
extern int a;
extern int b;
int max(){
return (a>b? a:b);
}
/*第二个源文件/
#include <stdio.h>
int a = 1;
int b = 2;
int max();
int main(void){
int c;
c = max();
printf(“max %d\n”,c);
return 0;
}
-
- 存储类型
- 变量的生命周期和作业范围
- 全局变量和局部变量
- 局部变量和局部变量
- 常量
-
运算符和表达式
-
表达式与语句的区别
- i=1为表达式,i=1;为语句(后面有无分号)
运算符
- 操作数个数
- 结和型
- 优先级
- 运算符的特殊性
- %两个操作数必须是整形: 1.0%2错误
- 3++;错误
- i = 1 ; i++;表达式的值为1,i=2
- 位运算的重要性
- 取反:~i
- <<和>>
- &和|
- ^
- 将操作数中的第n位置1:其他不变 num = num | 1<<n(1左移位)
- 将操作数中的第n位置1其他不变:num = num & ~(1 << n)(1左移n位后取反)
- 测试指定位:if(num & 1<<n){为1为真}
- 从某个数中取出其中的几位:
2.输入,输出专题
-
格式化的输入输出:scanf(),printf()
-
printf()
int printf(const char *format, …);
输出格式是字母没有数字,特别注意八进制无符号:%o而不是%0(不是0),表示的时候是 012(八进制数)
例如:使用宏定义一年有多少秒:#define YEAR_SECONDD (24LL60LL60LL*365LL):防止溢出
- \nd的作用:刷新缓冲区
int main()
{
printf(“[%s:%d]:before:while()\n”,FUNCTION,LINE);
//上面如果没有/n,则什么也输不出。
while(1); //放置sleep(2);也会出现这种情况。
printf(“[%s:%d]:after:while()”,FUNCTION,LINE);
}
缓冲区刷新的条件:printf()函数的缓冲区大小为1024个字节,当超出缓冲区大小时缓冲区就会被刷新。
1.进程结束。
2.遇到\n。
3.缓冲区满。
4.手动刷新缓冲区fflush(stdout)。
5.调用exit(0);但是还可以调用_exit(0),不刷新缓冲区。
2.scanf():
-
%s
- 使用%s输入字符串:中间不能有空格(遇空格结束)
- %s危险易造成内存泄漏:,因为不知道存储空间大小。
char str[3];
scanf(“%s”,str);
//输入 “123456”
输出123456
-
scanf返回正确输入的个数(这也是判断scanf是否输入正确的标志)
-
scanf:放入循环非常危险
-
int i ; while(1){ scanf("%d",&i); printf("i = %d\n",i);}
//当输入字符时,导致陷入无限循环
int i ,ret; while(1){ ret = scanf("%d",&i); if(ret != 1){ break; } printf("i = %d\n",i);}
-
scanf吃掉空格或者回车
-
char a,b; scanf("%c",&a); scanf("%*c%c",&b); printf("a =_____%c______,b =____ %c_____\n",a,b); //%d%*d%d,意思是将第二个数值不存入变量,相当于跳过去这个数值
-
-
-
char a,b; scanf("%c",&a); getchar() scanf("%c",&b); printf("a =_____%c______,b =____ %c_____\n",a,b);
2.字符输入输出函数:getchar(),putchar()
3.字符输入输出函数:gets(!),puts()
- gets会溢出缓存区。(极度危险)可以使用getline()和fgets()来替代
- 这也许是你第一次碰到snprintf函数。许多现有代码调用的是sprintf,但sprintf不检查目标缓冲区是否溢出,相反,snprintf要求其第二个参数是目标缓冲区的大小,因此可确保缓冲区不溢出。snprintf不是标准ANSIC的一都分,但这个标准的修订版C9X正在考虑。不过,许多厂商提供的标准C库含有这个函数。我们在本书里使用snprintf,如果你的系统不提供这个函数,可用我们提供的通过调用sprintf实现的版本。
值得注意的是,许多网络入侵是由黑客通过发送数据,导致服务器对sprintf的调用溢出其缓冲区而发生的。你必须小心使用的函数还有gets、strcat和strcpy,透常应调用fgets、strncat 和 strncpy 函数代替
3.流程控制
顺序
选择
switch–case后面跟的是常量表达式(程序中不能改变的量).
if-goto:(慎用:goto实现的是无条件的跳转,且不能跨函数跳转.)
循环
4.数组(是构造类型)
-
一维数组
-
int arr[3]不初始化为乱码
-
static int arr[3]:未被初始化默认为0
-
%p是地址。
-
int arr[3]={1,2,3} arr = {1,2,3};//ERROR,因为arr表示地址常量,也是数组的起始位置 arr[4] = 12 //虽然越界了但是不会报错。
2.二位数组
- int arr[][3] = {1,2,3,4}:行号可以省略(但是后面必须要有赋值)
- arr[2][2]由一维数组arr[0],arr[1]组成 arr+1 跨一列:跨到arr[1][0]
3.字符数组
char str[2] = {‘1’,‘2’}
char str[2] = “1”;两个初始化方式差别,str后面有’/0’(后面这种命名,且后面这种必须预留一位不然就会内存泄漏)
常用函数:
- strlen&sizeof:
- strlen:不包含’/0’ : strlen(“hello\0abc”) = 5(以尾0来结尾来计算)
- sizeof:包含’/0‘:真正占的字节个数(是个数也是大小)
- strcpy&strncpy:
-
char str[] = "hello\0abc"; //如果想改变不能 str = "123"; //想改变 strcpy(str,"123"); -
char *strcpy(char *dest, const char *src); //拷贝以尾0结束,包括尾0 char str[3] = "ab"; puts(str); strcpy(str,"abcdef"); //这个居然过了 puts(str); exit(0); char *strncpy(char *dest, const char *src, size_t n); strncpy(str,"1bcde",strlen(str))
-
- strcat&strncat:
- strcmp&strncmp:
4.数组名
5.数组越界
指针(大小均为4B)
-
变量与地址
-
指针和指针变量
1.
int i = 1;
int * p = &i
int ** q = &p;i &i 1 i的地址 ----- p &p *p &i p的地址 i ----- q &q *q **q &p q的地址 *q=*(&p)=p *(*q)=*(p)=i i TYPE name = vale; name &name *name name里面的内容 name的地址 成为name中的内容 -
直接访问和间接访问
-
空指针与野指针
-
空类型(void *)
-
初始化的书写规则
-
指针的运算
&,*,关系运算(+),++,– -
指针与数组
-
指针与一维数组
- 数组个数 length = sizeof(a)/sizeof(a*)
- int *p = (int [3]){1,2,3},定义了一个数组,只不过该数组没有名字。
-
指针与二维数组
- int a[2][3]; a是一个行指针,*a列指针
. inta[2][3]; int *p; p = a,&a[0][0],(a+0) ----只能p[0]->p[6]这样一个一个访问
. int a[2][3]; int (*p)[3] = a - a[i][j] = ((a+i)+j)
- int a[2][3]; a是一个行指针,*a列指针
-
指针与字符数组
char a[] =“123”;
char *p = a+1;
puts(a) //123
puts§ //23//a = "456"; ---------使用strcpy() char *p=a; p = "456";//可以 //strcpy(p,"456") ---------error 你居然想修改123?
-
-
const与指针
const float pi = 3.14:一旦定义必须初始化;pi = 12;错误
const float pi = 3.14;
float *p = π //*pi = 12;就会改变pi的值,----非常危险
const float *p = π //应该这样定义指针常量(指针所指向的内容能变化但是指针的指向不能变化)
- int * const p
常量指针(指针指向可以变化但是指针所指向的内容不能变化) - const int *p
- int const *p
int i = 10;
const int *p = &i;
i = 11; //right
*p = 11;//error
const int * const p ;//都不改变
- int * const p
-
指针数组与数组指针
- 数组指针(指向数组的指针):int (*p)[3] ; ------ 也就是行指针
- 指针数组:存放数组的指针:int *p[3]
-
多级指针
函数
- return 0:只结束当前函数,不一定结束当前程序
- exit(0):无论处在何处只要一调用程序就结束
- 一维数组与函数:传递数组起始地址和数组长度
函数与指针
指针函数
- 返回值 * 函数名 (形参)
-
int * fun(int); int * find_num(int (*p)[N],int num) { if(num > M-1){ return NULL; } return *(p+num); //返回第p行的首地址 }
-
函数指针
类型 (*指针名) (形参);
int (*p)(int)
int add(int a,int b)
{
return a+b;
}
int main(void)
{
int a = 3,b = 5;
int (*ret)(int,int);
ret = add;
printf("%d\n",ret(3,5));
}
函数指针数组
类型 (*数组名) [下标] (形参)
int (*arr[2])(int,int);
int add(int a,int b)
{
return a+b;
}
int sub(int a,int b)
{
return a-b;
}
int main(void)
{
int a = 3,b = 5;
int (*ret[2])(int,int) = {add,sub};
printf("+++%d \n",ret[0](1,2));
printf("---%d \n",ret[1](1,2));
return 0;
}
构造类型
结构体
- 嵌套定义:
-
struct birthday_st { int year; int month; int day; }; struct student_st { int id; struct birthday_st birth; char name[NAMESIZE]; int math; int chinese; }; -
struct student_st { int id; struct birthday_st { int year; int month; int day; } birth; char name[NAMESIZE]; int math; int chinese; };
-
- 赋值
-
struct student_st st = {.id = 12,.birth.year=20111}; struct student_st st = {11,{2012,12,1},"student",90,,91}; -
struct student_st st = {.id = 12,.birth.year=20111}; struct student_st *p = &st; p->id = 12; (*p).id = 12; - 内存空间的计算:
-
1. __attribute__ ((packed)) 的作用就是告诉编译器取消结构在编译过程中的优化对齐,按照实际占用字节数进行对齐,是GCC特有的语法。这个功能是跟操作系统没关系,跟编译器有关,gcc编译器不是紧凑模式的,我在windows下,用vc的编译器也不是紧凑的,用tc的编译器就是紧凑的。例如: 在TC下:struct my{ char ch; int a;} sizeof(int)=2;sizeof(my)=3;(紧凑模式) 在GCC下:struct my{ char ch; int a;} sizeof(int)=4;sizeof(my)=8;(非紧凑模式) 在GCC下:struct my{ char ch; int a;}__attrubte__ ((packed)) sizeof(int)=4;sizeof(my)=5
-
-
- 函数传参:
- fun(struct student st); fun(st); 传参非常大(相当于多值传参): 应该使用指针传参
共用体
-
产生及其意义
-
union 共用体名{ };
-
-
类型描述
-
嵌套使用
-
#include<stdio.h> 2 #include<stdlib.h> 3 #include<stdint.h> 4 union test_un{ 5 6 struct 7 { 8 uint16_t i; 9 uint16_t j; 10 }x; 11 uint32_t y; 12 }a; 13 int main(void) 14 { 15 a.y = 0x11223344; 16 printf("%#x,%#x\n",a.x.i,a.x.j); 17 return 0; 18 } -
#include<stdio.h> 2 #include<stdlib.h> 3 #include<stdint.h> 4 union test_un{ 5 6 struct 7 { 8 char a:1; 9 char b:2; 10 char c:1; 11 }x; 12 char y; 13 }a; 14 int main(void) 15 { 16 a.y=4; 17 18 printf("%d\n",a.x.b); 19 return 0; 20 //0000 0100s 21 }
-
-
定义变量,初始化,成员引用
-
内存空间大小
-
函数传参(值,地址)
-
位域
枚举类型
-
5 enum day 6 { 7 8 MON, 9 TUS 10 }; 11 int main(void) 12 { 13 enum day a = TUS; 14 return 0; 15 16 }-
5 enum day 6 { 7 8 MON = 1, 9 TUS 10 }; 11 int main(void) 12 { 13 enum day a = TUS; 14 return 0; 15 16 }
-
-
5 //当做宏使用 enum 6 { 8 MON = 1, 9 TUS = 2 10 }; 11 int main(void) 12 { int state = 1= MON; 13 switch state 14 return 0; 15 16 }动态内存管理
-
malloc
-
void *malloc(size_t size); -
int * p = NULL; p = malloc(sizeof(int)); free(p);
-
-
-
#include<stdio.h> #include<stdlib.h> void func(int **p,int num) { if(*p == NULL) { printf("malloc is null"); } *p = malloc(sizeof(int)*num); if(*p!=NULL) { printf("malloc is successs!\n"); } return; } int main(void) { int *p = NULL; int num = 100; func(&p,num); if(p==NULL) { printf("main p is null"); }else{ printf("main p is not null"); } free(p); return 0; } -
#include<stdio.h> #include<stdlib.h> void * func(int *p,int num) { if(*p == NULL) { printf("malloc is null"); } *p = malloc(sizeof(int)*num); if(*p!=NULL) { printf("malloc is successs!\n"); } return; } int main(void) { int *p = NULL; int num = 100; p = func(p,num); if(p==NULL) { printf("main p is null"); }else{ printf("main p is not null"); } return 0; } -
calloc
- void *calloc(size_t nmemb, size_t size);
-
realloc
- void *realloc(void *ptr, size_t size);
-
free
- void free(void *ptr);
- free一个空指针不报错。
关键字
typedef
-
typedef 已有的数据类型 新名字;
-
typedef int INT; int main(void) { INT i = 100; printf("%d\n",i); return 0; } -
不可以使用define定义
-
#define IP int *; IP a,b; // ===== int *p,*q;---error
-
-
typedef int ARR[6];定义了一个一维数组的类型。
-
typedef struct { int i; int j; }Node,Node*; ---------------------上下等价;//不建议对指针进行重命名,不容易观察。 struct Not { int i; int j; }; typedef struct Not Node; typedef struct Not Node*; -
typedef int fun(int); fun func; //===int func(int)
const与define
- 有些集成化的调试工具可以对const常量进行调试,但是不能对宏常量进行调试
- 有无类型检查
- 定义常量的两种方式const,define
- 一个简单替换,一个类型检查
- 处理阶段不同 :一个预处理阶段一个编译阶段
- const定义的只读变量在程序运行过程中只有一份拷贝(因为它是全局的只读变量,存放在静态区),而#define定义的宏常量在内存中有若干个拷贝
- const定义的只读变量从汇编的角度来看,只是给出了对应的内存地址,而不是像#define一样给出的是立即数,所以const定义的只读变量在程序运行过程中只有一份拷贝。
extern和include
- 首先要搞清楚的是.h头文件中都是一些声明性的语句,是不分配内存的,所以头文件中有对函数的声明,有define语句,有没有实例化的结构体定义,但是没有对变量的定义(比如 int a),有的只是对外变量的声明(extern int a)。 因为全局变量一般定义在原文件.c中(假设是a.c),对于一个文件调用另一个文件的全局变量,我们不能用#include包含源文件而只能包含头文件,所以常用的方法是用extern int a来声明外部变量。 还有一种方法是可以是在a.c文件中定义了全局变量int global_num ,可以在a.h头文件中写extern int global_num 这样其他源文件可以通过include a.h来声明她是外部变量就可以了. 对于一个文件调用另一个文件中的函数,最常用的是在.h头文件中配上extern声明函数,其他源文件可以通过#include该头文件来声明函数并且在调用该函数。 也可以是在a.c中定义该函数int fun()后 ,在 b.c文件中使用extern int fun()来声明该函数是外部定义后并实行调用。 此外,extern修饰符可用于指示C或者C++函数的调用规范。比如在C++中调用C库函数,就需要在C++程序中用extern “C”声明要引用的函数。这是给链接器用的,告诉链接器在链接的时候用C函数规范来链接。主要原因是C++和C程序编译完成后在目标代码中命名规则不同
static
sizeof
volatile
内置函数分析
- memset
- void *memset(void *s, int c, size_t n);
- memset原型,-1和0赋值
深入C程序原理和C语言内存结构
C程序编译和环境
- c编译器
- gun gcc
- cl编译器(微软)–可以优化
更多推荐



所有评论(0)