码迷,mamicode.com
首页 > 编程语言 > 详细

C语言要点笔记

时间:2016-03-26 07:02:35      阅读:296      评论:0      收藏:0      [点我收藏+]

标签:


一.基础

1. 位运算补码(取反加一)是为了计算负数。

2. 编译原理:

源文件——.i文件——.s文件(汇编文件)——.o文件(二进制文件)——可执行文件(预处理——汇编——编译——执行)

3. GccC语言编译器)+vimlinux自带的字符编辑器)

eg.  vim 文件名,gcc 文件名,./a.out(输出)】

4. C语言基础语法:

1)if嵌套最多15层,If语句一般三到四层,else if最多写7个,switch(里面的表达式只能是charInt类型)。

2)循环语句的老祖宗Goto语句【语法:(声明变量,定义标签(:),if判断,goto 标签)】

 

二. 函数

1. 函数调用过程原理:

应用程序启动后(剩余)内存分配如下:

1)环境变量区:pathlanguage...

2)栈区:执行代码段;弹栈压栈(先进后出);无论32位还是64位系统,栈的大小是固定的(2M),则如果声明一个结构体大小超过2M,则会造成内存溢出;  

3)Static(静态区):

4)常量区:

5)代码段区:函数体...

2. ★(static)静态变量 全局变量

1Static修饰的变量为静态变量,采用静态存储形式,但反过来,静态存储形式的不一定就是局部静态变量,例如全局变量也是静态存储形式。

2)静态变量分为全局静态变量和局部静态变量

全局静态变量与全局变量有区别:

虽然同为静态存储方式,但是全局静态变量失去了全局的“普遍含义”它所指的“全局”仅限制在本文件里,而全局变量却是各个文件可见的。

?局部静态变量与局部变量有区别:

A.存储方式不同,前者为静态存储方式,后者为动态存储方式;

B.作用域是一致的,只局限于“模块”或者“代码段”;

?局部静态变量最大的特点就是作用类似于全局变量,而作用域类似于局部变量,(生命周期)与应用程序同生共死,在走出了某个函数或者代码段后生命周期延续,当再次回到这个函数或者代码段时,上次走出时的值仍然保存到现在,所以一般用它来做计数器。

3.自动变量(auto)和寄存器变量(register),外部变量(extern

★变量类型用来说明变量所占空间的大小,变量存储类型用来说明变量的作用范围。

C语言变量存储类型有:

自动类型:不加则默认(局部变量)

?寄存器类型:放在一个CPU寄存器中(数据在寄存器中操作比在内存中快),提高了程序代码执行速度。注意:取地址符&不能用于寄存器变量,它只能用于整型和字符型。

?静态类型和外部类型

4.const修饰符【修饰只读变量(声明了const的变量是不能修改的)】

C语言中的Const放在不同位置有不同的作用,在不同情况下有不同用法。在此之前,开发者一直使用宏定义:#define VAR 100 来定义一下有特殊用途的类常量,不过因其存在一些劣势,const应运而生,后来便使用const int VAR=100 来定义类常量了。两种写法存储区域不同,前者放在常量区,后者放在栈区。

★★总结:

const声明的变量必须要进行初始化赋值,如果错过这个机会,以后就不能再给const的变量赋值了。

?int const和const int,“颠倒写”都是可以的,但当const和指针掺和到一起时,“颠倒写”的规律可未必成立。

?const和指针搭配是噩梦

eg1.

int main(){

int a = 5;

int b = 6;

//int const *p=&a;//const在*前面,修饰指向的对象,p可变,p指向的变量的值不可变

int * const p=&a;//const在*后面,修饰指针,p不可变(只读),p指向的变量的值可变

//const int *const p=&a;//指针p和p指向的对象都不可变

//p = &b;

*p = 100;

return 0;

}

结论:通俗的说,A指针可以随便指向一个整型,但只要被A盯上了的整型变量在使用*A引用时就不能修改了。

eg2.

int num=12;

int tmp=100;

const int *A=#

A=&tmp;

tmp=3

printf(“result=%d\n”,*A);//输出结果为3

return 0;

}

eg3.char *find_char(char const * source){

char *p=(char*)source;//强制转换

return p;

}

结论:即使A指向了tmp,虽然不能修改*A,但是仍然可以用tmp来修改这个值,不管*A

5.volatile 修饰符

volatile影响编译器编译的结果变量是随时肯发生变化的,与volatile变量有关的运算,不要进行编译优化,以免出错。

valatile可以保证对特殊地址的稳定反应,不会出错。

6.可变参数列表

eg. void fun(int a,...) //不知道a后面有多少个参数,写三个点。

7.递归函数(解决复杂问题)

8.程序结构(以函数为最小单位)

9.系统函数

随机数

rand()%100:%100表范围0-100,另外,可以srand((unsigned)time(NULL))为参照物。

?Math.h

Sqrt():在gcc编译的时候要加-lm参数,把math库文件加进来。

 

三. 数组

字符数组:char[5]={‘h’, ‘e’ , ‘l’ , ‘l’ , ‘o’};结尾没有”\0”。

字符串数组:char[10]=”hello”;虽然使用双引号初始化,但是编译器会给数组的结尾加上”\0”(转义字符),表示结束。

 

四. 指针  

1. 指针是保存内存地址的变量;可以用来存放任何已被定义的变量地址,即使他们没有被赋值。

eg.

int num=5;//定义Int类型的变量

int *p;(int*)p //声明一个Int类型的指针用来保存Int类型变量的地址

p=# //num变量的地址放到p里面

printf(“%d\n,*p”);

指针变量 p

变量指针 *p //此处(声明后)加*(间接运算符)表示取指针里面的值,即用指针来访问值。

 

2. 空指针可以指向任何地址并对该地址的数值进行修改或者删除,所以需将其初始化为0

3. 不同类型变量所占内存大小不同,指针只能保存与它类型相同的变量的内存地址。

32位系统中所有类型的指针大小都是是4个字节(正好保存4G内存地址),64位系统中是8个字节】,(sizeof(指针))

 

4. 易混淆概念:指针地址,指针保存的地址和改地址的值。

5. 为什么使用指针(堆和栈的概念)

虽然通过变量名可以访问数据,但在操作大型数据和类时,由于指针可以通过内存地址直接访问数据,从而避免在程序中复制大量的代码,因此指针的效率最高,一般来说,指针会有三大用途:

1)处理堆中堆放的大型数据。

2)快速访问类的成员数据和函数。

3)以别名的方式向函数传递参数

 

★堆和栈

一般来说,程序就是与数据打交道,程序执行某一功能时将给功能所需数据加载到内存中,然后在执行完毕时释放掉该内存。

数据在内存中的存放形式:

1)栈区(stack:由操作系统自己分配并释放,一般存放函数和参数值,局部变量等。

其操作方式类似于数据结构中的栈。

2)堆区(heap): 由程序员分配并显示释放(一般用molloc,realloc,new等函数从堆中分配到一块内存),若程序员不释放程序结束时可能由操作系统回收。

注意:它与数据结构中的堆是两回事,分配方式倒是类似于链表。

3)寄存器区:用来保存栈顶指针和指令指针(用于控制程序中指令的执行顺序)

4)全局区(静态区 static):全局变量和静态变量的存储是放在一起的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和静态变量在相邻的另一块区域,程序结束后由系统释放。

5)文字常量区:存放常量字符串,程序结束后由系统释放。

6)程序代码区:存放函数体的二进制代码。

  

附注:★C语言的标准内存分配函数:malloc,calloc,realloc,free等。

malloccalloc的区别为1块与n块的区别:

malloc调用形式为(类型*)malloc(size):在内存的动态存储区中分配一块长度为“size”字节的连续区域,返回该区域的首地址。

calloc调用形式为(类型*)calloc(nsize):在内存的动态存储区中分配n块长度为“size”字节的连续区域,返回首地址。

realloc调用形式为(类型*)realloc(*ptrsize):将ptr内存大小增大到size

free的调用形式为free(void*ptr):释放ptr所指向的一块内存空间。

C++中为new/delete函数。

 

★★堆和栈的区别:

1)内存的申请方式不同:前者需程序员自己申请,因此也需指明变量大小;后者由系统自动分配。

2)系统响应的不同

对于栈,当栈的剩余空间大于所申请的空间,系统会为程序提供内存,否则提示栈溢出。

?对于堆,系统收到申请空间的请求后会变量一个记录内存空闲地址的链表,找到一个空间大于所申请空间的堆结点时,就将该节点从接了内存空闲地址的链表中删除,并将该结点的内存分配给程序。然后在这块区域的首地址处记录分配的大小,这样使用free函数,delete(C++ )函数释放内存时,函数才能正确识别并删除该内存区域的所有变量。另外,所申请的内存空间与堆结点上的内存空间不一定相等,系统会自动将堆节点上多出的那一部分内存空间回收到空闲链表中。

3)空间大小的不同

栈是一块连续的内存区域,它的大小是2M,也有的说是1M,总之是一个编译时就确定的常数,是由系统预先根据栈顶的地址和栈的最大容量定义好的。

?堆是不连续的内存区域,各块区域由链表将它们串联起来,它的上限是由系统中的有效虚拟内存来定的,因此获得的空间较大,获得方式也较灵活。

4)执行效率的不同

栈由系统自动分配,因此速度较快,但是程序员不能对其进行操作。

?堆是由程序员分配的内存,一般速度较慢,而且容易产生碎片,不过用起来很方便。

5)执行函数时的不同

栈:先进后出原则

?堆:效率比栈低的多,也容易产生碎片,好处是可以存储相当大的数据,并且一些细节也可以由程序员来安排

 

6. 内存泄露

简单的说就是申请了一块内存空间使用完毕后没有释放,表现为随着程序运行时间越长,占用内存越多,最终用尽全部内存,整个系统崩溃。由程序申请的一块内存,没有任何一个指针指向它,那么这块内存就泄露了。

 

五. 指针与数组

1. 用指针操作数组

2. 字符串:

strcat(连接)strlen(长度)strcpy(拷贝)strcmp(比较大小,相等返回0,不相等返回-1)

3. 命令行参数

main函数的参数列表中可以写入形参:

Int main(int argc, char *argv[])

{

int i;

printf(“argc:d%\n,argc”);//以空格为单位分割

printf(“s%\n”,argv[0]”);//输出命令本身

for(i=1;i<argc;i++){

printf(“s%\n”,argv[i]”);//把输入的命令行参数打印出来

}

return 0;

}

 

六. 预处理与VT码(在未用windows界面早期使用命令时使用VT码)

1.  #include 指令

#include “file.h”  #include <file.h>

“ ”:编译器从用户的工作路径开始搜索(当前目录找不到会到系统库查找)

?<>:编译器从标准库路径开始搜索

导入.h文件:是为了建立文件之间的联系,.h文件只是存放函数声明,比较小。

2. 条件编译
#if,  #else,  #endif,  #ifdef,  #ifndef,  #undef(取消宏定义)

eg1. 多行注释:

#if  0/1(不显示/显示)

...

#endif

eg2.宏替换

#define OFF 0/1

...

OFF

eg3.

#ifndef  _HHHH_H

#define  _HHHH_H

...

#endif

//以上代码防止头文件被重复引用

 

3. 宏替换(#define 标识符(即符号常量) 字符串)//宏替换有问题,因运而生内联函数

 

★typedef 关键字【重新定义(数据类型)eg.  UINT x ; //无符号整形 PINT p;//整形的指针)】

注:typeof还可以重新定义结构体(例如结构体嵌套中使用它重新命名结构体)或枚举等:

eg. Typeof struct date_st DATEtepeof struct date_st{...}DATE;

 

★#define 定义swap(A,B)

1) {int temp=a; a=b; b=temp;}
这种方法最常见,也是最不容易出错的。
2{a = a+b; b = a-b; a = a-b;}
这种方法少了一个中间变量,缺点是有可能溢出。
3{a ^= b; b^= a; a ^= b; }
这种位运算方法速度快也不需第三个变量。

 

★补充:#(双引号)##号(连接字符串)的用法:

eg. define str(s1,s2)  #s2#s2

//以字符串形式(加” ”)输出”s1””s2” ,如果s1,s2string类型则直接输出。

#define slink(s1,s2)  s1##s2 //s1,s2不能加” ”

#define link(s1,s2) s1 s2 //s1,s2” ”】

//无论s1,s2是字符还是int##只是将s1,s2进行连接。

 

七. C语言模块编程

 

拆分文件,以多个文件编写C语言程序(头部引入头文件)。

 eg:   *.c   *.h   main.c

 

 

八. 静态链接库与动态链接库

//存放函数库的常用目录 /lib  /usr/lib

//静态链接库:对函数库的链接是放在编译时期完成的,对象文件(.o)与牵涉到的函数库被拷贝到一个执行文件,通常文件名为libxx.a xx(库名)

//.so结尾的是动态链接库,.a结尾的是静态链接库

 

九. 指针的高级应用

1.指针变量赋值问题

int *a;

int *b;

 

★将指针b赋值给指针a,即a=b,之前要释放指针a所指向的内存空间,否则a的内存空间不再被访问,造成内存丢失。

★C语言规定指针只能指向类型相同的变量,指向不同类型变量的时候一定要进行强制类型转换。

 

2.指针用于数组

数组名与指针变量的区别:数组名是一个地址常量,数组名是不能改变的,而指针是一个变量,操作数组中的元素可以用指针实现。

?指针越界错误(段错误:不再访问范围内)

?数组与指针的区别:当用指针存储字符串的时候,字符串存储在静态存储区,此时字符串不能用指针修改。eg.  Char *s=”Hello World”;

3.二级指针(指向指针的指针)

二级指针的值传递与指针传递。

eg.

#include <stdio.h>

#include<stdlib.h>

void init(int **p){

*p=malloc(4);

}

int mian(){

int *p=NULL;

init(&p)

*p=100

printf(“d%\n,*p”);

free(p);

retrun 0;

}

4.多级指针

5.函数指针(基本上作函数的参数)【可用来实现C语言接口】

eg.

#include<stdio.h>

int add(int a,int b){

return a+b;

}

int main(){

int(*cal)(int,int);//函数指针

cal = add;//将函数指针指向add函数

printf(“d%\n”,cal(1,2));

return 0;

}

 

十. 组合数据类型

1. 结构体类型

1)结构体定义:不同数据类型的变量的集合。

*结构体就是C语句,结束必须加分号。结构体内,字符数组成员赋值用strcpy()。

eg.

struct(结构体) student(类型名){...}; //结构体声明

struct(结构体) student(类型名) stu(变量名);  //结构体使用

注:可在结构体声明时就定义一个结构体变量,可写在{...}前或{...}后。

 

2)结构体数组的使用跟普通数组一样。

eg.struct student stu[50]

 

3)★访问结构体成员:

结构体变量名.成员名  

?结构体指针变量->成员名

注:第一种是栈中变量定义的结构体,第二种放在堆里面,是使用剩余内存来开辟(molloc)的空间【结构体申请空间要在main()中】。如果结构体很大,里面涉及变量很多时用第二种方式。

 

4)结构体的嵌套

 

5)结构体中,char类型由占用一个字节变为4个字节【与前边对齐】。

在结构体结尾加上 __attribute__((packed))【不自动对齐】。

eg. #include<stdio.h>

sturct a_st{

char ch;

int i;

}attribute__((packed));//不自动对齐

 

int main(){

struct a_st A[10000];//如果自动对齐,声明此数组将会浪费很多空间

printf(“d%\n”,sizeof(stuct a_st));

return 0;

}

6)将结构体定义为指针类型

?用指针操作数组或字符串的时候,不能用sizeof()计算字符串或数组的长度,应为指针的大小是固定的。

 

补充:★class与struct的区别

*【structs过渡到class,C++是C的升级版。C语言的编译器gcc不支持class关键字,所以可用c++或g++编译。gcc也可以编译.cpp(c++)文件,但要求该.cpp文件格式是C的格式,调用库要用C语言的库去调用而不能用C++的库去调用】

*【eg.C++由很多特性:(源程序文件头部有:#include<iostream>,using namespace std;);(结构体使用时不用写stuct);(C++输出count<<..<<..<<endl;);等等】

*C语言编译器gcc最新版本支持在结构体里写函数,先前早期版本不行。

★用stuct(结构体),里面的成员属性和函数都是公共的,谁都能访问。而用class,要加修饰符,默认的是private:将它改成public:即可。

2. 枚举类型(经常作为函数 返回值 )

eg.  

enum bool{false,ture}; //java中的ture和false就是通过枚举定义的

enum bool isNum(int n){...}

 

typedef enum status STATUS; //用typedef重定义(重命名)

enum status{FAIL=-1,NOLINK,ONLINK};//可以自己约定开始的值

STATUS isAction(){...}

 

int main(){

// enum bool b;

prinft(“d%\n”,false);//输出结果为0

prinft(“d%\n”,FAIL);//输出结果为-1

prinft(“d%\n”,ONLINK);//输出结果为1,则中间是0

}

*以上enum(枚举)类似宏(#define False 0),但是宏返回的是 int类型,而enum返回的是boolean类型。

3.类型定义 typedef

4.联合(union)

定义:将几个基本数据类型组合在一起(所有数据类型的空间都是在一起的)。

网络编程中使用广泛。

 

?位域(由于内存空间的发展,已经被淘汰了):

?位域只存储在char类型和int类型

?位域就是按位分配内存的结构体

?位域不能跨段(一段位域不能跨两个字节)

?不支持数组位域

 

十一. vin的使用

a:在光标后追加   A:在一行后追加    i:在光标钱面插入  I:在一行前面插入

s: 删除当前光标后插入  S: 删除整行后插入   o: 另起一行插入  O:在当前行上面插入

yy: 复制当前行  pp:粘贴当前行在光标的下一行  dd: 剪切当前行  u:撤销

nyy: 多行复制(n表示数字,从光标的位置开始向下复制n行)

ngg: 定位(n表示数字)  gg:调到文件开头   GG:跳到文件的最后一行

查找:在正常模式下输入 / 所要查找的单词;next n:向下查找,N:向上查找

替换:(%表示整个文件,也可用具体行号范围如:0,40)%s/要查找的字符/要替换的字符

添加注释:(开头如上用具体行号)s/^/\/\/(^表示文件的开头,\位转义字符)

去掉注释:开头如上用具体行号)s/^\/\/(^表示文件的开头,\位转义字符)

自动补全:ctrl+p

查看多个源程序:vim 文件名1 文件名2 -o(小写o为上下屏,大写O为左右屏)

退出多个源程序: :qa   切屏:按两下ctrl+w  

找到头部引入文件::sp(split ) 文件名  或者:vs(vsplit) 文件名


十二. gccgdb的使用

版本:gcc -v  帮助:gcc  - -help  查手册:man 参数 ...

1)预处理:gcc  -E 文件名(.c) -o 文件名(.i)

2)汇编:gcc  -S 文件名(.i)  -o 文件名(.s)(不写则默认生成.s文件)

3)编译:gcc  -c  文件名(.s)  -o 文件名(.o)(不写则默认生成.o文件)

注:vim生成的是文本文件,将.o文件转换成二进制文件:%!xxd ,返回原样: %!xxd -r

4)连接:gcc test.o -o test 可以直接执行第四步跳过前面3

gcc其它参数:

-g: 添加程序的调试信息,然后可以使用GDB调试程序 (->gdb  a.out)

eg. Start: 单步执行(->s)   run: 执行完   bt: 查看栈,看有多少函数执行  n: 执行下一步

 

十三. ubuntu下的DDD调试工具的使用

用法和gdb一样,只是在gdb的基础上实现了可视化界面

 

十四. 文件操作

*C语言对文件的操作分为两种方式,即流式文件操作和I/O文件操作(前者调用标准库实现。后者调用内核函数库实现,linux操作系统中,通过其提供的底层接口实现)

1. 流式文件操作

1)  fopen()

fopen 的原型是:FILE *fopenconst char *filename, const char *mode,

fopen实现三个功能:为使用而打开一个流;把一个文件和此流相连接;给此流返回一个FILE指针参数;filename指向要打开的文件名,mode表示打开状态的字符串,其取值如下表:

“r” :  以只读方式打开文件   

“r+”:  以读/写方式打开文件,如无文件则出错   

“w”:  以只写方式打开文件(并清空文件内容)

“w+”: 以读/写方式打开文件,如无文件则生成新文件   

“a”:  以追加方式打开只写文件,(文件存在,写入的数据会被加到文件尾,即文件原先的内容会被保留,如无文件则生成新文件)

“a+”: 以追加方式打开读/写文件,同上

FILE这个结构包含了文件操作的基本属性,对文件的操作都要通过这个结构的指针来进行,此种文件操作常用的函数及功能如下:

fgetc():从流中读取一个字符

fgets():从流中读一行或指定个字符

fputc(): 写一个字符到流中

fputs(): 写字符串到流

fseek(): 在流中定位到指定的字符(移动文件中的指针)

rewind(): 复位文件定位器到文件开始处

ftell(): 读取字节数

fread(): 从流中读指定个数的字符

fwrite(): 向流中写指定个数的字符

fprintf(): 按格式输入到流

ferror(): 发生错误时返回其值

feof(): 到达文件尾时返回真值

...

★fgets()

函数功能:用于从文件流中读取一行或指定个数的字符。

原型:char * fgets(char * string, int size, FILE * stream);

参数说明:
(1)string为一个字符数组,用来保存读取到的字符。
(2)size为要读取的字符的个数。如果该行字符数大于size-1,则读到 size-1 个字符时结束,并在最后补充‘ \0‘;如果该行字符数小于等于 size-1,则读取所有字符,并在最后补充 ‘\0‘。即,每次最多读取 size-1 个字符。
(3)stream为文件流指针。
*【返回值】:读取成功,返回读取到的字符串,即string;失败或读到文件结尾返回NULL。因此我们不能直接通过fgets()的返回值来判断函数是否是出错而终止的,应该借助feof()函数或者ferror()函数来判断。
*注意:fgets()gets()不一样,不仅仅是因为gets()函数只有一个参数 FILE *stream,更重要的是,fgets()可以指定最大读取的字符串的个数,杜绝了gets()使用不当造成缓存溢出的问题。

freadfwrite

 函数功能:用来读写一个数据块。

一般调用形式:fread(buffer,size,count,fp);  fwrite(buffer,size,count,fp);

参数说明:

(1)buffer:是一个指针,对fread来说,它是读入数据的存放地址。对fwrite来说,是要输出数据的地址。

(2)size:要读写的字节数;

(3)count:要进行读写多少个()size字节的数据项;

(4)fp:文件流指针。

*注意:1 完成读写(fwrite())操作后必须关闭流(fclose())

fprintf():按格式输入到流,其原型是int fprintf(FILE *stream, const char *format[, argument, ...]);其用法和printf()相同,不过不是写到控制台,而是写到流罢了。

fseek()

函数功能:用来移动文件流的读写位置.

头文件:#include <stdio.h>
定义函数:int fseek(FILE * stream, long offset, int whence);

参数说明:

(1)参数stream为已打开的文件指针,

(2)参数offset为根据参数whence来移动读写位置的位移数。

参数whence 为下列其中一种:

(SEEK_SET 从距文件开头offset位移量为新的读写位置.

?SEEK_CUR 以目前的读写位置往后增加offset个位移量.

?(SEEK_END 将读写位置指向文件尾后再增加offset 个位移量.

whence 值为SEEK_CUR SEEK_END , 参数offset 允许负值的出现.

*下列是较特别的使用方式:

1) 欲将读写位置移动到文件开头时:fseek(FILE *stream, 0, SEEK_SET);

2) 欲将读写位置移动到文件尾时:fseek(FILE *stream, 0, 0SEEK_END);

*返回值:当调用成功时则返回0, 若有错误则返回-1, errno 会存放错误代码.

*附注:fseek()不像lseek()会返回读写位置, 必须用ftell()取得目前读写的位置。

eg.

创建文件1.text (内容:ABC

读取文件:

eg1.

#include<stdio.h>

int main(){

FILE *fp = fopen(“1.tex”,” r”);  //r表示以只读方式打开文件1.text

char c = fgetc(fp);

printf(“c%\n”,c); //读取A

printf(“c%\n”,fgetc(fp));//第一次读取后指针往后移一位,则第二次读取B,同理第三次读取C,而第四次将输出空格

fclose(fp);

return 0;

}

注:可用循环结构实现上述打印

 

eg2.

#include<stdio.h>

#include<stdlib.h>

#include<string.h>

int main(){

FILE *fp = fopen(“1.tex”,” r”);  

char *p = molloc(100);

memcpy(p,”\0”,100); //此处不能用strcpy(p,0)”\0”在此表示填充

fgets(p,4,fp);//4表示读取的字节数(计算机最小单位是字节而不是位)

printf(“s%\n”,p);

putchar(‘\n’); //去掉空格(按原格式读取)

free(p);

fclose(fp);

feturn 0;

}

 

补充:★strcpymemcpy

?strcpymemcpy都是标准C库函数,它们有下面的特点:
strcpy提供了字符串的复制。即strcpy只用于字符串复制,并且它不仅复制字符串内容,还会复制字符串的结束符。【已知strcpy函数的原型是:char* strcpy(char* dest, const char* src);
memcpy提供了内存地址的复制。即memcpy对于需要复制的内容没有限制,因此用途更广。

?strcpymemcpy主要有以下3方面的区别:
1)复制的内容不同。strcpy只能复制字符串,而memcpy可以复制任意内容,例如字符数组、整型、结构体、类等。
2)复制的方法不同。strcpy不需要指定长度,它遇到被复制字符的串结束符"\0"才结束,所以容易溢出。memcpy则是根据其第3个参数决定复制的长度。
3)用途不同。通常在复制字符串时用strcpy,而需要复制其他类型数据时则一般用memcpy

 

eg3.

#include<stdio.h>

int main(int argc , ch ar*argv[ ]){

FILE *fp = NULL;

int size = 0;

if(argc < 2){

return 1;

}

fp = fopen(argv[1] ,” r”);  //命令行读取第一个参数

fseek(fp, 0 , SEEK_END); //fp从开头移动到结尾

size = ftell(fp); //ftell()表示读取字节数

printf(“file size:d%\n”, size);

fclose(fp);

feturn 0;  

}

 

eg4.

#include<stdio.h>

int main(int argc , char*argv[ ]){

int size = 0;

FILE *fp = NULL;

char *str = NULL;

fp = fopen(argv[1] ,” r”);  //命令行读取第一个参数

If(fp == NULL){ //做判断,如果文件不存在就退出

perror(“error”);

retrun 1;

}

//perror( ) 用来将上一个函数发生错误的原因输出到标准设备(stderr)。(不可以丢了#include<stdio.h>这个头文件,perror是包含在这个文件里的)

fseek(fp, 0 , SEEK_END); //fp从开头移动到结尾(0SEEK_END

size = ftell(fp); //ftell()表示读取字节数

rewind(fp);//复位指针

str = malloc(size+1); //size+1: 包括”\n”

memcpy(str , “0” , size+1)//第二个参数是个地址

fread(str , size , 1 , fp); //数字1表示读1

str[size]=’\0’; //读完后将其终止

printf(“s%\n”, str);

fclose(fp);

Free(str);

feturn 0;  

}

 

 

 

十五. const_extern_static修饰符详解

1. extern称为外部变量。为了使变量除了在定义它的源文件中可以使用外,还要被其它文件使用,因此,必须将全程变量通知每一个程序模块文件,此时可用extern说明。

2. .c文件中static修饰的变量或方法只限制在本文件里访问,加extern关键字也访问不了。

3. 不要在头文件(.h)文件中用static关键字修饰变量或方法(基本上关键字都写在实现文件(.c)里面)。

十六. 动态数组

 

十七. Makefile的使用

创建test.c文件

eg . 编写Makefile文件:

all:test //目标条件

test:test.o //依赖(.o)文件, :表示依赖关系

  gcc test.o -o test

test.o:test.s

  gcc -c test.s -o test.o

test.s:test.i

  gcc -S test.i -o test.s -std=c99

test.i:test.c

  gcc -E test.c -o test.i

执行:make all

clean: //处理(删除)命令

rm -rf  *.i  *.o  *.s

执行:make clean (不写clean,默认是执行第一个all)

直接生成汇编:make test.s

(可用变量替换的方式)修改程序:

TARGET=test

OBJ=test.o

All:$(TARGET)

$(TARGET):test.o

gcc test.o -o test //可写成:gcc $^ -o $@,$^指代依赖文件(test.o ) ,S@指代目标文件($(TARGET))

...

 

也可跳过预处理和汇编:(注释语句用#

TARGET=test

All:$(TARGET)

$(TARGET):$(OBJ)

  gcc $^ -o $@,$^

test.i:test.c //可用%代替test,写成%.i : %.c%代表当前文件,此外*代表当前文件夹

  gcc -c test.c -o test.o //同上可写成:gcc -c $^ -o $@

clean:

rm -rf  *.i  *.o  *.s

.PHONY:$(TARGET)   all   //不管程序代码是不是最新的(有没有变动)都从新编译

 

C语言要点笔记

标签:

原文地址:http://blog.csdn.net/baidu_16083471/article/details/50984085

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!